#include <NewSoftSerial.h>
#include <etherShield.h>
#include <LiquidCrystal.h>

//#define DEBUG

#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif

#include <string.h> // for strlen();

// Define the pins used for the software serial port.  Note that we won't
// actually be transmitting anything over the transmit pin.
#define rxPin 3
#define txPin 300

// LED onboard
#define ledPin 13

// Set up the serial port
NewSoftSerial currentcost(rxPin, txPin);

char stringData[16];
int content_length;

const char iphdr[] PROGMEM ={
  0x45,0,0,0x82,0,0,0x40,0,0x20};

// MAC and IP have to be unique in your local area network.
static uint8_t mymac[6] = {
  0x11,0x22,0x33,0x44,0x55,0x66};   // The MAC address of your arduino.
static uint8_t myip[4] = {
  192,168,0,99};                    // The IP address of your arduino.
static uint16_t srcport = 6407;

// Destination settings
static uint8_t destmac[6] = {
  0x01,0x23,0x45,0x67,0x89,0xAB};   // The MAC address of your router.
static uint8_t broadcastip[4] = {
  1,2,3,4 };                        // The IP address of your server.

// Destination Port 52240
#define DEST_PORT_L  0x10
#define DEST_PORT_H  0xCC

char startPwr[] = "<ch1><watts>";
char startTmpr[] = "<tmpr>";
char endChar = '<';

char readChar = 0xFF;

int sizePwr = sizeof(startPwr)-1;
int sizeTmpr = sizeof(startTmpr)-1;

int statePwr = sizePwr;
int stateTmpr = sizeTmpr;

int newstate = 0;

long PwrNum = 0;
double TmprDouble = 0;

char Pwr[16] = "";
char Tmpr[16] = "";

// Packet buffer, must be big enough to packet and payload
#define BUFFER_SIZE 150
static uint8_t buf[BUFFER_SIZE+1];

EtherShield es=EtherShield();
LiquidCrystal lcd(8, 11, 9, 4, 5, 6, 7);

struct UDPPayload {
  uint32_t time;
  long power;
  double temperature;
};
UDPPayload udpPayload;

char ready = 0;

void setup(void) {
	// Display welcome message on LCD screen.
  lcd.begin(16, 2);
  lcd.print("Current Cost+");

  // Define pin modes for tx and rx pins.
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  // Set the data rate for the NewSoftSerial port.
  currentcost.begin(57600);

  // Set the data rate for the hardware serial port
  Serial.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  // Initialise the UDP payload
  udpPayload.time = millis();
  udpPayload.temperature = 0;
  udpPayload.power = 0;

  init_ethershield();
}

void loop(void) {

  readChar = currentcost.read();

  if (readChar > 31) {
    stateTmpr = parseDataTmpr(stateTmpr, readChar);
    if (stateTmpr < 0) {
      Tmpr[abs(stateTmpr)-1] = readChar;
    }
    statePwr = parseDataPwr(statePwr, readChar);
    if (statePwr < 0) {
      Pwr[abs(statePwr)-1] = readChar;
    }
  }
  else if (readChar == 13) {

    PwrNum = atol(Pwr);
    TmprDouble = strtod(Tmpr,NULL);

    // Print to the serial port.
    Serial.flush();
    Serial.print("Pwr:");
    Serial.print(PwrNum);
    Serial.print("Tmpr:");
    Serial.println(TmprDouble);
    Serial.flush();

    // Write to the LCD screen.
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Power: ");
    lcd.print(PwrNum);
    lcd.print("W");
    lcd.setCursor(0, 1);
    lcd.print("Temp: ");
    lcd.print(TmprDouble);
    lcd.print("C");

    digitalWrite(ledPin, LOW);

    // Indicate that data is ready to be sent.
    ready = 1;
  }

  if (ready == 1) {
    ready = 0;

    digitalWrite(ledPin, HIGH);

    sprintf(stringData, " %lu, %lf", PwrNum, TmprDouble);
    content_length = strlen(stringData);

    Serial.flush();
    DEBUG_PRINTLN(stringData);
    Serial.flush();

    udpPayload.time = millis();
    udpPayload.temperature = TmprDouble;
    udpPayload.power = PwrNum;

    broadcastData();

    digitalWrite(ledPin, LOW);
  }
}

void broadcastData( void ) {
  uint8_t i=0;
  uint16_t ck;

  // Setup the MAC addresses for ethernet header
  while(i<6){
    buf[ETH_DST_MAC +i]=destmac[i]; // Broadcsat address
    buf[ETH_SRC_MAC +i]=mymac[i];
    i++;
  }
  buf[ETH_TYPE_H_P] = ETHTYPE_IP_H_V;
  buf[ETH_TYPE_L_P] = ETHTYPE_IP_L_V;
  es.ES_fill_buf_p(&buf[IP_P],9,iphdr);

  // IP Header
  buf[IP_TOTLEN_L_P]=28+sizeof(UDPPayload);
  buf[IP_PROTO_P]=IP_PROTO_UDP_V;
  i=0;
  while(i<4){
    buf[IP_DST_P+i]=broadcastip[i];
    buf[IP_SRC_P+i]=myip[i];
    i++;
  }
  es.ES_fill_ip_hdr_checksum(buf);
  buf[UDP_DST_PORT_H_P]=DEST_PORT_H;
  buf[UDP_DST_PORT_L_P]=DEST_PORT_L;
  buf[UDP_SRC_PORT_H_P]=10;
  buf[UDP_SRC_PORT_L_P]=srcport; // lower 8 bit of src port
  buf[UDP_LEN_H_P]=0;
  buf[UDP_LEN_L_P]=8+sizeof(UDPPayload); // fixed len
  // zero the checksum
  buf[UDP_CHECKSUM_H_P]=0;
  buf[UDP_CHECKSUM_L_P]=0;
  // Copy the data:
  i=0;
  // Most fields are zero, here we zero everything and fill later.
  uint8_t* b = (uint8_t*)&udpPayload;
  while(i< sizeof( UDPPayload ) ){
    buf[UDP_DATA_P+i]=*b++;
    i++;
  }
  // Create correct checksum.
  ck=es.ES_checksum(&buf[IP_SRC_P], 16 + sizeof( UDPPayload ),1);
  buf[UDP_CHECKSUM_H_P]=ck>>8;
  buf[UDP_CHECKSUM_L_P]=ck& 0xff;
  es.ES_enc28j60PacketSend(42 + sizeof( UDPPayload ), buf);
}

int parseDataTmpr(int oldstate, char chr) {
  newstate = oldstate;
  if (newstate > 0) {
    if (chr == startTmpr[sizeTmpr-newstate]) {
      newstate--;
    }
    else {
      newstate = sizeTmpr;
    }
  }
  else if (newstate <= 0) {
    newstate--;
    if (chr == endChar) {
      newstate = sizeTmpr;
    }
  }
  return newstate;
}

int parseDataPwr(int oldstate, char chr) {
  newstate = oldstate;
  if (newstate > 0) {
    if (chr == startPwr[sizePwr-newstate]) {
      newstate--;
    }
    else {
      newstate = sizePwr;
    }
  }
  else if (newstate <= 0) {
    newstate--;
    if (chr == endChar) {
      newstate = sizePwr;
    }
  }
  return newstate;
}

//*****************************************************************************
//
// Standard NUElectronics EtherShield Functions, No Changes Necessary
//
//*****************************************************************************

void init_ethershield() {
  // initialize enc28j60
  es.ES_enc28j60Init(mymac);
  es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
  delay(10);

  /* Magjack leds configuration, see enc28j60 datasheet, page 11 */
  // LEDA=greed LEDB=yellow
  // 0x880 is PHLCON LEDB=on, LEDA=on
  // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  // 0x990 is PHLCON LEDB=off, LEDA=off
  // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  // 0x880 is PHLCON LEDB=on, LEDA=on
  // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x880);
  delay(500);
  // 0x990 is PHLCON LEDB=off, LEDA=off
  // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
  es.ES_enc28j60PhyWrite(PHLCON,0x990);
  delay(500);
  // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
  // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
  es.ES_enc28j60PhyWrite(PHLCON,0x476);
  delay(100);

  // init the ethernet/ip layer:
  es.ES_init_ip_arp_udp_tcp(mymac,myip,80);
}