#ifdef __BORLANDC__
#include <conio.h>
#define SEND(c) putch(c)
#else
#include <msp430x14x.h>
#define SEND(c) usart0TxOne((char)c)
#endif

#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "usart0.h"
#include "errnum.h"
#include "sensor.h"
#include "util.h"

#define OUTFMT_BUFFER_SIZE (192)

static char buffer[OUTFMT_BUFFER_SIZE];

const char prompt[5] = {'\x0a', '\x0d', '>',' ','\x00'};

const char crlf[3] = {'\x0a', '\x0d','\x00'};

const char lineTerm[3] = {0x0d, 0x0a, 0x00};

const char * sensorCode[] = { "P",
			      "T",
			      "H",
			      "GFL",
			      "GFH",
			      "C",
			      "TC",
			      "xxx" }; // countdown timer

const char * levelCode[] = { "PL",
			     "PH",
			     "TL",
			     "TH",
			     "HL",
			     "HH",
			     "GFLL",
			     "GFLH",
			     "GFHL",
			     "GFHH",
			     "CL",
			     "CH",
			     "TCL",
			     "TCH",
			     "xxx",
			     "xxx"
			      };
			     
static const char * amaskOutFmt   = "$PAMASK, %02x, ";
static const char * alarmOutFmt   = "$PALARM, %02x, ";
static const char * versionOutFmt = "$PSID, V%s, ";
static const char * dateOutFmt    = "$PDATE, %02d/%02d/%04d ";
static const char * timeOut12Fmt  = "$PTIME12, %02d:%02d:%02d%c ";
static const char * timeOut24Fmt  = "$PTIME24, %02d:%02d:%02d ";
static const char * errorOutFmt   = "$PERR, E%d, %s ";

static boolean timeFormat24Hr = false;

static void outputString(const char * pStr, int len) 
{
  int i = 0;

  if ((*pStr == '\0') || (len <= 0))
    return;

  while((*pStr != '\x00') && (i < len)) {
    if (usart0TxOne(*pStr) == 0) {
      pStr++; i++;
      }
    }
}

static void outputBuffer(void)
{
  outputString(buffer, OUTFMT_BUFFER_SIZE);
}

int outfmtInit(void)
{
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  return 0;
}

int outfmtGetBuffer (char **ppBuf, int *pBufSiz)
{
  *ppBuf = buffer;
  *pBufSiz = OUTFMT_BUFFER_SIZE;
  return 0;
}

int outfmtSendBuffer(void)
{
  outputString(buffer, OUTFMT_BUFFER_SIZE);
  return 0;
}


// append checksum and [cr][lf] terminating sequence to null-terminated string
static void appendChecksum(char * nmeaStr)
{  
  char * pBufferOffset = nmeaStr;
  char * pChksum = nmeaStr;
  int checksum = 0;

  // move the offset pointer to the first null after the space
  while(*pBufferOffset != '\0') pBufferOffset++;

  // compute and print the checksum
  while (*pChksum != '\0') {
    checksum += (int)(*pChksum);
    pChksum++;
  }
  
  sprintf(pBufferOffset, "*%d%s",checksum,lineTerm);
}

void outfmtSendAlarmInfo(int type, unsigned char avalue)
{
  const char * pFormat;
  
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  
  if (type == 0)
    pFormat = amaskOutFmt;
  else if (type == 1)
    pFormat = alarmOutFmt;
  else
    return;
  
  sprintf(buffer, pFormat, avalue);
  
  appendChecksum(buffer);
  
  outputBuffer();
}

void outfmtSendPrompt(void) 
{
  outputString(prompt, 5);
}

void outfmtSendCrLf(void)
{
  outputString(crlf, 3);
}

void outfmtSendVersion(char * versionString)
{
  // TODO: what about truncating the length of versionString if it's too long
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  sprintf(buffer, versionOutFmt, versionString);

  appendChecksum(buffer);
  outputBuffer();
}

void outfmtSendData(void)
{
  int i;
  float sensorVal;
  char * pBufferOffset = buffer;

  // init the buffer and print the header
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  sprintf(buffer, "$PEDATA, ");

  // move the offset pointer to the first null after the space
  while(*pBufferOffset != '\0') pBufferOffset++;

  // print the current value for each sensor
  for (i=0;i<SENSOR_NUM_SENSORS;i++) {
    if (i == SENSOR_ID_COUNTDOWN) continue; // FIXME: find better mechanism
    if (sensorGetData(i, &sensorVal) != 0) {
      sensorVal = -1.0;
    }
    
    sprintf(pBufferOffset, "%s%4.2f, ", sensorCode[i], sensorVal);
    while(*pBufferOffset != '\0') pBufferOffset++;
  }

  appendChecksum(buffer);
  
  outputBuffer();   
}

void outfmtSendLevels(void)
{
  int i;
  float levelVal;
  char * pBufferOffset = buffer;

  // init the buffer and print the header
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  sprintf(buffer, "$PALEVEL, ");

  // move the offset pointer to the first null after the space
  while(*pBufferOffset != '\0') pBufferOffset++;

  // print the current value for each sensor
  for (i=0;i<(SENSOR_NUM_SENSORS<<1);i++) {
    if (i == (SENSOR_ID_COUNTDOWN<<1)) continue; // FIXME: find a better mechanism
    if (i == ((SENSOR_ID_COUNTDOWN<<1)+1)) continue; //FIXME:
    sensorGetLevel(i, &levelVal);
    sprintf(pBufferOffset, "%s%f, ", levelCode[i], levelVal);
    while(*pBufferOffset != '\0') pBufferOffset++;
  }

  appendChecksum(buffer);
  
  outputBuffer();   
}

void outfmtSendDate(int sendMonth, int sendDay, int sendYear)
{
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  sprintf(buffer, dateOutFmt, sendMonth, sendDay, (sendYear+2000));
  appendChecksum(buffer);
  outputBuffer();
}

void outfmtSendTime(int sendHour, int sendMin, int sendSec)
{
  char time12Suffix;
  
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  
  if (timeFormat24Hr == true) {
    sprintf(buffer, timeOut24Fmt, sendHour, sendMin, sendSec);
    }
  else {
    if (sendHour > 12) {
      time12Suffix = 'p';
      sendHour -= 12;
      }
    else {
      time12Suffix = 'a';
      }
    sprintf(buffer, timeOut12Fmt, sendHour, sendMin, sendSec, time12Suffix);
    }
  
  appendChecksum(buffer);
  outputBuffer();
}

static const char * pUnknownErrorStr = "Unknown error";

void outfmtSendError(int errNum)
{
  char * pErrStr = (char *)errnumGetDesc(errNum);

  if (pErrStr == NULL) {
    pErrStr = (char *)pUnknownErrorStr;
  }
  
  // TODO: check length of errStr
  clearString(buffer, OUTFMT_BUFFER_SIZE);
  sprintf(buffer, errorOutFmt, errNum, pErrStr);
  appendChecksum(buffer);
  outputBuffer();
}
