#ifndef __BORLANDC__
#include <msp430x14x.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "basic.h"
#include "config.h"
#include "util.h"
#include "errnum.h"
#include "cmdparse.h"

typedef struct cmdNode {
  char * cmdString;
  struct cmdNode * next;
  struct cmdNode * subCommand;
  commandHandler handler;
  } cmdNode, *pCmdNode;

#ifdef CONFIG_NO_MALLOC
static cmdNode commands[CONFIG_CMDPARSE_NUMCMDS];
static int freeIdx = 0;
#endif

static pCmdNode cmdList = NULL;

int invalidCommandHandler(char *);

int cmdparseInit(void) {
  return 0;
}

static pCmdNode allocNode(void)
{
  pCmdNode pNewNode = NULL;
  
#ifdef CONFIG_NO_MALLOC
  if (freeIdx < CONFIG_CMDPARSE_NUMCMDS) {
    pNewNode = &commands[freeIdx++];
  }
#else
  pNewNode = malloc(sizeof(cmdNode));
#endif

  return pNewNode;
}

#ifdef __BORLANDC__
static void showNode(cmdNode *pNode)
{
  while (pNode != NULL) {
    // printf("CmdNode [%p] for [%s]\n",pNode,pNode->cmdString);
    if (pNode->subCommand != NULL) {
      // printf("-- subcommands --\n");
      showNode(pNode->subCommand);
    }
    pNode = pNode->next;
  }
}

void cmdparseShow(void)
{
  showNode(cmdList);
}
#endif

//
// TODO: multithread issue -- should lock the cmdList when adding cmdNodes
//

int cmdparseAddCommand(char * pCmdStr, char * pSubCmdStr, commandHandler handler)
{
  cmdNode * pCmdNode;
  cmdNode * pSubCmdNode;
  
  if (pCmdStr == NULL) {
    return -1; // invalid arg error
  }
  
  // search the current list
  pCmdNode = cmdList;
  while (pCmdNode != NULL) {
    if (strcmp(pCmdStr, pCmdNode->cmdString) == 0) {
      break;
      }
    pCmdNode = pCmdNode->next;
    }

  // if we didn't find the command, then add it and save for subcommand if applicable  
  if (pCmdNode == NULL) {
    pCmdNode = allocNode();
  	
    if (pCmdNode != NULL) {
      pCmdNode->cmdString = pCmdStr;
      pCmdNode->handler = (commandHandler)((pSubCmdStr == NULL) ? handler : NULL);
      pCmdNode->subCommand = NULL;
      pCmdNode->next = cmdList;
      cmdList = pCmdNode;
      }
    }
  
  // if there is no subcommand given then just return

  if (pSubCmdStr == NULL) {
    return 0;
  }

  // search the current list
 
  pSubCmdNode = pCmdNode->subCommand;
  while (pSubCmdNode != NULL) {
    if (strcmp(pSubCmdStr, pSubCmdNode->cmdString) == 0) {
      break;
      }
    pSubCmdNode = pSubCmdNode->next;
    }

  if (pSubCmdNode == NULL) {
    pSubCmdNode = allocNode();
  	
    if (pSubCmdNode != NULL) {
      pSubCmdNode->cmdString = pSubCmdStr;
      pSubCmdNode->handler = handler;
      pSubCmdNode->subCommand = NULL;
      pSubCmdNode->next = pCmdNode->subCommand;
      pCmdNode->subCommand = pSubCmdNode;
      }
    }
  else {
    // subcommand found, return error
    return -1;
    }
  return 0;
}
  
static char keyword[16];

// perhaps easier to do recursively, but our target has limited stack 
// resources

static cmdNode * findCommand(cmdNode * pStartNode, char ** ppCmdStr)
{
  cmdNode * pCmdNode = pStartNode;
  cmdNode * pPrevNode = NULL;
  char * pPrevStr;

  while (**ppCmdStr != '\0') {

    clearString(keyword,16);

    // eat whitespace and grab an argument
    eatWhiteSpace(ppCmdStr);

    if (grabArgument(ppCmdStr, keyword, 16) == -1)
      return NULL;

    // search the current list
    while (pCmdNode != NULL) {
      if (strcmp(keyword, pCmdNode->cmdString) == 0)
        break;
      pCmdNode = pCmdNode->next;
    }

    if (pCmdNode == NULL) {
      if (pPrevNode == NULL) {
        return NULL;
      }
      else {
        *ppCmdStr = pPrevStr; // restore the tail of the string
        return pPrevNode;
      }
    }
    else {
      pPrevNode = pCmdNode;
      pPrevStr = *ppCmdStr;
      
      // TM DEBUG: got here with no string remaining and the correct 
      //  command was in pCmdNode. It got boinked by the following assignment
      
      if (pCmdNode->subCommand != NULL) {
        pCmdNode = pCmdNode->subCommand;
      }
      else {
	break;
      }
    }
  }

  return pCmdNode; // TODO: check this
}

int cmdparseDoCommand(char * pCmdStr)
{
  int retVal;
  cmdNode * pFindCmd;

  if (*pCmdStr == '\0') {
    return 0;
  }

  // find the command
  pFindCmd = findCommand(cmdList, &pCmdStr);

  if ((pFindCmd != NULL) && (pFindCmd->handler != NULL)) {
    // TODO: eat whitespace here??
    retVal = pFindCmd->handler(pCmdStr);
  }
  else {
    return ERRNUM_INVALID_CMD;
  }

  return retVal;
}

int invalidCommandHandler(char * cmdStr)
{
  return ERRNUM_INVALID_CMD;
}




