/****************************************************************************/
/* Summary  : Command processor for BEDS Prototype on Persistor CF2	    */
/* Filename : bedsUI.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */
/* Revision : 1.0							    */
/* Created  : 09/17/2012						    */
/****************************************************************************/
/* Modification History:						    */
/* 17sep2012 rah - created from Persistor "toPico" stationery and	    */
/*		   previous BEDS prototypes				    */
/* $Log$
 */
/****************************************************************************/

#define	PD_CMD_BUILD_LINKS	// MUST APPEAR BEFORE #include <cfxpico.h>

#include <mbariTypes.h>		/* MBARI type definitions		*/
#include <mbariConst.h>		/* MBARI constants			*/
#include <cfxpico.h>		// Persistor PicoDOS Definitions
#include <beds.h>		/* BEDS general definitions		*/
#include <bedsUI.h>		/* BEDS Command line processor		*/
#include <bedsCmd.h>		/* BEDS user functions			*/
#include <serial.h>		/* BEDS serial I/O definitions		*/
#include <watch.h>		/* BEDS watch cycle			*/
#include <variable.h>		/* Variable function definitions	*/
#include <syslog.h>		/* BEDS System logger			*/
#include <clock.h>		/* BEDS Clock module			*/
#include <io.h>			/* BEDS I/O definitions			*/
#include <fileUtils.h>		/* BEDS file utilities definitions	*/
#include <modem.h>		/* BEDS acoustic modem software		*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <errno.h>


/********************************************************/
/*	Forward Declarations of additional functions	*/
/********************************************************/

MLocal char	*PrivCmd(CmdInfoPtr cip);
char		*DecodeCmd(CmdInfoPtr);

/****************************************/
/*	External Data			*/
/****************************************/

Extern Int16	wdtTicker;

/************************************************/
/*	Module Local Data			*/
/************************************************/

MLocal char	promptBuf[256];
MLocal char	*privMsg = "Setting command parser privilege to ";
MLocal char	*InvalidPrivMsg = "Invalid privilege level";

/********* Notes on privLevel (PRV LEV REQ in table) ************/
/*								*/
/* privLevel is a signed char, can be 0 to 127			*/
/*								*/
/* Our use:							*/
/* Connection on the acoustic modem port execute at priv level 0*/
/* Connection on the debug port, which can only happen in the lab,*/
/* execute at priv level 20					*/
/* Anyone can execute the PRIV command, which sets the priv level*/
/* This allows you to do dangerous stuff, but you have to want to*/
/*								*/
/* Command privs set according to the following:		*/
/*  0 = commands we want to execute during deployment		*/
/* 10 = File I/O commands, including delete, etc		*/
/* 20 = debug commands, including port and memory commands	*/
/* 99 = dangerous commands, including BOOT and RESET		*/
/****************************************************************/

MLocal CmdTable	ToPicoCmdTable[] = 
{
//------------------------------------------------------------------------------------
//	The members of the first entry in the command table are treated with special
//	meanings as described below:
//	PROMPT		HANDLER		2COLS DEF  CR  ABV	HEADER TEXT
	"BEDS)$P$G"		// Your custom command prompt using DOS prompt translation
	, PDCCmdStdPicoRun,	// The default interactive run handler
	1,			// 1 for two column help, 0 for single column
	0,			// Default privilege level at start
	0,			// Flag to accept carriage-return as repeat last command
	0,			// Flag to accept abbreviated commands
	//	Text to display before listing all of the individual commands help text
	"\n~~~~~~~~~~~~~~~  Benthic Event Detection System   ~~~~~~~~~~~~~~~~~~"
	"\n~~~ To start recording inertial data, QUIT the command interface ~~~"

	
//------------------------------------------------------------------------------------
//	 COMMAND	COMMAND		  MIN PRV  CR NUM	SHORT HELP TEXT
//	  NAME		FUNCTION	  REQ LEV REP BASE	EMPTY STRING "" TO 
//	(CONSTANT)	POINTER		  ARG REQ  OK 0=NO	OMIT FROM HELP LIST
//------------------------------------------------------------------------------------
//	vvvvvvvv KEEP THESE COMMANDS NEAR THE TOP TO MINIMIZE SEARCH TIME vvvvvvvv
	,"LOAD"		, PDCLoadMxCmd,	    0, 99, 0, 16,	""
	,"PBM"		, PDCResetToPBMCmd, 0, 99, 0,  0,	""	
	,"MON"		, PDCResetToPBMCmd, 0, 99, 0,  0,	""
//	THESE TWO KEEP MOTOCROSS IN CHECK DURING LOADS 98/06/05--jhg
	,"."		, PDCNothingCmd,    0, 0,  0,  0,	""
	,"\003"		, PDCNothingCmd,    0, 0,  0,  0,	""

//------------------------------------------------------------------------------------
//	BEDS Commands follow:
//		Command entries can actually go anywhere in the table, but placing them
//		at the top has them show firs in the help command.
//------------------------------------------------------------------------------------

	,"PRIV"		, PrivCmd,          0, 0,  0,  10,	"Set cmd parser priv level"
	,"EVENT"	, EventCmd,         0, 0,  0,  10,	"Get Events"
	,"EVENTS"	, EventCmd,         0, 0,  0,  10,	""
	,"WATCH"	, WatchCmd,         0, 0,  0,  10,	"Show watch cycles"
	,"SYSRECS"	, SysRecCmd,        0, 0,  0,  10,	"Get System Records"
	,"SYSREC"	, SysRecCmd,        0, 0,  0,  10,	""
	,"VAR"		, varCmd,           0, 0,  0,  10,	"Get/Set variable"
	,"VARS"		, varCmd,           0, 0,  0,  10,	""
	,"VARIABLE"	, varCmd,           0, 0,  0,  10,	""
	,"QUIT"		, QuitCmd,          0, 0,  0,  0,	"Exit command parser"
	,"SYSLOG"	, SyslogCmd,        0, 0,  0,  0,	"Write to system logger"
	,"DECODE"	, DecodeCmd,        1, 0,  0,  0,	"Decode data file"
	,"ANALOG"	, AnalogCmd,        0, 0,  0,  10,	"Read analog chan"
	,"DECIMATE"	, DecimateCmd,	    1, 0,  0,  10,	"Decimate and split files"
	,"SPLIT"	, DecimateCmd,	    1, 0,  0,  10,	"Split and decimate files"
	// File utilities
	,"MORE"		, MoreCmd,	    1, 0,  0,  0,       "Paginate file"
	,"TAIL"		, TailCmd,	    0, 0,  0,  10,      "TAIL <n> <filename>"
	,"DEPLOY"	, DeployCmd,        0, 5,  0,  0,       "Set/reset/show deployment mode"
	,"DELETELINE"	, deleteLine,	    0, 10, 0,  10,      "Delete one line from file"
	,"DELETECHARS"	, deleteChars,	    0, 10, 0,  10,      "Delete chars from file"
	,"DELETECHAR"	, deleteChars,	    0, 10, 0,  10,      ""
	,"INSERTLINE"	, insertLine,	    0, 10, 0,  10,      "Insert one line in file"
	,"INSERTCHARS"	, insertChars,	    0, 10, 0,  10,      "Insert chars in file"
	,"INSERTCHAR"	, insertChars,	    0, 10, 0,  10,      ""
        ,"RECOVER"      , BEDSRecoverCmd,   0, 100, 0,	0,	"equiv to RESET PICO"
	// Commands that process messages from modem
#ifndef TEAPOT
	,"CONNECT"	, ModemConnectCmd,  0, 0,  0,  0,	""
	,"LOWPOWER"	, ModemDisconnectCmd,0, 0, 0,  0,	""
	,"MODEM"	, ModemCmd, 	    0, 10, 0,  0,	"Send commands to modem"
	,"USER:"	, ModemOfflineCmd,  0,  0, 0,  0,	""
	,"COMMAND"	, ModemOfflineCmd,  0,  0, 0,  0,	""
#endif

	// Modem testing
	,"SREG"		, SRegCmd,          1, 0,  0, 10,	""

// BEDS Time commands
	,"SETPCLK", BEDSSetPicoClkFromAccurateClk, 0, 100, 1, 0, "Set PicoDOS RTC from accurate RTC"
	,"SETACLK", BEDSSetAccurateClkFromPicoClk, 0, 100, 1, 0, "Set accurate RTC from PicoDOS RTC"
#ifdef DEBUG_TIME
	,"TIMEDIFF",	BEDSTimeOffsetCmd,  0, 0,  1,   0, "Show clock offset in ms"
	,"TIMETEST",	TimeTestCmd,	    0, 0,  0,   0,	 ""
	,"DURTEST",	DurTestCmd,	    0, 0,  0,   0,	 ""
#endif

//------------------------------------------------------------------------------------
//	STANDARD PICODOS COMMANDS FOLLOW: (comment them out to remove or override)
//------------------------------------------------------------------------------------

	,"APP"		, PDCGoCmd,  	    0, 99, 0,   16,	"run flash app [args...]"
	,"ATTRIB"	, PDCAttribCmd,	    0, 10, 0,	0,	"[+ - RASH] [d:][p][name]"
	,"BACKROM"	, PDCBackROMCmd,    0, 10, 0,	16,	"[d:][path] [/SAVI]"
	,"BAUD"		, PDCBaudCmd,	    0, 20, 0,	10,	"[newrate] [/Q/P/E/O/N/2]"
	,"BOOT"		, PDCBootCmd,	    0, 99,  0,	0,	"[PICO][PBM][APP]"
	,"CAPTURE"	, PDCCaptureCmd,    0, 10, 0,	16,	"[d:][p]fn [/Dx/B/N/E]"
	,"CCC"		, PDCCardChangeCmd, 0, 10, 0,	10,	"Card Change [delay secs]"
	,"CHDIR"	, PDCCHDirCmd,	    0, 10, 0,	0,	"[drive:][path]"
	,"CHKDSK"	, PDCChkdskCmd,	    0, 10, 0,	16,	"[d:][p][fn] [/F][/I]"
	,"COPY"		, PDCCopyCmd,	    0, 10, 0,	0,	"source dest [/V]"
	,"DUMP"		, PDCDumpCmd,	    0, 10, 0,	16,	"file[start[,end]]"
	,"DATE"		, BEDSDateCmd,	    0,  0, 1,	0,	"print date/time"
	,"PDATE"	, PDCDateCmd,	    0, 99, 1,	0,	"[mdy[hms[a|p]]] /IEUMCP]"
	,"DEL"		, PDCDelCmd,	    0, 10, 0,	0,	"[drv:][pth][name] [/P]"
	,"DIR"		, PDCDirCmd,	    0,  0, 0,	16,	"[d:][p][fn] [/PWBLV4A:a]"
	,"ERASE"	, PDCDelCmd,	    0, 10, 0,	0,	"[drv:][pth][name] [/P]"
	,"FDISK"	, PDCFdiskCmd,	    0, 99, 0,	16,	"[/Pnn/M/Sdev/@/F/Rnn/Q]"
	,"FORMAT"	, PDCFormatCmd,	    0, 99, 0,	0,	"[drv:][/V[:label]][/Q/E]"
	,"GO"		, PDCGoCmd,	    0, 99, 0,	16,	"args... | addr /A | /Fn"
	,"LO"		, PDCLoadSRecCmd,   0, 99, 0,	16,	"[ofs][;Bx[+]] [;G]"
	,"MKDIR"	, PDCMKDirCmd,	    0, 10, 0,	0,	"[drive:][path]"
	,"MD"		, PDCMemDispCmd,    0, 10, 1,	16,	"display [range]"
	,"MM"		, PDCMemModCmd,	    0, 99, 1,	16,	"modify [address]"
	,"ML"		, PDCMemListCmd,    0, 20, 1,	16,	"disassemble [range]"
	,"MON"		, PDCResetToPBMCmd, 0, 99, 0,	0,	"Reset to PBM"
	,"PATH"		, PDCPathCmd,	    0, 10, 0,	0,	"[[d:]path[;...]] [/P]"
	,"PBM"		, PDCResetToPBMCmd, 0, 99, 0,	0,	"Reset to PBM"
	,"PROMPT"	, PDCPromptCmd,	    0, 20, 0,	0,	"[text] [/P]"
	,"PR"		, PDCPortReadCmd,   1, 20, 1,	10,	"pin read    <1..50>"
	,"PC"		, PDCPortClrCmd,    1, 20, 0,	10,	"pin clear   <1..50>"
	,"PS"		, PDCPortSetCmd,    1, 20, 0,	10,	"pin set     <1..50>"
	,"PT"		, PDCPortToggleCmd, 1, 20, 1,	10,	"pin toggle  <1..50>"
	,"PM"		, PDCPortMirrorCmd, 1, 20, 1,	10,	"pin mirror  <1..50>"
	,"TIME"		, BEDSTimeCmd,	    0,  0, 1,	0,	"[hh:mm:ss [a|p]] [/M/C]"
	,"PTIME"	, PDCTimeCmd,	    0, 99, 1,	0,	"[hh:mm:ss [a|p]] [/M/C]"
	,"TYPE"		, PDCTypeCmd,	    0,  0, 0,	0,	"[drv:][pth][name]"
	,"REN"		, PDCRenCmd,	    0, 10, 0,	0,	"[d:][p]oldname newname"
	,"RESET"	, BEDSResetCmd,	    0, 100, 0,	0,	"(hard reset)"	
	,"RMDIR"	, PDCRMDirCmd,	    0, 10, 0,	0,	"[drive:][path]"
	,"SAVE"		, PDCSaveCmd,	    0, 10, 0,	16,	"file[start][end]"
	,"SD"		, PDCSectorDumpCmd, 0, 10, 1,	16,	"sect.dump[d:][range][/C]"
	,"SET"		, PDCSetCmd,	    0, 10, 0,	0,	"[var=[str]] [/SLFE?]"
	,"XS"		, PDCXSCmd,	    0, 0,  0,	0,	"[/X][/C][/Q]file"
	,"XR"		, PDCXRCmd,	    0, 0,  0,	0,	"[/X][/C][/Q][file]"
	,"YS"		, PDCYSCmd,	    0, 0,  0,	0,	"[/G][/Q]file[,file...]"
	,"YR"		, PDCYRCmd,	    0, 0,  0,	0,	"[/G][/Q]"
#ifndef TEAPOT
	,"ZS"		, ZSCmd,	    0, 0,  0,	0,	"ZModem send"
	,"SZ"		, ZSCmd,	    0, 0,  0,	0,	""
#endif
	,"KS"		, KSCmd,	    0, 0,  0,	0,	"Kermit send"
	,"VER"		, PDCVerCmd,	    0, 0,  0,	0,	"Firmware versions"

//------------------------------------------------------------------------------------
// Future placeholders for PicoDOS

	,"BREAK"	, PDCBreakCmd,	    0,	20, 0,	0,	""
	,"CLS"		, PDCCLSCmd,	    0,	20, 0,	0,	""
	,"DEVICE"	, PDCDeviceCmd,	    0,	20, 0,	0,	""
	,"EXIT"		, PDCExitCmd,	    0,	20, 0,	0,	""
	,"INSTALL"	, PDCInstallCmd,    0,	20, 0,	0,	""
	,"LABEL"	, PDCLabelCmd,	    0,	20, 0,	0,	""
	,"MEM"		, PDCMemCmd,	    0,	20, 0,	0,	""
	,"MODE"		, PDCModeCmd,	    0,	20, 0,	0,	""
	,"PATH"		, PDCPathCmd,	    0,	20, 0,	0,	""
	,"VOL"		, PDCVolCmd,	    0,	20, 0,	0,	""

//------------------------------------------------------------------------------------
	,"GSAVE"	, PDCSaveCmd,	    0,	20, 0,	16,	""
	,"GS"		, PDCSaveCmd,	    0,	20, 0,	16,	""
	,"CD"		, PDCCHDirCmd,	    0,	10, 0,	0,	""
	,"CD."		, PDCCHDirCmd,	    0,	10, 0,	0,	""
	,"CD.."		, PDCCHDirCmd,	    0,	10, 0,	0,	""
	,"MKD"		, PDCMKDirCmd,	    0,	10, 0,	0,	""
	,"RD"		, PDCRMDirCmd,	    0,	10, 0,	0,	""
	,"HELP"		, PDCHelpCmd,	    0,	0,  0,	0,	""
	,"HE"		, PDCHelpCmd,	    0,	0,  0,	0,	""
	,"H"		, PDCHelpCmd,	    0,	0,  0,	0,	""
	,"?"		, PDCHelpCmd,	    0,	0,  0,	0,	""
	,"RES"		, PDCResetCmd,	    0,	20, 0,	0,	""	
	,"G"		, PDCGoCmd,	    0,	20, 0,	16,	""
	,"GO"		, PDCGoCmd,	    0,	20, 0,	16,	""
	,"CRC"		, PDCCRCCmd,	    0,	10, 0,	16,	""
	,"CMD10"	, PDCCmdStdCmdTest, 0,	20, 0,	10,	""
	,"CMD16"	, PDCCmdStdCmdTest, 0,	20, 0,	16,	""


	//TERMINATING ENTRY (!!!!This must be present at end of table !!!!)
	,""		, 0,		    0,	0, 0,	0,	0
};


/************************************************************************/
/* Function    : runUI							*/
/* Purpose     : Run the (modified PicoDOS) user interface		*/
/* Inputs      : Initial privilege level for UI commands		*/
/* Outputs     : None							*/
/* Comments    : Runs on serial port set up via setConsole() in serial.c*/
/*		 Runs until user quit or timeout			*/
/************************************************************************/
Void	runUI(Nat16 privLevel)
{
    short	getsRtn;
    CmdInfo	ci;

    sensorsOn(MODEM_SAMPLING);
    sysLogPrintf("Running User I/F on %s port", 
		 consoleIsSwitched() ? "TPU" : "console");
    ToPicoCmdTable[0].privLevel = privLevel;

    while(TRUE)
    {
	CmdStdSetup(&ci, ToPicoCmdTable, gets_lpt);

	cprintf("\nBEDS)%s>", DSDGetCurrentWorkingDir(DSDGetCurrentDrive()));

	if (ci.altgets)
	    getsRtn = (*ci.altgets)(ci.line, sizeof(ci.line));
	else
	    getsRtn = CmdStdLPGets(ci.line, sizeof(ci.line));

	if (getsRtn == TMOUT)
	{
	    cprintf("\nTIMEOUT\n");
	    sysLog("Command parser TIMEOUT");
	    sensorsOff(MODEM_SAMPLING);
	    return;
	}

	CmdParse(&ci);			// Parse command line

	/***** Check for '=' assignment on command line *****/
	if ((ci.argc > 2) && (strcmp(ci.argv[1].str, "=") == 0))
	    ci.errmes = setVar(ci.argv[0].str, ci.argv[2].str, TRUE);
	else
	{
#ifdef WATCHDOG
	    wdtTicker = 1;		// Enable WDT tickle in ticker	
	    CmdDispatch(&ci);		// No assignment, dispatch it
	    wdtTicker = -1;		// Disable WDT tickle in ticker
#else
	    CmdDispatch(&ci);		// No assignment, dispatch it
#endif
	}

	if (ci.errmes == 0)		// no error
	    continue;			// ... so keep on truckin

	if (ci.errmes == CmdErrUnknownCommand && ci.justCR)	// we don't complain about <enter>
	    continue;			// ... so keep on truckin

	if (ci.errmes == CMD_BREAK)	// wants out...
	{
	    sensorsOff(MODEM_SAMPLING);
	    return;
	}

	cprintf("\n%s\n", ci.errmes);
    }

} /* runUI() */


/************************************************************************/
/* Function    : PrivCmd						*/
/* Purpose     : Set privilege level in command table			*/
/* Inputs      : CmdInfoPtr						*/
/* Outputs     : NULL if OK, else error message				*/
/************************************************************************/
MLocal char *PrivCmd(CmdInfoPtr cip)
{
    DosSwitch	QMsw = { "/", '?', 0, 0 };		// help

    CmdExtractCIDosSwitches(cip, "?", &QMsw);

// HELP SWITCH SELECTED (common format to many commands)
    if (QMsw.pos)
    {
	printf("\nPRIV <level>   where <level> is a number between 0 and 127\n\n"
		"Sets privilege level for executing privileged commands\n\n"
		"Commands that are restricted when deployed are at privilege level 10 and up,\n"
		"with the most dangerous commands as high as level 100\n\n"
		"Executing this command exposes some very dangerous user commands\n\n"
		"USE WITH EXTREME CAUTION!\n\n");
	return(0);
    }
    
    if (cip->argc < 2)
	printf("\nCurrent privilege level is %d\n", ToPicoCmdTable[0].privLevel);

    else if (cip->argv[1].isval)
    {
	if ((cip->argv[1].value < 0) || (cip->argv[1].value > 127))
	    return(InvalidPrivMsg);

	printf("\n%s%ld\n", privMsg, cip->argv[1].value);
	ToPicoCmdTable[0].privLevel = cip->argv[1].value & 0x7f;
	sysLogPrintf("%s%ld", privMsg, cip->argv[1].value);
    }
    else
	return(InvalidPrivMsg);

    return(NULL);

} /* PrivCmd() */
