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

#define MAX_LINE_LEN 128

#define CHAR_WHITESPACE 0
#define CHAR_NUMERIC    1
#define CHAR_LETTER     2
#define CHAR_OTHER      3

#define SENSOR_TYPE_O3  0
#define SENSOR_TYPE_CO  1

typedef struct data_point {
  double reading;
  double lat;
  double lon;
  char date[16];
} data_point_t;

int charType( char c )
{
  //  printf("\nCHAR:%d \n%c\n", c,c);
  if (c == ' ' || c == '\t') return CHAR_WHITESPACE;
  if ((c >= '0' && c <= '9') || c == '.') return CHAR_NUMERIC;
  if (c >= 'A' || c <= 'Z') return CHAR_LETTER;
  printf("OTHER:%d\n", c);
  return CHAR_OTHER;
}

int nextToken( char *s, int start )
{
  if (start < 0) return -1;

  int startType = charType(s[start]);
  int len = strlen(s);
  int i = start;

  while (i < len && charType(s[i]) == startType) i++;

  if (i == len) {
    printf("############ badNextToken\n");
    return -1;
  }
  return i;
}

int parseDataPoint( char *line, data_point_t* dp, int sensor_type )
{
  double reading = -1;
  double lonNMEA = 0;
  char lonDir = 0;
  double latNMEA = 0;
  char latDir = 0;
  char date[16];

  int pos = 0;
  while ((line[pos] == 0 || line[pos] == ' ') && pos < MAX_LINE_LEN) pos++;
  printf("LINE=%s\n", line);
  reading = atoi(line + pos);
  if (sensor_type == SENSOR_TYPE_O3) {
    reading /= 10.0;
  } 
  pos = nextToken(line, pos); // the reading 
  pos = nextToken(line, pos); // whitespace
  if (pos < 0) return 1;
  latNMEA = atof(line + pos);
  pos = nextToken(line, pos); // lat
  if (pos < 0) return 1;
  latDir = line[pos++];
  lonNMEA = atof(line + pos);
  pos = nextToken(line, pos); // lon
  if (pos < 0) return 1;
  lonDir = line[pos++];
  strncpy(date, line+pos, 6);
  date[6] = 0;

  if (reading < 0 || lonNMEA < 1 || latNMEA < 1 || lonDir == 0 || latDir == 0) return 1;

  //  printf("%d,%f,%c,%f,%c,%s\n", reading, lonNMEA, lonDir, latNMEA, latDir, date);

  dp->reading = reading;
  dp->lon = (int)(lonNMEA / 100);
  dp->lon += (lonNMEA - dp->lon * 100) / 60.0;
  if (lonDir == 'W') dp->lon = -dp->lon;

  dp->lat = (int)(latNMEA / 100);
  dp->lat += (latNMEA - dp->lat * 100) / 60.0;
  if (latDir == 'S') dp->lat = -dp->lat;

  strcpy(dp->date, date);

  if (strlen(date) != 6) return 1;

  return 0;
}

char *getSensorString( char *sensor_string, int sensor_type )
{
  if (sensor_type == SENSOR_TYPE_O3) {
    strcpy(sensor_string, "OZONE");
  } else {
    strcpy(sensor_string, "CO");
  }
  return sensor_string;
}

char *getKMLStart( char* line_out, int sensor_type, char* folder_name )
{
  char sensor_string[16];
  getSensorString(sensor_string, sensor_type);

  int pos = 0;
  pos += sprintf(line_out + pos, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  pos += sprintf(line_out + pos, "<kml xmlns=\"http://earth.google.com/kml/2.0\">\n");
  pos += sprintf(line_out + pos, "\t<Folder><description>%s readings</description><name>%s</name>\n", sensor_string, folder_name);
  return line_out;
}

char *getKMLEnd( char *line_out )
{
  sprintf(line_out, "\t</Folder>\n</kml>\n");
  return line_out;
}

char *lineToKML( char *line_in, char *line_out, int sensor_type )
{
  data_point_t dp;

  char sensor_string[16];
  getSensorString(sensor_string, sensor_type);

  if (parseDataPoint(line_in, &dp, sensor_type)) {
    return 0;
  }

  int pos = 0;
  pos += sprintf(line_out + pos, "\t\t<Placemark>\n");
  pos += sprintf(line_out + pos, "\t\t\t<description>AIR %s data point, date=%s</description>\n", sensor_string, dp.date);
  pos += sprintf(line_out + pos, "\t\t\t<name>%s=%.1fppm</name>\n", sensor_string, dp.reading);
  pos += sprintf(line_out + pos, "\t\t\t<visibility>1</visibility>\n");
  pos += sprintf(line_out + pos, "\t\t\t<Point><extrude>1</extrude><altitudeMode>relativeToGround</altitudeMode>\n");
  pos += sprintf(line_out + pos, "\t\t\t<coordinates>%lf,%lf,0</coordinates>\n", dp.lon, dp.lat);
  pos += sprintf(line_out + pos, "\t\t\t</Point>\n");
  pos += sprintf(line_out + pos, "\t\t</Placemark>\n");
  return line_out;
}

void emptyLine( char *line )
{
  int i;
  for (i = 0; i < MAX_LINE_LEN; i++) line[i] = '\n';
}

void fixLine( char *line )
{
  int i;
  for (i = 0; i < MAX_LINE_LEN; i++) {
    if (line[i] == 10 || line[i] == 13) {
      line[i] = 0;
      return;
    }
    if (line[i] == 0) line[i] = ' ';
  }
}

void usage( char *program_name )
{
  printf("this program takes two arguments:\n\n");
  printf("  %s <sensor-type> <input-file> <output-file>\n\n", program_name);
  printf(" where <sensor-type> is 0 (O3), or 1 (CO)\n");
  printf(" <input-file> is the name of a text file containing data passed from the arduino\n");
  printf(" <output-file> is the name of a KML file to write.\n");
}

int main( int argc, char** argv ) 
{
  if (argc != 4) {
    usage(argv[0]);
    return -1;
  }

  char line[MAX_LINE_LEN];
  char line_out[1024];
  int sensor_type = atoi(argv[1]);
  FILE *fin = fopen(argv[2], "r");
  FILE *fout = fopen(argv[3], "w");

  int line_num = 0;

  fprintf(fout, "%s", getKMLStart(line_out, sensor_type, argv[3]));
  emptyLine(line);
  while (fgets(line, MAX_LINE_LEN, fin)) {
    fixLine(line);
    printf("line=%s\n", line);
    printf("line len=%d\n", strlen(line));

    line_num++;
    if (lineToKML(line, line_out, sensor_type)) {
      fprintf(fout, "%s", line_out);
    }
    emptyLine(line);
  }
  fprintf(fout, "%s", getKMLEnd(line_out));

  fclose(fout);
  fclose(fin);

  return 0;
}
