#include "protocol.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

unsigned long startTime, stopTime, usedTime;
char trattamento[10] = { 0,0,0,0,0,0,0,0,0,0 };


#define UARTBUFFSIZE    128
struct _UartBuffer {
	uint8_t buffer[UARTBUFFSIZE];
	uint16_t length;
	};

typedef struct _UartBuffer UartBuffer;

UartBuffer rxBuffer, txBuffer;

extern int getRemainingTime(void);
extern int sendConsumedTime(uint32_t timeConsumed, const char* treatment);
extern int checkNodeRedConnection(void);
extern int getWifiConn(void);
extern void setWiFi();

extern HardwareSerial comSerial;

void UartBufferInit();
extern uint8_t RXData;
extern bool isEnabled;
extern int timeRem;

void PCmd_TIME(UartBuffer* _response);
void PCmd_STOP(UartBuffer* _response);
void PCmd_STOPNGO(UartBuffer* _response);
void PCmd_WMNGR(UartBuffer* _response);
void PCmd_START(UartBuffer* response, char* _command);
void PCmd_CONS(UartBuffer* response, char* _command);
void PCmd_CONN(UartBuffer* _response);
void PCmd_sendACKCNN();
void PCmd_sendACK();

void PCmdClear(UartBuffer* command) {
	memset(command, 0, sizeof(UartBuffer));
	}

uint8_t PCmdCheckSumOk(UartBuffer command) {
	uint8_t csum = 0;
	uint16_t i = 1;
	for (i = 1; i < command.length - 2; i++) {
		csum = (csum + command.buffer[i]) % 256;// prima era 0xFF;
		}
	if ((csum == 0) || (csum == 2) || (csum == 3))
		csum += 10;
	return (command.buffer[command.length - 2] == csum);
	}

uint8_t PCmdIsValid(UartBuffer command) {

	if ((command.length > 0) && (command.buffer[0] == 2) && (command.buffer[command.length - 1] == 3)) {
		return PCmdCheckSumOk(command);
		}
	return 0;
	}

void PCmdFillEnd(UartBuffer* command) {
	uint8_t len = 1;
	uint8_t csum = 0;
	uint8_t* p = &command->buffer[1];
	while (*p) {
		csum = (csum + *p) % 256;//prima era 0xFF;
		len++;
		p++;
		}
	if ((csum == 0) || (csum == 2) || (csum == 3))
		csum += 10;
	*p = csum;
	p++;
	*p = 0x03;
	command->length = len + 2;
	}

void PCmdFillNack(UartBuffer* command) {
	PCmdClear(command);

	command->buffer[0] = 0x02;
	command->buffer[1] = 0x15;
	command->buffer[2] = 0x15;
	command->buffer[3] = 0x03;

	command->length = 4;
	}

void PCmdFillAck(UartBuffer* command) {
	PCmdClear(command);

	command->buffer[0] = 0x02;
	command->buffer[1] = 0x06;
	command->buffer[2] = 0x06;
	command->buffer[3] = 0x03;

	command->length = 4;
	}

uint8_t PCmdReqResp(UartBuffer request, UartBuffer* response) {
	if (PCmdIsValid(request)) {

		// char* s_cmd = ext_string(&request.buffer[1], request.length-3);

		char* s_cmd = (char*)malloc(request.length - 3 + 1);

		memset(s_cmd, 0, request.length - 3 + 1);
		memcpy(s_cmd, &request.buffer[2], request.length - 4); //in origine era [1] e [-4]

		if (strncmp(s_cmd, "AL", 2) == 0) {      // ## alive
			//PCmdFillAck(response);
			PCmd_sendACK();
			}
		else
			if (strncmp(s_cmd, "TIME", 4) == 0) {     // 
				PCmd_TIME(response);
				}
			else
				if (strncmp(s_cmd, "STOP", 4) == 0) {     //   
					PCmd_STOP(response);
					}
				else
					if (strncmp(s_cmd, "START", 5) == 0) {     //   
						PCmd_START(response, s_cmd);
						}
					else
						if (strncmp(s_cmd, "CONS", 4) == 0) {     //   
							PCmd_CONS(response, s_cmd);
							}
						else
							if (strncmp(s_cmd, "STONGO", 6) == 0) {     //   STOP'N'GO
								PCmd_STOPNGO(response);
								}
							else
								if (strncmp(s_cmd, "WMNGR", 5) == 0) {     //   
									PCmd_WMNGR(response);
									}
								else
									if (strncmp(s_cmd, "CONN", 4) == 0) {     //
										PCmd_CONN(response);
										}
									else {
										PCmdFillNack(response);
										}

		free(s_cmd);
		}
	else {
		PCmdFillNack(response);
		}
	return 0;

	}

void UartTransmit(UartBuffer command) {
	comSerial.write(command.buffer, command.length);
	/*  for (int j =0;j<command.length; j++){
		Serial.println(command.buffer[j],HEX);
	  }
	  */
	}

uint8_t UartRxBufferEmpty() {
	return (rxBuffer.length == 0);
	}
void UartRxBufferClear() {
	memset(&rxBuffer, 0, sizeof(UartBuffer));
	}
void UartTxBufferClear() {
	memset(&txBuffer, 0, sizeof(UartBuffer));
	}

void UartBufferInit() {
	UartRxBufferClear();
	UartTxBufferClear();
	}

void UartRxBufferDecode() {

	UartTxBufferClear();
	if (!UartRxBufferEmpty()) {
		PCmdReqResp(rxBuffer, &txBuffer);
		UartTransmit(txBuffer);
		}
	UartRxBufferClear();
	}

uint8_t IsSTX(uint8_t data) {
	return (data == 0x02);
	}

uint8_t IsETX(uint8_t data) {
	return (data == 0x03);
	}

uint8_t LastByteIsETXinRxBuffer() {
	return (IsETX(rxBuffer.buffer[rxBuffer.length - 1]));
	}


void checkComData() {

	if (IsSTX(RXData)) {
		rxBuffer.length = 0;
		}
	rxBuffer.buffer[rxBuffer.length++] = RXData;
	if (LastByteIsETXinRxBuffer()) {
		UartRxBufferDecode();
		}

	}


void PCmd_STOP(UartBuffer* _response) {
	stopTime = millis();
	Serial.println("STOP: ");
	usedTime = (stopTime - startTime) / 1000;
	Serial.println(usedTime);
	if (sendConsumedTime(usedTime, trattamento) == 0) {
		PCmd_sendACKCNN();
		}

	//PCmdClear(command);
//  PCmdFillAck(_response);
	}

void PCmd_STOPNGO(UartBuffer* _response) {
	stopTime = millis();
	Serial.println("STOP'N'GO: ");
	usedTime = (stopTime - startTime) / 1000;
	Serial.println(usedTime);
	startTime = millis();
	if (sendConsumedTime(usedTime, trattamento) == 0) {
		PCmd_sendACKCNN();
		}


	//PCmdClear(command);
//  PCmdFillAck(_response);
	}



void PCmd_TIME(UartBuffer* _response) {
	_response = &txBuffer;
	int j;
	//  int time=0;
	if (getRemainingTime() != 0) {
		timeRem = 0;
		}
	//  Serial.print("time: ");
	//  Serial.println(timeRem);

	UartTxBufferClear();
	PCmdClear(_response);
	_response->buffer[0] = 0x02;
	j = sprintf((char*)&(_response->buffer[2]), "TME:%lu:%d/", timeRem, isEnabled);
	_response->buffer[1] = j; //dimensione del solo payload
	PCmdFillEnd(_response);
	//UartTransmit(txBuffer);
	}



void PCmd_START(UartBuffer* response, char* _command) {

	startTime = millis();
	Serial.println("START");

	//  uint8_t result  = 1;
	char* token;

	char delimiters[3] = "/:";
	// get the first token
	token = strtok(_command, delimiters);

	// walk through other tokens
	while (token != NULL) {
		if (strcmp(token, "START") == 0) {

			token = strtok(NULL, delimiters);
			strcpy((char*)trattamento, token);
			//trattamento=atoi(token);
			}
		token = strtok(NULL, delimiters);
		}
	Serial.print("trattamento: ");
	Serial.println(trattamento);
	/*
	  if (result){
		  PCmdFillAck(response);
	  }
	  else
		  PCmdFillNack(response);
		  */
	}

void PCmd_CONS(UartBuffer* response, char* _command) {

	//  startTime=millis();

	//  uint8_t result  = 1;
	char* token;

	char delimiters[3] = "/:";
	// get the first token
	token = strtok(_command, delimiters);

	// walk through other tokens
	while (token != NULL) {
		if (strcmp(token, "CONS") == 0) {

			token = strtok(NULL, delimiters);
			usedTime = atol(token);

			token = strtok(NULL, delimiters);
			strcpy((char*)trattamento, token);

			}
		token = strtok(NULL, delimiters);
		}

	Serial.print("CONS-> ");
	Serial.print(" Tempo consumato: ");
	Serial.print(usedTime);
	Serial.print(" Trattamento: ");
	Serial.println(trattamento);

	if (sendConsumedTime(usedTime, trattamento) == 0) {
		PCmd_sendACKCNN();
		}

	}



void PCmd_CONN(UartBuffer* _response) {
	/*  extern const char* username;
	  extern const char* devicePassword;
	  extern const char* serverURL_getRemaining;
	  */
	_response = &txBuffer;
	int connection = 0;
	int j;

	UartTxBufferClear();
	PCmdClear(_response);
	_response->buffer[0] = 0x02;
	if (getWifiConn() == 0) {
		connection = 1;
		if (checkNodeRedConnection()) {
			connection = 2;
			}
		}
	j = sprintf((char*)&(_response->buffer[2]), "CNN:%d/", connection);
	_response->buffer[1] = j; //dimensione del solo payload
	PCmdFillEnd(_response);
	//UartTransmit(txBuffer);

	}

void PCmd_WMNGR(UartBuffer* _response) {

	Serial.println("WiFi MANAGER");
	setWiFi();
	}

void PCmd_refreshCONN(int connection) {
	/*  extern const char* username;
	  extern const char* devicePassword;
	  extern const char* serverURL_getRemaining;
	  */
	UartBuffer* _response = &txBuffer;
	int j;

	UartTxBufferClear();
	PCmdClear(_response);
	_response->buffer[0] = 0x02;
	j = sprintf((char*)&(_response->buffer[2]), "CNN:%d/", connection);
	_response->buffer[1] = j; //dimensione del solo payload
	PCmdFillEnd(_response);
	UartTransmit(txBuffer);

	}

void PCmd_sendACKCNN() {
	/*  extern const char* username;
	  extern const char* devicePassword;
	  extern const char* serverURL_getRemaining;
	  */
	UartBuffer* _response = &txBuffer;
	int j;

	UartTxBufferClear();
	PCmdClear(_response);
	_response->buffer[0] = 0x02;
	j = sprintf((char*)&(_response->buffer[2]), "ACKCNN/");
	_response->buffer[1] = j; //dimensione del solo payload
	PCmdFillEnd(_response);
	UartTransmit(txBuffer);

	}
void PCmd_sendACK() {
	/*  extern const char* username;
	  extern const char* devicePassword;
	  extern const char* serverURL_getRemaining;
	  */
	UartBuffer* _response = &txBuffer;
	int j;

	UartTxBufferClear();
	PCmdClear(_response);
	_response->buffer[0] = 0x02;
	j = sprintf((char*)&(_response->buffer[2]), "ACK/");
	_response->buffer[1] = j; //dimensione del solo payload
	PCmdFillEnd(_response);
	UartTransmit(txBuffer);

	}
