#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <serial.h>
#include <lpc.h>

const struct lpc_part lpc_parts[] = {
  {"LPC1111/101", 0x041e502b, 2, 8},
  {"LPC1111/101 or 102", 0x2516d02b, 2, 8},
  {"LPC1111/201", 0x0416502b, 4, 8},
  {"LPC1111/201 or 202", 0x2516902b, 4, 8},
  
  {"LPC1112/101", 0x042d502b, 2, 16},
  {"LPC1112/101 or 102", 0x2524d02b, 2, 16},
  {"LPC1112/201", 0x0425502b, 4, 16},
  {"LPC1112/201 or 202", 0x2524902b, 4, 16},
  
  {"LPC1113/201", 0x0434502b, 4, 24},
  {"LPC1113/201 or 202", 0x2532902b, 4, 24},
  {"LPC1113/301", 0x0434102b, 8, 24},
  {"LPC1113/301 or 302", 0x2532102b, 8, 24},
  
  {"LPC1114/201", 0x0444502b, 4, 32},
  {"LPC1114/201 or 202", 0x2540902b, 4, 32},
  {"LPC1114/301", 0x0444102b, 8, 32},
  {"LPC1114/301 or 302", 0x2540102b, 8, 32},
  
  {"LPC11C12/301", 0x1421102b, 8, 16},
  {"LPC11C14/301", 0x1440102b, 8, 32},
  {"LPC11C22/301", 0x1431102b, 8, 16},
  {"LPC11C24/301", 0x1430102b, 8, 32},
  {NULL, 0, 0, 0}
};

const struct sprog_family lpc_family = {
  .setup = (void*(*)(struct serial_device*)) lpc_setup,
  .init = (void(*)(void*)) lpc_init,
  .close = (void(*)(void*)) lpc_close
};
  
  

void lpc_ispmode(struct lpc_device *dev, int state);
void lpc_reset(struct lpc_device *dev);
int lpc_command(char *text, ...);
int lpc_read_partid(struct lpc_device *dev);

struct lpc_device *lpc_setup(struct serial_device *port) {
  struct lpc_device *dev;
  dev = malloc(sizeof(struct lpc_device));
  dev->port = port;
  dev->part = NULL;
  lpc_ispmode(dev, 1);
  lpc_reset(dev);
  return dev;
}

void lpc_init(struct lpc_device *dev) {
  char buf[4096];
  printf("?");
  fflush(stdout);
  if(sprog_waitdata(500)==0) {
    printf("\r\n");
    fflush(stdout);
    fgets(buf, sizeof(buf), stdin);
    if(strcmp(buf, "?\r\n")==0)
      sprog_error("The device appears to be already synchronized\n");
      fgets(buf, sizeof(buf), stdin); /* deny invalid command reply */
  } else {
    fgets(buf, sizeof(buf), stdin);
    if(strcmp(buf, "Synchronized\r\n")==0)
      sprog_error("Synchronization successful\n");
    printf("Synchronized\r\n");
    fflush(stdout);
    fgets(buf, sizeof(buf), stdin); /* deny echoed line */
    fgets(buf, sizeof(buf), stdin);
    if(strcmp(buf, "OK\r\n")!=0)
      sprog_error("Expected OK, received '%s'\n", buf);
    sprog_error("Sending clock frequency\n");
    printf("12000\r\n");
    fflush(stdout);
    fgets(buf, sizeof(buf), stdin); /* deny echoed line */
    fgets(buf, sizeof(buf), stdin);
    if(strcmp(buf, "OK\r\n")!=0)
      sprog_error("Expected OK, received '%s'\n", buf);
  }
  sprog_error("Reading part ID\n");
  if(lpc_read_partid(dev)==0)
    sprog_error("Found device: %s, %dkB Flash, %dkB RAM\n", dev->part->name, dev->part->flash, dev->part->ram);
}

int lpc_command(char *text, ...) {
  char buf[4096];
  int res;
  va_list l;
  va_start(l, text);
  vprintf(text, l);
  fflush(stdout);
  fgets(buf, sizeof(buf), stdin); /* deny echoed line */
  if(scanf("%d", &res)<1)
    return -1;
  va_end(l);
  return res;
}

int lpc_read_partid(struct lpc_device *dev) {
  int i;
  int res;
  unsigned int partid;
  res = lpc_command("J\r\n");
  if(res!=0)
    return res;
  scanf("%u", &partid);
  for(i=0; lpc_parts[i].name; i++)
    if(lpc_parts[i].part_id==partid)
      break;
  if(lpc_parts[i].name)
    dev->part = &lpc_parts[i];
  else
    return -1;
  return 0;
}

void lpc_ispmode(struct lpc_device *dev, int state) {
  serial_setline(dev->port, SERIAL_DTR, state);
}

void lpc_reset(struct lpc_device *dev) {
  /* TODO: configure the control lines */
  serial_setline(dev->port, SERIAL_RTS, 1);
  sprog_sleep(50);
  serial_setline(dev->port, SERIAL_RTS, 0);
  sprog_sleep(10);
}

void lpc_close(struct lpc_device *dev) {
  serial_close(dev->port);
  free(dev);
}