#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include "SerialParameters.h"
#include "StringConverter.h"
#include "Syslog.h"

SerialParameters::SerialParameters(int *argc, char **argv,
				   const char *defaultPortName)
{
  if (defaultPortName)
    portName = strdup(defaultPortName);
  else
    portName = 0;

  baud = 9600;
  dataBits = 8;
  parity = NoParity;
  stopBits = 1;

  Boolean error = False;

  for (int i = 1; i < *argc; i++) {

    if (!strcmp(argv[i], "-dev")) {

      if (i < *argc - 1) {
	free((void *)portName);
	portName = strdup(argv[++i]);
      }
      else {
	error = True;
      }
    } 
     
    else if (!strcmp(argv[i], "-serial")) {
      if (i < *argc - 1) {
	free((void *)portName);
	try {
	  parseString(argv[++i]);
	}
	catch (Exception e) {
	  Syslog::write("Bad serial parameter string: %s", e.msg);
	  error = True;
	}
      }
      else {
	error = True;
      }
    }
  }

  if (!portName) {
    Syslog::write("Serial port name not specified");
    error = True;
  }

  if (error) {
    Syslog::write("Valid args:\n"
		  "-dev deviceFileName\n"
		  "-serial devName,baud,dataBits,parity,stopBits\n");

    throw Exception("Error in serial parameter specification");
  }

  sprintf(_string, "%s,%d,%d,%c,%d",
	  portName, baud, dataBits, 
	  parity ? 'y' : 'n',
	  stopBits);
}


SerialParameters::~SerialParameters()
{
  free((void *)portName);
}


const char *SerialParameters::string()
{
  return _string;
}


void SerialParameters::parseString(const char *string)
{
  char buf[100];
  strcpy(buf, string);

  enum {
    PortName, 
    BaudRate,
    DataBits,
    Parity,
    StopBits,

    NParamFields
  };

  char *token;
  char *ptr;
  int nTokens;
  for (ptr = buf, nTokens = 0; 
       (token = strtok(ptr, ",")) != 0; 
       ptr = 0, nTokens++) {

    switch (nTokens) {
    case PortName:
      portName = strdup(token);
      break;

    case BaudRate:
      baud = StringConverter::stringToInteger(token);
      break;

    case DataBits:
      dataBits = StringConverter::stringToInteger(token);
      break;

    case Parity:
      parity = getParity(token);
      break;

    case StopBits:
      stopBits = StringConverter::stringToInteger(token);
      break;

    default:
      throw Exception("Extra token(s)?");
    }
  }
	 
  if (nTokens != NParamFields) {
    // Error
    throw Exception("Missing token(s)?");
  }

}


SerialParameters::Parity SerialParameters::getParity(char *token)
{
  char str[100];
  char errorBuf[100];

  // Case-fold to lower case
  for (int i = 0; i < strlen(token); i++) {
    str[i] = tolower(token[i]);
  }

  if (!strcmp(str, "n") || !strcmp(str, "no") || !strcmp(str, "none"))
    return SerialParameters::NoParity;
  else if (!strcmp(str, "e") || strcmp(str, "even"))
    return SerialParameters::EvenParity;
  else if (!strcmp(str, "o") || strcmp(str, "odd"))
    return SerialParameters::OddParity;
  else {
    sprintf(errorBuf, "Unknown parity: \"%s\"", token);
    throw Exception(errorBuf);
  }
}
