#ifdef __BORLANDC__
#include <conio.h>
#define SEND(ch) putch(ch)
#else
#include <msp430x14x.h>
#define SEND(ch) usart0TxOne((char)ch)
#endif

#include <stdio.h>
#include <string.h>

#include "basic.h"
#include "config.h"
#include "errnum.h"
#include "eventq.h"
#include "cmdparse.h"
#include "outfmt.h"
#include "util.h"
#include "usart0.h"

static char buffer[CONFIG_INPUT_BUFFER_SIZE];

static boolean echoOn = true; // TODO: change to false for deployment
static boolean errorOn = false; // return errors

static int offset;
static int expectLfGotCr = 0;

typedef enum {expectCR, expectLF} receiveState;

static receiveState rxState = expectCR;

int setEchoCmdHandler(char * cmdString);

int showCmdprocHandler(char *cmdString)
{
  // ("expectLfGotCr[%d]\n",expectLfGotCr);
  return 0;
}

static void changeRxState(receiveState newRxState)
{
  if (rxState != newRxState) {
    // printf("rxState changed to %s\n",((newRxState == expectCR) ? "expectCR": "expectLF"));
    rxState = newRxState;
    }
}

boolean cmdprocAccumulateCmd(char inCh)
{
  boolean gotNewline = false;
  
  if (inCh > 0) {
    // if echoOn is set, then echo the character
    
    // 
    // Normally, we expect <String><CR><LF>, but we also need to handle
    // <String><newline> or <String><CR>
    //
    // 0x0a = LF, 0x0d = CR
    switch (rxState) {
    case expectCR:    
      if ((inCh == 0x0a) || (inCh == 0x0d)) {
	// got CR or LF while expecting CR -- terminate the string
        buffer[offset] = '\0'; // terminate the string
	gotNewline = true;
	offset = 0; // ready offset for next command string
	if (echoOn == true) {
	  outfmtSendCrLf();
	  }
	changeRxState(((inCh == 0x0d) ? expectLF : expectCR));
        }
      else {
	buffer[offset] = inCh;
	circInc(offset, 0, CONFIG_INPUT_BUFFER_SIZE-1);
	if (echoOn == true) {
	  SEND(inCh);
	}
      }
      break;
    case expectLF:
      if (inCh == 0x0a) { 
        // expecting LF, got LF, toss LF character
        }
      else if (inCh == 0x0d) {
	// expecting LF, got CR, start new command
	expectLfGotCr++;
        }
      else {
	// buffer any other character
	buffer[offset] = inCh;
	circInc(offset, 0, CONFIG_INPUT_BUFFER_SIZE-1);
	if (echoOn == true) {
	  SEND(inCh);
	  }
        }
        
      changeRxState(expectCR);        
      break;
    } // switch
  }
  
  return gotNewline;
}

#ifndef __BORLANDC__

void cmdprocAccumulateCh(void) 
{
  int retVal; 
  char ch;
  
  retVal = usart0RxOne(&ch);
  
  if ((retVal != -1) && (cmdprocAccumulateCmd(ch) == true)) {
    if ((retVal = cmdparseDoCommand(buffer)) != 0) {
      if (errorOn == true) {
        outfmtSendError(retVal);
        }
    }
    outfmtSendPrompt();
  }
}

#else

void cmdprocAccumulateChExt(char inCh)
{
  int retVal;

  if ((inCh) && (cmdprocAccumulateCmd(inCh) == true)) {
    if ((retVal = cmdparseDoCommand(buffer)) != 0) {
      if (errorOn == true) {
        outfmtSendError(retVal);
        }
    }
    outfmtSendPrompt();
  }
}

#endif

static int setEchoCmdHandler(char * cmdString)
{
  eatWhiteSpace(&cmdString);
  
  if (strcmp(cmdString, "on") == 0) {
    echoOn = true;
  }
  else if (strcmp(cmdString, "off") == 0) {
    echoOn = false;
  }
  else {
    return ERRNUM_INVALID_ARG;
  }

  return 0;
}

static int setErrorCmdHandler(char * cmdString)
{
  eatWhiteSpace(&cmdString);
  
  if (strcmp(cmdString, "on") == 0) {
    errorOn = true;
  }
  else if (strcmp(cmdString, "off") == 0) {
    errorOn = false;
  }
  else {
    return ERRNUM_INVALID_ARG;
  }
  return 0;
}

int cmdprocInit(void)
{
  int result = 0;
  
  // zero out the command buffer
  clearString(buffer, CONFIG_INPUT_BUFFER_SIZE);

#ifndef __BORLANDC__
  // register to receive character rx events 
  eventqRegisterHandler(EVENTQ_EVENT_UART0RX, cmdprocAccumulateCh);
#endif

  // add the 'set echo' command handler
  result = cmdparseAddCommand("set", "echo",setEchoCmdHandler);

  // add the 'set error' command handler
  result = cmdparseAddCommand("set", "error",setErrorCmdHandler);

  cmdparseAddCommand("show","cmdproc",showCmdprocHandler);
    
  return result;
}
