/*******************************************************************************
MODULE      :   Commands.c
  Processes commands from logging interface

  MODULE IS ADAPTED FROM ANOTHER VERY OLD PROJECT
  So some code may look really ugly. Even to my eyes ;-)

19xxxxxx AB     Initial
20191219 AB     Slightly updated for JADE EFM32JG1B test
*******************************************************************************/

// --- Includes ---------------------------------------------------------------
#define   LOGGING_TOKEN    COMMAND_LOGGING
#include "TraceLeUart.h"

#include <efm32.h>
#include <em_gpio.h>

#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>

#include "types.h"

#include "Command.h"
#include "TraceLeUart.h"
//#include "Nvm.h"
//#include "Iic.h"
#include "Adc.h"
//#include "ComAnet.h"
//#include "MasterAnet.h"
//#include "SlaveAnet.h"
#include "Leds.h"
#include "Hardware.h"

// --- Pin Definitions --------------------------------------------------------

// --- Defines ----------------------------------------------------------------
#define MAX_CHAR_COMPARE    3       /* max. number of characters valid for command compare */
#define MAX_ARGS            5       /* max. number of arguments to be processed */
#define MAX_COMMAND_LENGTH  15      /* max. length of command */
#define MAX_LINE_LENGTH     50      /* max. length of on command line */
#define MAX_ARG_LENGTH      6       /* max. length of one argument */
#define BUFFER_SIZE         100     // max. size of line including command and args...

/* Commands are defined as array of strings and as consecutive numbers for easier handling in command.c */
#define DUMMY           0
#define VERSION         1
#define UPTIME          2
#define ADCS            3
#define HELP            4
#define LIST_EEPROM     5
#define CLEAR_EEPROM    6
#define NVMSTATUS       7
#define LOGGING         8
#define STATUS          9
#define ERRORS          10
#define IRQS            11
#define LED             12
#define COVOLT          13
#define RELAIS          14
#define BOOT            15
#define HWLOGGING       16
#define NEW_LINE        17
// have to be 2 items more than #defines above
#ifndef NO_TRACE
              // #commands length
const char saCommands[20] [33] = {
"DUMMy",
"VERSion",
"UPTIme",
"ADCS [1|2|3|4]",
"HELP",
"LIST_eeprom",
"CLEAR_eeprom (attention !!!)",
"NVMStatus",
"LOGGing",
"STATus",
"ERROrs",
"IRQS",
"LED num [0|1] (num=0|2)",
"COVoltage correct. LINE in mV",
"RELais 1|2|? toggle relais",
"BOOT sw reset to booter",
"HWLogging - toggle on/off",
"\n",
  "\0"};

// --- Typedefs ---------------------------------------------------------------

// --- PUBLIC Variables -------------------------------------------------------

static BYTE bNewCommands;       // number of commands waiting to be processed
// incremented in uart receiver IRQ routine

// --- Variables --------------------------------------------------------------

static BYTE bArgc                  ;    // number of arguments
// static BYTE bLogLevel                ;    // level of logging messages

static char sCommand[MAX_COMMAND_LENGTH + 1]   ;    // input command buffer
static char sArg[MAX_ARGS][MAX_ARG_LENGTH + 1] ;    // buffer for command arguments
static char sTmpBuffer[BUFFER_SIZE]        ;       // temporary buffer

// --- Macros -----------------------------------------------------------------

#endif  // NO_TRACE

// --- Functionprototypes -----------------------------------------------------
extern int StackCheck (void);

void CommandProcessing(void);
BYTE CommandCompare (void);

// --- Code -------------------------------------------------------------------

void CommandInit(void)
  {
#ifndef NO_TRACE
  bNewCommands = 0;
#endif  // NO_TRACE
  }

/******************************************************************************
void CommandProcessing (void)

  Called whenever a new command arrived in input buffer. Or from main loop
  every 200ms. Decodes commands an processes arguments. Notification about new
  command via global variable. Input buffer read with direct fetch from uart.
******************************************************************************/
void CommandProcessing(void)
{
#ifndef NO_TRACE
  if ( !TraceCommandAvail() ) return;   // check if commands are ready

  BYTE bChar, bTmp;
  BYTE bCount = 0;
//  BYTE j, i;
//  char cBuf[81];
//  char cBuf1[10];
//  int iT0, iT1, iT2, iT3, iT4, iT5, iT6;//, iT7;
  int iTemp;

  do
    {
    if ( ! (bChar = TraceGetKey() ) )    // read byte from UART input buffer
      {
      if ( !bCount )
        {     /* if no characters are processed till now then error, else normal abortion of while loop */
        TRACE_L2("CMD: Error - TraceGetKey returns 0 (false triggered new command?)");
        TRACE_L2("CMD:  bNewCommands: %d", bNewCommands);
        }
      break;    /* error on getkey breaks */
      }
    if ( bChar == '\b' && bCount ) bCount--;     /* Backspace processing */
    else
      {
      if ( bChar != '\b' && bCount < (BUFFER_SIZE-1) )
        {
        if ( bChar != '\n' && bChar != '\r' )
          {      /* don't process CR or LF */
          //sTmpBuffer[bCount] = toupper(bChar);    // 20090116 AB make case sensitive (command is still insensitive, see command compare)
          sTmpBuffer[bCount] = bChar;
          bCount++;
          }
        }
      }
    if ( bChar != '\n' && bChar != '\r' ) TRACE_L2("Key '%c'", bChar);
    }
  while ( (( (bChar != '\r') && (bChar != '\n') ) && (bCount < (BUFFER_SIZE-1)) ) || (bChar == '\b') );  /* (the BUFFER_SIZE-1 character overflows bCount and breaks while loop)
                                           if BUFFER_SIZE overflows then break while loop */
  if ( bCount == (BUFFER_SIZE-1) )            /* sTmpBuffer overflow - line to long, read till no character or line end (\n or \r) */
    {
    while ( (bChar = TraceGetKey()) )
      {         /* characters available to read */
      if ( bChar == '\n' || bChar == '\r' ) break;    /* Read until end of command to flush too long commands */
      }
    }

  if ( bCount > 0 ) sTmpBuffer[bCount] = '\0';
  else
    {
    sTmpBuffer[0]= '\0';
    }

  TraceCommandAvailDec();               /* one command read from input buffer */

  if ( !bCount )
    {
    TRACE_L2("CMD: Error - input stream to long or no input (only <CR> or <LF>");
    return;                   /* input stream to long, bCount overflow */
    }

  TRACE_L2("%s\n", sTmpBuffer);       /* debugging  print complete Command Line */

  if ( bCount < MAX_LINE_LENGTH ) bChar = CommandCompare ();    /* only commands shorter than MAX_LINE_LENGTH characters are allowed */
  else
    {
    TRACE_L2("\nCMD: Error - command line to long\n");
    return;
    }

  bCount = 0;       /* increment bCount whenever one argument was processed */

  TRACE_L2("Command compare gives command %d", (int) bChar);

  switch ( bChar )
    {
    case VERSION:
      TracePrintFirmwareVersion();
      //TRACE(" ZTM-BD   Build: %s   %s", TraGetBuildTime(),  TraGetBuildDate());
      // __DEBUG will be set by MPLAB for debug builds
  #if defined  (__DEBUG__) || defined (DEBUG) || defined (__DEBUG)
      TRACE (" DEBUG");
  #endif
      TracePrintHwInfo();
      //TracePrintKeypadInfo();
      break;

    case UPTIME:
      TracePrintUptime();
      TracePrintStartTime();
      break;

    case ADCS:
      bTmp = 1;
      switch (sArg[0][0])
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
          bTmp = atoi(&sArg[0][0]);
          // fall through
        default:
          AdcTrace(bTmp);
          break;
        }
      bCount--;
      break;

    case HELP:
      TRACE(" Valid commands -");
      bTmp = 1;
      while ( saCommands[bTmp][0] != '\n' )
        {
        TRACE("   %s", saCommands[bTmp]);
        bTmp++;
        }
      TRACE(" Only the first %d characters are decisive. Not case sensitive.", (int) MAX_CHAR_COMPARE);
      break;

/* 20180917 remmed out cause of FLASH space constraints    case LIST_EEPROM:
      j = 0;
      do
        {
        cBuf[0] = '\0';
        for (i = 0; i < 16; i++, j++)
          {
          sprintf(cBuf1, " 0x%02X", NvmGetChar(j));
          strcat(cBuf, cBuf1);
          }
        TRACE("%s", cBuf);
        }
      while (j < 128); //EEPROM_SIZE);
      break;

    case CLEAR_EEPROM:
      for (i = 0; i < 8; i++) NvmStoreChar(i, 0xFF);
      TRACE("CMD: cleared first %d bytes in EEPROM", i);
      break;
*/
    case NVMSTATUS:
      TRACE(" ");
//      NvmTraceStatus();
      break;

    case LOGGING:
      /*if (!bArgc)
        {
        TRACE("CMD: Logging is %s", bLogLevel ? "ON" : "OFF");
        }
      else  // At least one Argument
        {
        bCount++;
        if (!strncmp ( (char *) &sArg[0], "OFF", MAX_ARG_LENGTH) )  bLogLevel = FALSE;
        if (!strncmp ( (char *) &sArg[0], "ON" , MAX_ARG_LENGTH) ) bLogLevel = TRUE;
        TRACE("CMD: Switch logging to %s",  (char *) &sArg[0]);
        };
      */break;

    case STATUS:
      if ( (iTemp = StackCheck()) >= 0 )    // check if stack is initialized to enable stack check
        {
        TRACE("CMD: stack - %d bytes free", iTemp );
        }
      else
        {
        TRACE("CMD: stack - can not be checked");
        }
//      IicPrintStatus();
//      ComTraceErrors();
      break;

    case ERRORS:
      //TRACE("CMD: Errors <- not implemented yet!");
      break;

    case IRQS:
      TracePrintIRQs();
//      ComTraceIRQs();
//      MasTraceIRQs();
//      SlvTraceIRQs();
//      IicTraceIRQs();
      break;

    case LED:
      if (bArgc)
        {
        if (bArgc == 1) LedToggle(sArg[0][0] - '0');
          else {
          switch (sArg[1][0])
            {
            case '0':
              LedOff(sArg[0][0] - '0');
              bCount--;
              break;
            case '1':
              LedOn(sArg[0][0] - '0');
              bCount--;
              break;
            }
          }
        bArgc--;
        }
      else
        {
        TRACE("COM: use f.i. LED 0 1");
        }
      break;

    case COVOLT:
      break;

    case RELAIS:
      break;


    case BOOT:
      // restart (jump into booter)
      __disable_irq();    // __asm("CPSID i");
//      GPIO_PinModeSet(gpioPortB, 13, gpioModeInputPull, 0);   // STOP_FAST_START
//      GPIO_PinModeSet(gpioPortC, 14, gpioModePushPull, 1);    // START_FAST_START
      _delay_us( 50000 );
      NVIC_SystemReset();
      break;

    case NEW_LINE:    /* ignore new line */
      break;

    case HWLOGGING:    /* toggle Hardware logging  */
//      HwLoggingToggle();
      break;

    default:
      if ( strlen(sTmpBuffer) < 47 )
        {
        TRACE("CMD: Unknown command '%s'. Try 'HELP'", sTmpBuffer);
        }
      else
        {
        TRACE("CMD: Unknown command '%47s'. Try 'HELP'", sTmpBuffer);
        }
      break;
    }

  if ( bArgc > bCount )         /* all arguments processed ? */
    {
    TRACE("CMD: Arguments not processed -");
    while ( bCount < bArgc )
      {
      TRACE("  '%s'", sArg[bCount]);
      bCount++;
      }
    }

  return;
#endif  // NO_TRACE
  }



BYTE CommandCompare ()
{
#ifndef NO_TRACE
  BYTE bChar = 0, bCount = 0, bTmp = 0, k, l;
  if ( bNewCommands > 20 )    /* if too much commands flush transmit buffer to get more processing time
                       and suppress further echos */
    {
    //      SNT_FLU ();
    bNewCommands = 0;
    TRACE("CMD: Too much commands: %d", bNewCommands);
    TRACE("CMD: Input buffer flushed\n");
    }

  if ( (l = strlen (sTmpBuffer)) )
  {
    bArgc = 0;
    while ( sTmpBuffer[bCount] == ' ' && bCount < l && bTmp < MAX_COMMAND_LENGTH ) bCount++;   /* remove leading blanks */
    while ( sTmpBuffer[bCount] != ' ' && bCount < l && bTmp < MAX_COMMAND_LENGTH )
      {
      if ( sTmpBuffer[bCount] != '\n' && sTmpBuffer[bCount] != '\r' && sTmpBuffer[bCount] != ' ' )
        {
        sCommand[bTmp] = toupper(sTmpBuffer[bCount]);   // 20090116 command case insensitive, args case sensitive
        bTmp++;
        }
      bCount++;
      }
    sCommand[bTmp] = '\0';
    TRACE_L2(" CMD: command_compare - Command string %s\n", sCommand);

    for ( k = 0; k < MAX_ARGS; k++ )
      {
      while ( sTmpBuffer[bCount] == ' ' && bCount < l ) bCount++;   /* remove blanks */
      bTmp = 0;
      while ( sTmpBuffer[bCount] != ' ' && bCount < l && bTmp < MAX_ARG_LENGTH )
        {
        if ( sTmpBuffer[bCount] != '\n' && sTmpBuffer[bCount] != '\r' && sTmpBuffer[bCount] != ' ' )
          {
          sArg[k][bTmp] = toupper(sTmpBuffer[bCount]); // 20101006 args case insensitive too
          bTmp++;
          }
        bCount++;
        }
      sArg[k][bTmp] = '\0';
      TRACE_L2(" CMD: command_compare ARG ");
      TRACE_L2("%u bArgc: %u %u - %s\n", k, bArgc, bArgc, sArg[k]);
      if ( bTmp ) bArgc++;
      if ( bCount >= l ) break;    /* end if no more input characters to process */
      }

    while ( saCommands[bChar][0] != '\0' && bChar < 50 )     // no more than 50 commands allowed
      {
      bChar++;
      TRACE_L2(" CMD: comparing to %s", saCommands[bChar]);
      if ( !strncmp (saCommands[bChar], (char *)sCommand, MAX_CHAR_COMPARE) ) break;
      }

  }

  TRACE_L2(" CMD: command %d found  ", (int) bChar);
  return bChar;
#else
  return 1;
#endif  // NO_TRACE
  }


