/* this file porovides the usb interaction code */
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "common.h"
#include "interface.h"
#include "output.h"
#include "power.h"
#include "usb/usb.h"
#include "memory.h"


#define BUF_SIZE 32

ram char buffer_in[BUF_SIZE];
char in_index;
ram char buffer_out[BUF_SIZE];
char data_pending=0;
rom char ok[]="\nOK\n";
rom char ko[]="\nKO\n";

/* 0 is still button pressed from turn_on */
/* 1 is button released */
/* 2 is button pressed again */
unsigned char power_state;

/* utility calls */
unsigned char readHexByte(const char *text, unsigned char *result) {
  unsigned char tempa,tempb;
  if (text[0] == '\0') return 0; /* return no error if no arg supplied */
  tempa = text[0] - 0x30;
  if (tempa > 9) tempa -= 7; // A..F
  if (tempa > 15) tempa -= 0x20; // a..f
  if (tempa > 15) return -1; // invalid conversion
  tempb = text[1] - 0x30;
  if (tempb > 9) tempb -= 7;
  if (tempb > 15) tempb -= 0x20; 
  if (tempb > 15) return -1; // invalid conversion
  *result = (tempa << 4) + tempb;
  return 0;
}

unsigned char readHexWord(const char *text, signed short *result) {
  unsigned char tempa, tempb;
  if (text[0] == '\0') return 0; /* return no error if no arg supplied */
  if (readHexByte(text,&tempa)) return -1;
  if (readHexByte(text+2,&tempb)) return -1;
  *result = tempa * 256 + tempb;
  return 0;
}

unsigned char readBoolean(const char *text, unsigned char *result) {
  if (text[0] == '\0') return 0; /* return no error if no arg supplied */
  if (text[0]=='0') {
    *result = 0;
    return 0;
  }
  if (text[0]=='1') {
    *result = 1;
    return 0;
  }
  return -1;
}

char* find_token(void) {
  char *index = buffer_in;
  while (isalnum(*index)) ++index; /* get to the end of the command word (eg "Read"); */
  while (isspace(*index)) ++index; /* get to next actual thing, either \0 or something interesting */
  return index; 
}

void initialise_interface(void) {
	mInitializeUSBDriver();
  	/* set up our buffers */
	memset(buffer_in,0,BUF_SIZE);
	memset(buffer_out,0,BUF_SIZE);
  	in_index = 0;
	power_state = 0;
}

unsigned char get_data(void) {
	return getsUSBUSART(buffer_in + in_index, BUF_SIZE-1-in_index);
}

void put_data(void) {
	if (mUSBUSARTIsTxTrfReady()) {
		putsUSBUSART(buffer_out);
		data_pending = 0;
	}
}

char service_command(void) {
	char retval = -1;
	unsigned char temp;
	signed short tempw;
	memset(buffer_out,0,BUF_SIZE);
/* FIXME */
//	memcpy(buffer_out,buffer_in,BUF_SIZE);
//	return 0;
/* FIXME */
	switch(buffer_in[0]) {
	case 'H':
		retval = 0;
		conf = conf_backup;
		sprintf(buffer_out,"Hiya");
		break;
  	case 'L':
    	retval = readHexByte(find_token(),&conf.laser);
	    sprintf(buffer_out,"%02hhX",conf.laser);
		set_laser_brightness();
    	break;
	case 'D':
    	retval = readHexByte(find_token(),&conf.display);
	    sprintf(buffer_out,"%02hhX",conf.display);
		set_display_brightness();
    	break;
  	case 'B':
    	temp = conf.battery;
	    retval = readBoolean(find_token(),&temp);
    	if (!retval) conf.battery = temp;
	    sprintf(buffer_out,"%d",conf.battery);
    	break;
	case 'T':
    	temp = conf.temperature;
	    retval = readBoolean(find_token(),&temp);
    	if (!retval) conf.temperature = temp;
	    sprintf(buffer_out,"%d",conf.temperature);
    	break;
	case 'G':
		retval = 0;
		switch(buffer_in[1]) {
		case 'G':
		case 'M':
			sprintf(buffer_out,"BLNK");
			break;
		case 'C':
			sprintf(buffer_out,"%2hd",get_charge());
			break;
		case 'T':
			sprintf(buffer_out,"%2hd",get_temperature());
			break;
		case 'I':
			sprintf(buffer_out,"%2hd",get_current());
			break;
		case 'V':
			sprintf(buffer_out,"%2hd",get_voltage());
			break;
		default:
			retval = -1;
		}
	case 'C':
		retval = 0;
		memcpyram2flash(&conf_backup,&conf,sizeof(config));
		break;
	case 'Q':
		shutdown();
	}
	return retval;
}

void do_comms(void) {
  char offset;
  offset = get_data();
  if (offset) {
    /* we've got some new data */
    char* fin = strchr(buffer_in,0x0a);
    in_index += offset;
    if (in_index >= BUF_SIZE-1) {
      fin = buffer_in + BUF_SIZE-1;
    }
    if (fin) {
      /* buffer is either full or we have a newline. fin points to end of text... */
      *fin = '\0'; /* make sure that our text has newline stripped and zero-terminated*/
      if (service_command()) {
        /* failed read */
        strcatpgm2ram(buffer_out,ko);
      } else {
        strcatpgm2ram(buffer_out,ok);
        /* successful read */
      }
      in_index = 0;
	  memset(buffer_in,0,BUF_SIZE);
	  data_pending = 1;
    }
  }
  if (data_pending) {
	put_data();
  }
}

void ServiceUSB(void) {
	USBCheckBusStatus();
	if (usb_device_state > DETACHED_STATE) {
		do {
			USBDriverService();
			}	
		while (usb_device_state < CONFIGURED_STATE);
		/* only do these if actually connected */
		do_comms();
		CDCTxService();
	} else {
		/* no point processing power events if USB connected */
		switch (power_state) {
		case 0: if (!power_pressed()) 
			power_state = 1;
			break;
		case 1:	if (power_pressed()) 
			power_state = 2;
			break;
		case 2: if (!power_pressed()) {
				Delay10KTCYx(200);
				shutdown();
				}
			break;
		case 6: shutdown();
			/* close down on detach from USB, if that's how we started... */
		}
	}
}


