#include "exosite_http.h"

//device provisioning variables
int8 exo_idNum = EXO_DEV_ID_NUM;
char exo_idVal[EXO_DEV_ID_FORMAT_LENGTH];
char exo_cik[EXO_PROV_CIK_LENGTH + 1];

//pointers to http client data strings
char* exo_hostname; 
char* exo_response;
char* exo_headers;
char* exo_url;
char* exo_cgi;

//uptime tracking variables
int32 startupTime = 0; //subtract from current time to get uptime
int32 uptime = 0;

///////////////////////////////////////////////////////////////////////////////
// exo_setHostname()
//
// Used to set the http client to send to your exosite product hostname
//
///////////////////////////////////////////////////////////////////////////////
void exo_setHostname() {
   sprintf(exo_hostname, "%s%s", EXO_PRODUCT_ID, EXO_HOSTNAME);
}

///////////////////////////////////////////////////////////////////////////////
// exo_loadCik()
//
// Used to load the 40 character authentication token into ram from eeprom. 
//
///////////////////////////////////////////////////////////////////////////////
void exo_loadCik() {
   int i;
   for(i = 0; i < EXO_PROV_CIK_LENGTH; i++) {
      exo_cik[i] = read_eeprom(i + EXO_EEPROM_PROV_CIK_ADDR);
   }
   exo_cik[EXO_PROV_CIK_LENGTH] = '\0';
}

///////////////////////////////////////////////////////////////////////////////
// exo_saveCik()
//
// Used to save the 40 character authentication token into eeprom from ram. 
//
///////////////////////////////////////////////////////////////////////////////
void exo_saveCik() {
   int i;
   for(i = 0; i < EXO_PROV_CIK_LENGTH; i++) {
       write_eeprom(i + EXO_EEPROM_PROV_CIK_ADDR, exo_cik[i]);
   }
   write_eeprom(EXO_EEPROM_iS_PROV_ADDR, TRUE);
}

///////////////////////////////////////////////////////////////////////////////
// exo_init()
//
// Used to initialize the exosite pointers to the wanted locations. All except
// the cgi pointer must have been initialized with the http client so that
// requests can be changed by modifying the string. The cgi initialize
// functions determine whether it is a GET or POST request so they are called
// in each request function. In each request setup function, it is assumed that
// each of these strings has been changed and must be reset.
//
// Parameters:
//             hostnamePtr -  pointer to hostname memory location. Must have
//                            been called with HttpClientSetHostName()
//
//             responsePtr -  pointer to http response memory location. Must have
//                            been called with HttpClientSetResponseBuffer()
//
//             headersPtr  -  pointer to http headers memory location. Must have
//                            been called with HttpClientSetCustomHeaders()
//
//             urlPtr      -  pointer to http response memory location. Must have
//                            been called with HttpClientSetUrl()
//
//             cgiPtr      -  pointer to http cgi data memory location. Doesn't
//                            need to have been called with HttpClientSetCGI...()
//
///////////////////////////////////////////////////////////////////////////////
void exo_init(char* hostnamePtr, char* responsePtr, char* headersPtr, char* urlPtr, char* cgiPtr) {
   exo_hostname = hostnamePtr;
   exo_response = responsePtr;
   exo_headers = headersPtr;
   exo_url = urlPtr;
   exo_cgi = cgiPtr;
}

///////////////////////////////////////////////////////////////////////////////
// exo_getTime()
//
// Used to setup a request to get a timestamp from exosite. Can be called 
// before device is provisioned
//
// Response functions: exo_respTimeInit(), exo_respTimeUpdate()
//
///////////////////////////////////////////////////////////////////////////////
void exo_getTime(){
   exo_setHostname();
   strcpy(exo_url, EXO_TIMESTAMP_URL); 
   exo_headers[0] = '\0';
   HttpClientSetCgiGet(NULL);
   HttpClientSetCgiPost(NULL);
}

///////////////////////////////////////////////////////////////////////////////
// exo_testProvision()
//
// Used to setup a request that tests the devices provisioning by requesting a
// list of available content. Should be called either when the device has 
// just been provisioned or if the eeprom memory reads that the device has
// already been provisioned.
// 
// Response function: exo_respTestProv()
//
///////////////////////////////////////////////////////////////////////////////
void exo_testProvision(){
   exo_setHostname();
   strcpy(exo_url, EXO_PROV_TEST_URL); 
   sprintf(exo_headers, "%s%s", EXO_CIK_HEADER, exo_cik);
   HttpClientSetCgiGet(NULL);
   HttpClientSetCgiPost(NULL);
}

///////////////////////////////////////////////////////////////////////////////
// exo_provision()
//
// Used to setup a request to provision the device using its device id.
//
// Response function: exo_respProv()
//
///////////////////////////////////////////////////////////////////////////////
void exo_provision(){
   exo_setHostname();
   strcpy(exo_url, EXO_PROV_URL); 
   strcpy(exo_headers, EXO_CONT_TYPE_HEADER);
   sprintf(exo_idVal, EXO_DEV_ID_FORMAT, exo_idNum); //set CGI for ID value
   sprintf(exo_cgi, "%s=%s", EXO_DEV_ID_KEY, exo_idVal);
   HttpClientSetCgiGet(NULL);
   HttpClientSetCgiPost(exo_cgi);
}

///////////////////////////////////////////////////////////////////////////////
// exo_update()
//
// Used to setup a request to update the temperature, humidity and uptime
// resources for this device on exosite. Must be provisioned before calling.
//
// Response function: exo_respUpdate()
//
// Parameters:
//             humid: unsigned byte representing humidity in %
//
//             temp: unsigned byte representing temperature in Celsius
//
///////////////////////////////////////////////////////////////////////////////
void exo_update(unsigned int8 humid, unsigned int8 temp){
   exo_setHostname();
   strcpy(exo_url, EXO_UPDATE_URL); 
   sprintf(exo_headers, "%s%s\r\n%s", EXO_CIK_HEADER, exo_cik, EXO_CONT_TYPE_HEADER);
   sprintf(exo_cgi, "%s=%u&%s=%u&%s=%Lu", EXO_TEMPERATURE_ALIAS, temp, 
                                          EXO_HUMIDITY_ALIAS, humid,
                                          EXO_UPTIME_ALIAS, uptime);
   HttpClientSetCgiGet(NULL);
   HttpClientSetCgiPost(exo_cgi);
}

///////////////////////////////////////////////////////////////////////////////
// exo_respInitTime()
//
// Used to handle the response from a get time request. Initializes the time
// for when the device startsup. Must be called once in order to determine 
// uptime on future get time requests.
//
// Request function: exo_getTime()
//
///////////////////////////////////////////////////////////////////////////////
void exo_respTimeInit() {
   if(!HttpClientGetResult()) {
      startupTime = atoi32(exo_response);
      printf("\r\nStart time initialized: %Lu", startupTime);
   } else {
      printf("\r\nError in Getting Current Time: %Lu", HttpClientGetResult());
   }
}

///////////////////////////////////////////////////////////////////////////////
// exo_respTimeUpdate()
//
// Used to handle the response from a get time request. Calculates the current
// uptime by subtracting the current time from the startup time. 
// respTimeInit() must have been called once before calling this.
//
// Request function: exo_getTime()
//
///////////////////////////////////////////////////////////////////////////////
void exo_respTimeUpdate() {
   if(!HttpClientGetResult()) {
      uptime = atoi32(exo_response) - startupTime;
      printf("\r\nUptime updated: %Lu", uptime);
   } else {
      printf("\r\nError in getting current time: %Lu", HttpClientGetResult());
   }
}


///////////////////////////////////////////////////////////////////////////////
// exo_respTestProv()
//
// Used to handle the response from a provision test request. The actual data
// returned by the request is ignored. The response code determines whether or
// not the test was successful.
// 
// Response Codes:
//                200 or 204  - Test was successful
//                401         - Test failed due to incrorrect authentication
//                Other       - Test failed for unknown reason
//
// Request function: exo_testProvision()
//
///////////////////////////////////////////////////////////////////////////////
int1 exo_respTestProv() {
   if(HttpClientGetResult() == 0 || HttpClientGetResult() == 204) {
      printf("\r\nProvision test success");
      return TRUE;
   } else if(HttpClientGetResult() == 401) {
      printf("\r\nDevice cik rejected");
      return FALSE;
   } else {
      printf("\r\nErro in provision test: %Lu", HttpClientGetResult());
      return FALSE;
   }
}

///////////////////////////////////////////////////////////////////////////////
// exo_respProv()
//
// Used to handle the response from a provision request. A successful request
// will return the devices authentication token in the response data.
// 
// Response Codes:
//                200   -  Provision was successful. Authentication token
//                         is copied into ram and stored to eeprom.
//                409   -  Provision failed due to a device ID conflict. If
//                         eeprom reads that there is an existing token stored,
//                         it is loaded and tested. Otherwise the device is 
//                         not provisioned and cannot update to exosite.
//                404   -  Provision failed due to device not being whitelisted.
//                         User must whitelist the set device ID in their
//                         exosite product.
//                Other -  Provision failed for unknown reason.
//
// Request function: exo_provision()
//
///////////////////////////////////////////////////////////////////////////////
int1 exo_respProv() {
   if(!HttpClientGetResult()) {
      printf("\r\nDevice provision success");
      memcpy(exo_cik, exo_response, EXO_PROV_CIK_LENGTH);
      exo_cik[EXO_PROV_CIK_LENGTH] = '\0';
      exo_saveCik();
      return TRUE;
   } else if(HttpClientGetResult() == 409){
      if(read_eeprom(EXO_EEPROM_iS_PROV_ADDR)) {
         printf("\r\nDevice already exists and is provisioned");
         exo_loadCik();
      } else {
         printf("\r\nDevice ID conflict");
      }
      return TRUE;
   }else if(HttpClientGetResult() == 404){
      printf("\r\nDevice ID not whitelisted");
      write_eeprom(EXO_EEPROM_iS_PROV_ADDR, FALSE);
      return FALSE;
   } else {
      printf("\r\nError in provisioning: %Lu", HttpClientGetResult());
      write_eeprom(EXO_EEPROM_iS_PROV_ADDR, FALSE);
      return FALSE;
   }
}

///////////////////////////////////////////////////////////////////////////////
// exo_respUpdate()
//
// Used to handle the response from a update request. Verifies that the update
// was successful.
// 
// Request function: exo_update()
//
///////////////////////////////////////////////////////////////////////////////
void exo_respUpdate() {
   if(!HttpClientGetResult() == 0 || HttpClientGetResult() == 204) {
      printf("\r\nUpdate successful");
   } else {
      printf("\r\nError in updating: %Lu", HttpClientGetResult());
   }
}
