#include <ArduinoLowPower.h>

#define CHARBUFF 256
#define SITEID "BoSLnano"
#define DBG 0 //this causes instability we probably need to decrease the nrf baud rate
char response[CHARBUFF];
char request[CHARBUFF];

uint8_t uploads = 0;

//these character arrays should be null terminated.
uint8_t sendATcmd(char* cmd, const char* expAns, uint32_t timeout = 500, uint8_t rtries = 5){
	uint8_t gotAns = 0;
	uint8_t retries = rtries;
	bool match = false;
	
	while(gotAns < retries && (not match)){
		
		
		while( Serial1.available() > 0) {
			Serial.write(Serial1.read());    // Clean the input buffer
		}
		
		Serial.print(F("->"));
		Serial.print(cmd);
		
		Serial1.println(cmd);
		if(expAns == NULL){
			return true;
		}else{
			if(parseATcmd(&expAns, 1, timeout) >= 0){
				match = true;
			}
		}
		
		gotAns++;
	}
	
	
	return match;
}

int8_t waitATcmd(const char** expAns, uint8_t cases, uint32_t timeout){
	uint32_t tsend = millis();	
	uint16_t i = 0;
	bool match = false;
	uint32_t match_fl = 0;
	uint32_t respt;
	
	memset(response, '\0', CHARBUFF);
	
	do{
		
		if(Serial1.available()){    
			response[i] = Serial1.read();
			#if DBG
			Serial.print(response[i]);
			#endif
			i++;
			//now we handle if the buffer fills up
			//this solution will only work when the string we want to check is less than half of CHARBUFF
			if (i >= CHARBUFF){
				memcpy(&response[0], &response[CHARBUFF/2], CHARBUFF/2);
				memset(&response[CHARBUFF/2], '\0', CHARBUFF/2);
				i = CHARBUFF/2;
			}
			// check if the desired answer is in the response of the module
			for(int8_t j = 0; j < cases; j++){
				if (strstr(response, expAns[j]) != NULL)    
				{
					match_fl |=  (1ul << j);
				}
			}
			if (match_fl == ((1ul << (cases +1)) - 1)){
				match = true;
			}
		}    
		// Waits for the asnwer with time out
		respt = millis() - tsend;
	}while((not match) && (respt < timeout));
	
	if (match){
		Serial.print("\nMatchs: ");
		Serial.println(match_fl);
		Serial.print("Response Time: ");
		Serial.println(respt);
	}
	
	return match ? match_fl : -1;
}

int8_t parseATcmd(const char** expAns, uint8_t cases, uint32_t timeout){
	uint32_t tsend = millis();	
	uint16_t i = 0;
	bool match = false;
	uint8_t match_id = 0;
	uint32_t respt;
	
	memset(response, '\0', CHARBUFF);
	
	do{
		
		if(Serial1.available()){    
			response[i] = Serial1.read();
			#if DGB
			Serial.print(response[i]);
			#endif
			i++;
			//now we handle if the buffer fills up
			//this solution will only work when the string we want to check is less than half of CHARBUFF
			if (i >= CHARBUFF){
				noInterrupts();
				memcpy(&response[0], &response[CHARBUFF/2], CHARBUFF/2);
				memset(&response[CHARBUFF/2], '\0', CHARBUFF/2);
				i = CHARBUFF/2;
				interrupts();
			}
			// check if the desired answer is in the response of the module
			for(int8_t j = 0; j < cases; j++){
				if (strstr(response, expAns[j]) != NULL)    
				{
					match = true;
					match_id = j;
				}
			}
		}    
		// Waits for the asnwer with time out
		respt = millis() - tsend;
	}while((not match) && (respt < timeout));
	
	if (match){
		Serial.print("\nMatch: ");
		Serial.println(expAns[match_id]);
		Serial.print("Response Time: ");
		Serial.println(respt);
	}
	
	return match ? match_id : -1;
}

uint8_t urcWait(char* cmd, uint32_t timeout){
	bool match = false;
	uint8_t matchln = 0;
	uint32_t tstart=millis();
	Serial.print("Wait:");
	Serial.println(cmd);
	while((not match) && ((millis() - tstart) < timeout)){
		if(Serial1.available()){
			uint8_t bt = Serial1.read();
			if(bt == cmd[matchln]){
				matchln++;
			}else{
				matchln = 0;
			}
			#if DBG
			Serial.write(bt);
			#endif
		}
		if (cmd[matchln] == '\0'){match = true;}
	}
	Serial.println();
	return match;
}

uint8_t uploadATsequence(uint8_t ercd){
	uint8_t ercode = 1;
	
	ercode = sendATcmd("AT", "OK");
	if (ercode == 0){return 1;}
	
	ercode = sendATcmd("AT+CFUN=0", "OK",2000);
	if (ercode == 0){return 2;}
	
	ercode = sendATcmd("AT%XSYSTEMMODE=1,0,0,1", "OK");
	if (ercode == 0){return 3;}
	
	ercode = sendATcmd("AT+CFUN=1", "OK");
	if (ercode == 0){return 4;}
	
	//sendATcmd("AT+CMEE=1", "OK");
	//sendATcmd("AT+CNEC=24", "OK");
	//sendATcmd("AT+CGEREP=1", "OK");
	//sendATcmd("AT+CIND=1,1,1", "OK");
	ercode = sendATcmd("AT+CEREG=1", "OK");
	if (ercode == 0){return 5;}
	
	ercode = sendATcmd("AT+CEREG?", "+CEREG: 1,1",10,1);
	if(ercode == 0){
		ercode = urcWait("+CEREG: 1", 20000);
	}
	if (ercode == 0){return 6;}
	
	ercode = sendATcmd("AT+CPIN?", "+CPIN: READY");
	if (ercode == 0){return 7;}
	urcWait("OK", 500);
	
	
	ercode = sendATcmd("AT#XHTTPCCON?", NULL);
	if (ercode == 0){return 8;}
	
	static const char* c[] = {"#XHTTPCCON: 0","#XHTTPCCON: 1"};
	ercode = parseATcmd(c, 2, 500);
	urcWait("OK", 500);
	delay(100);
	switch(ercode){
	case 0:
		ercode = sendATcmd("AT#XHTTPCCON=1,\"www.bosl.com.au\",80", "#XHTTPCCON: 1", 30000);
		urcWait("OK", 500);
		if (ercode == 0){return 9;}
		break;
	case 1:
		break;
	default:
		return 10;
	}

	sprintf(request,"AT#XHTTPCREQ=\"GET\",\"/IoT/testing/scripts/WriteMe.php?SiteName="SITEID".csv&T=%d&B=%d\",\"\"", uploads,(ercd+1));

	ercode = sendATcmd(request, "#XHTTPCRSP:0,1",6000,1);
	if (ercode == 0){return 11;}
	
	ercode = sendATcmd("AT#XHTTPCCON=0,\"www.bosl.com.au\",80", "#XHTTPCCON: 0");
	if (ercode == 0){return 12;}
	urcWait("OK", 500);
	
	ercode = sendATcmd("AT+CFUN=0", "OK", 3000);
	if (ercode == 0){return 13;}
	
	return ercode;
	
}



uint8_t nRFwake(){
	pinMode(nRF_SW_EN, OUTPUT);
	digitalWrite(nRF_SW_EN, LOW);
	delay(10);
	digitalWrite(nRF_SW_EN, HIGH);
	
	uint8_t ercode = urcWait("Ready", 5000);
	return ercode;
}

uint8_t nRFsleep(){
	uint8_t ercode;
	ercode = sendATcmd("AT#XSLEEP=1", "\0", 10,1);
	//ercode = sendATcmd("AT", "OK", 20, 1);
	//if we get no response then the module should be off
	ercode = !ercode;
	return ercode;
}

uint8_t nRFrst(){
	pinMode(nRF_RST, OUTPUT);
	digitalWrite(nRF_RST, LOW);
	delay(10);
	digitalWrite(nRF_RST,HIGH);
	
	
	uint8_t ercode = urcWait("Ready", 5000);
	return ercode;
}

void setup(){
	// delay(6000);

	Serial.begin(115200);
	Serial1.begin(115200);
	Serial.println("Begin");
	Serial.flush();
	pinMode(nRF_EN, OUTPUT);
	digitalWrite(nRF_EN, HIGH);

	nRFrst();
	//nRFsleep();
}

void loop(){
    uint8_t errode = 0;
    
	uploads++;

	nRFwake();
	// nRFrst();
    errode = uploadATsequence(errode);
	if(errode){
		Serial.print("Upload Success: ");
		Serial.println(uploads);
	}else{
		// Serial.print("Upload Aborted: ");
		// Serial.println(errode);
		// while(1);
	}
    // delay(500);
	
    Serial.println(uploads);
	Serial.println("Sleeping");
	nRFsleep();
	Serial.flush();
	// LowPower.deepSleep(20000);
	delay(20000);
	Serial.println("Waking");

}

