#include <iot_full.h>

///////////////////////////////////////////////////////////////////////////////
// Http request control state machine. Determines which request needs to be 
// sent and response needs to be handled. State is changed after response is
// handled, except for the wait state.
// 
// States: 
//          INIT_TIME:  Makes get time request and uses initialize time response
//                      Next State: PROVISION 
//                      (First State)
//          GET_TIME:   Makes get time request and uses uptime update response
//                      Next State: UPDATE 
//          TEST_PROV:  Makes provision test request
//                      Next State: GET_TIME if success
//                                  ERROR if failure
//          PROVISION:  Makes provision request
//                      Next State: TEST_PROV if success
//                                  ERROR if failure
//          UPDATE:     Gets humidity and temperature from dht11 sensore
//                      Makes update request with temp, humid, and uptime
//                      Next State: WAIT
//          WAIT:       Waits MIN_WAIT_TIME seconds
//                      Next State: GET_TIME
//          ERROR:      Does nothing, system has errored
//                      Next State: NONE
///////////////////////////////////////////////////////////////////////////////
typedef enum {
   INIT_TIME,
   GET_TIME,
   TEST_PROV,
   PROVISION,
   UPDATE,
   WAIT,
   ERROR
} httpState;

//Current state for state machine
httpState current_state = INIT_TIME;

//Is there a request that's waiting for a response
int1 g_MyHttpSending = FALSE;

//Flag used to print out "waiting" to serial only once
int1 waiting = FALSE;

//Used to calculate amount of time passed without using blocking delays
int32 current_time, last_time;

//Temperature and humidity
unsigned int8 temp;
unsigned int8 humid;

//HTTP client strings
char hostname[HOSTNAME_MAX];
char response[RESPONSE_MAX];
char headers[HEADERS_MAX];
char url[URL_MAX];
char cgi[CGI_MAX];


////////////////////////////////////////////////////////////////////////////////////
//
// Used to initialize the http client with the allocated strings
// Data can then be written to the strings to modify the requests
//
////////////////////////////////////////////////////////////////////////////////////
void initHttpSettings() {
   HttpClientSetHostPort(80);
   HttpClientSetHostName(hostname);
   HttpClientSetUrl(url);
   HttpClientSetCustomHeaders(headers);
   HttpClientSetResponseBuffer(response, RESPONSE_MAX);
}

/* The CCS HTTP Client library is very flexible in the manners of which
data can be sent to the remote HTTP server an how to read response
data from the remote HTTP server. See the ccs_http_client.h file for
a full documentation of this API. */
void MyHttpSend(void) {
   //Send request half of state machine
   //Calls out to exosite library to setup request
   //Sets system to waiting for response
   //Calls http client to send request
   switch(current_state) {
      case INIT_TIME:
         exo_getTime();
         g_MyHttpSending = TRUE;
         HttpClientStart();
         break;
         
      case GET_TIME:
         exo_getTime();
         g_MyHttpSending = TRUE;
         HttpClientStart();
         break;
         
      case TEST_PROV: 
         exo_testProvision();
         g_MyHttpSending = TRUE;
         HttpClientStart();
         break;
         
      case PROVISION:
         exo_provision();
         g_MyHttpSending = TRUE;
         HttpClientStart();
         break;
         
      case UPDATE:
         dht11_read(&humid, &temp);
         exo_update(humid, temp);
         g_MyHttpSending = TRUE;
         HttpClientStart();
         break;
         
      case WAIT:
         if(waiting) { 
            current_time = TickGet();
            //if defined number of seconds has passed since wait was started
         //start another update
         if ((current_time - last_time) >= (MIN_WAIT_TIME * TICK_SECOND)){
               waiting = FALSE;
               current_state = GET_TIME;
            }
         } else {
            //Do only first time
         printf("\r\nwaiting");
            last_time = TickGet();
            waiting = TRUE;
         }
         break;
       
      case ERROR:
         break;
   }
}

void MyHttpHandleResonse(){
   //Response half of state machine
   //handles response based on current state
   //advances to next state based on response
   //sets system to not waiting for response
   switch(current_state) {
      case INIT_TIME:
         exo_respTimeInit();
         g_MyHttpSending = FALSE;
         current_state = PROVISION;
         break;
         
      case GET_TIME:
         exo_respTimeUpdate();
         g_MyHttpSending = FALSE;
         current_state = UPDATE;
         break;
         
      case TEST_PROV: 
         if(exo_respTestProv()) {
         current_state = GET_TIME;
         } else {
            current_state = ERROR;
         }
         g_MyHttpSending = FALSE;
         break;
         
      case PROVISION:
         if(exo_respProv()) {
            current_state = TEST_PROV;
         } else {
            current_state = ERROR;
         }
         g_MyHttpSending = FALSE;
         break;
         
      case UPDATE:
         exo_respUpdate();
         g_MyHttpSending = FALSE;
         current_state = WAIT;
         break;
      case WAIT:
         //no responses sent during wait
       //set to not waiting for response
       g_MyHttpSending = FALSE;
         break;
      case ERROR:
         //no responses sent during wait
       //set to not waiting for response
       g_MyHttpSending = FALSE;
         break;
   }
}

void StackPrintfChanges(void)
{
   static enum {PRINT_INIT=0, PRINT_NO_MAC, PRINT_NO_DHCP, PRINT_IDLE} state=0;

   switch(state)
   {
      case PRINT_INIT:
         printf("\n\rNo MAC Link: %X:%X:%X:%X:%X:%X", MY_MAC_BYTE1, MY_MAC_BYTE2, MY_MAC_BYTE3, MY_MAC_BYTE4, MY_MAC_BYTE5, MY_MAC_BYTE6);
         state = PRINT_NO_MAC;
         break;

      case PRINT_NO_MAC:
         if (MACIsLinked())
         {
           #if defined(STACK_USE_DHCP_CLIENT)
            if (!DHCPIsEnabled(0))
           #else
            if (0)
           #endif
            {
               printf("\n\rDHCP Disabled: %u.%u.%u.%u", MY_IP_BYTE1, MY_IP_BYTE2, MY_IP_BYTE3, MY_IP_BYTE4);
               state = PRINT_IDLE;
            }
            else
            {
               printf("\n\rDHCP Not Bound");
               state = PRINT_NO_DHCP;
            }
         }
         break;

     #if defined(STACK_USE_DHCP_CLIENT)
      case PRINT_NO_DHCP:
         if (!MACIsLinked())
         {
            state = PRINT_INIT;
            break;
         }
         if (DHCPIsBound(0))
         {
            state = PRINT_IDLE;
            printf("\n\rDHCP Bound: %u.%u.%u.%u", MY_IP_BYTE1, MY_IP_BYTE2, MY_IP_BYTE3, MY_IP_BYTE4);
         }
         break;
     #endif

      case PRINT_IDLE:
         if (
               !MACIsLinked()
              #if defined(STACK_USE_DHCP_CLIENT)
               || (DHCPIsEnabled(0) && !DHCPIsBound(0))
              #endif
            )
         {
            state = PRINT_INIT;
         }
         break;
   }
}

void IPAddressInit(void)
{
   AppConfig.Flags.bIsDHCPEnabled = TRUE;
   DHCPEnable(0); //enable nicWide stack support.

   //MAC address of this unit
   MY_MAC_BYTE1=MY_DEFAULT_MAC_BYTE1;
   MY_MAC_BYTE2=MY_DEFAULT_MAC_BYTE2;
   MY_MAC_BYTE3=MY_DEFAULT_MAC_BYTE3;
   MY_MAC_BYTE4=MY_DEFAULT_MAC_BYTE4;
   MY_MAC_BYTE5=MY_DEFAULT_MAC_BYTE5;
   MY_MAC_BYTE6=MY_DEFAULT_MAC_BYTE6;
}

void main()
{
   IPAddressInit();
   TickInit();
   enable_interrupts(GLOBAL);
   StackInit();
   
   //initialize http client strings
   initHttpSettings();
   //give exosite library pointers to http client strings
   //so it can setup requests
   exo_init(&hostname, &response, &headers, &url, &cgi);
   //initialize dht11 sensor
   dht11_init();
   
   while(TRUE)
   {

      // TCP/IP code
      StackTask();

      StackPrintfChanges();

      StackApplications();
      
      //send request if not currently sending and has IP address
      if ((!g_MyHttpSending) && DHCPIsBound(0)) 
      {
       MyHttpSend();
      }
      //handle response if currently sending request and client is finished sending
      else if (!HttpClientIsBusy() && g_MyHttpSending)
      {
        MyHttpHandleResonse();
      }      
   }
}
