#include "acap_parse.h"
#include "acap_commands.h"
#include "util.h"
#include "scan.h"
#include "log.h"

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

#ifdef DEBUG
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#ifdef DEBUG
#include <stdio.h>
#endif

static char parse_tag[MAX_TAG_LEN+1];
static char inbuf[MAX_LINE_LEN+1], outbuf[MAX_LINE_LEN+1];
static char *inptr;
static ParseReturn (*continue_func)() = NULL;
static union {
  StoreStruct store;
  SearchStruct search;
  AuthStruct auth;
} command;


void
acapParseInit()
{
  acapCommandsInit();
}

ParseReturn
acapParse(char *buf)
{
  strcpy(inbuf, buf);
  inptr = inbuf;

  if (continue_func)
    return (*continue_func)();
  else
    return parseNew(); 
}

ParseReturn
parseNew()
{
  ParseReturn pr;
  char command[MAX_COMMAND_NAME_LEN+1];
  int idx;

  /* copy the tag from buf to parse_tag */
  for (idx=0; isTagChar(*inptr)&&idx<(sizeof(parse_tag)-1);
       parse_tag[idx++] = *(inptr++));
  parse_tag[idx] = 0;

  /* check for some errors */
  if ((isCRLF(inptr)||*inptr==' ')&&!idx) {
    sprintf(outbuf, "* BAD \"Tag expected\"\r\n");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    return pr;
  }
  else if (isCRLF(inptr)) {
    sprintf(outbuf, "%s BAD \"Command indentifier expected\"\r\n",
	    parse_tag);
    pr.result = ParseNext;
    pr.outstr = outbuf;
    return pr;
  }
  else if (*inptr!=' '&&!isTagChar(*inptr)) {
    sprintf(outbuf, "* BAD \"Invalid tag character 0x%X\"\r\n",
	    *inptr);
    pr.result = ParseNext;
    pr.outstr = outbuf;
    return pr;
  }
  else if (isTagChar(*inptr)&&idx==sizeof(parse_tag)-1) {
    sprintf(outbuf, "* BAD \"Tag too long\"\r\n");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    return pr;
  }

  /* eat the space */
  if (*inptr==' ')
    inptr++;

  /* copy the command from buf to command */
  for (idx=0; !isCRLF(inptr)&&*inptr!=' '&&idx<(sizeof(command)-1);
       command[idx++] = *(inptr++));
  command[idx] = 0;

  /* ACAP is not case-sensitive */
  strToUpper(command);

  /* call command handling function */
  if (!strcmp(command, "NOOP"))
    return parseNoop();
  else if (!strcmp(command, "LOGOUT"))
    return parseLogout();
  else if (acap_state==NonAuth) {
    if (!strcmp(command, "AUTHENTICATE"))
      return parseAuth();
#ifdef DEBUG
    else if (!strcmp(command, "auth"))
      return parseAuth();
#endif
  }
  else if (acap_state==Auth) {
    if (!strcmp(command, "STORE"))
      return parseStore();
    else if (!strcmp(command, "SEARCH"))
      return parseSearch();
  }

  /* unknown command */
  sprintf(outbuf, "%s BAD \"Unknown command or wrong server state\"\r\n",
	  parse_tag);
  pr.result = ParseNext;
  pr.outstr = outbuf;
  return pr;
}

ParseReturn
parseContinueCommand()
{
  ParseReturn pr;

  if (commandMore(parse_tag)==CommandMore) {
    pr.result = MoreToCome;
    continue_func = parseContinueCommand;
  }
  else {
    pr.result = ParseNext;
    continue_func = NULL;
  }
  
  pr.outstr = command_result;
  return pr;
}

ParseReturn
parseNoop()
{
  ParseReturn pr;

  if (isCRLF(inptr))
    sprintf(outbuf, "%s OK \"Finished doing nothing\"\r\n",
	    parse_tag);
  else
    sprintf(outbuf, "%s BAD \"Unexpected argument\"\r\n",
	    parse_tag);

  pr.result = ParseNext;
  pr.outstr = outbuf;
  return pr;
}

ParseReturn
parseAuth()
{
  ParseReturn pr;

  if (!continue_func) {
    setupScan(" %s %s", 2,
	      command.auth.type, sizeof(command.auth.type)-1,
	      command.auth.param, sizeof(command.auth.param)-1);
  }
  
  continue_func = NULL;
  
  switch (scanLine(inptr)) {
  case ScanBad:
    sprintf(outbuf, "%s BAD \"Bad command syntax\"\r\n", parse_tag);
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanMore:
    continue_func = parseAuth;
    strcpy(outbuf, "");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanMoreSync:
    continue_func = parseAuth;
    sprintf(outbuf, "+ \"Ready for additional command text\"\r\n");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanOk:
    switch (acapAuth(parse_tag, &command.auth)) {
    case CommandDone:
      pr.result = ParseNext;
      break;
    default:
      break;
    }
    pr.outstr = command_result;
    break;
  }
  
  return pr;
}

ParseReturn
parseStore()
{
  ParseReturn pr;

  if (!continue_func) {
    setupScan(" (%s %s %s)", 3,
	      command.store.entrypath, sizeof(command.store.entrypath)-1,
	      command.store.attribute, sizeof(command.store.attribute)-1,
	      command.store.value, sizeof(command.store.value)-1);
  }

  continue_func = NULL;

  switch (scanLine(inptr)) {
  case ScanBad:
    sprintf(outbuf, "%s BAD \"Bad command syntax\"\r\n", parse_tag);
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanMore:
    continue_func = parseStore;
    strcpy(outbuf, "");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanMoreSync:
    continue_func = parseStore;
    sprintf(outbuf, "+ \"Ready for additional command text\"\r\n");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanOk:

    switch (acapStore(parse_tag, &command.store)) {
    case CommandDone:
      pr.result = ParseNext;
      break;
    case CommandMore:
      pr.result = MoreToCome;
      continue_func = parseContinueCommand;
    default:
      break;
    }
    pr.outstr = command_result;
    break;

  }

  return pr;
}

ParseReturn
parseSearch()
{
  ParseReturn pr;

  if (!continue_func) {
    /* should be "RETURN", not "return" */
    setupScan(" %s return \\(%s\\)%i", 2,
	      command.search.entrypath, sizeof(command.search.entrypath)-1,
	      command.search.attribute, sizeof(command.search.attribute)-1);
  }

  continue_func = NULL;

  switch (scanLine(inptr)) {
  case ScanBad:
    sprintf(outbuf, "%s BAD \"Bad command syntax\"\r\n", parse_tag);
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanMore:
    continue_func = parseSearch;
    pr.result = ParseNext;
    pr.outstr = NULL;
    break;
  case ScanMoreSync:
    continue_func = parseSearch;
    sprintf(outbuf, "+ \"Ready for additional command text\"\r\n");
    pr.result = ParseNext;
    pr.outstr = outbuf;
    break;
  case ScanOk:

    switch (acapSearch(parse_tag, &command.search)) {
    case CommandDone:
      pr.result = ParseNext;
      break;
    case CommandMore:
      pr.result = MoreToCome;
      continue_func = parseContinueCommand;
    default:
      break;
    }
    pr.outstr = command_result;
    break;

  }

  return pr;
}

ParseReturn
parseLogout()
{
  ParseReturn pr;

  if (isCRLF(inptr)) {
    pr.result = SessionDone;
    sprintf(outbuf,
	    "* BYE \"Closing connection\"\n%s OK \"LOGOUT completed\"\r\n",
	    parse_tag);
    lprintf(LOG_INFO, "User logged out");
    acap_state = Logout;
  }
  else {
    pr.result = ParseNext;
    sprintf(outbuf, "%s BAD \"Unexpected argument\"\r\n",
	    parse_tag);
  }

  pr.outstr = outbuf;

  return pr;
}

