/*******************************************************************************
    TCP6 Demo Application Source File

    Company:
    Microchip Technology Inc.

    File Name:
    tcp6_demo_app.c

    Summary:
    Contains definition of "tcp6_demo_app_xxx() APIs"
    Implements a custom HTTP client-based application.

    Description:

*******************************************************************************/

// DOM-IGNORE-BEGIN
/*******************************************************************************
Copyright (c) 2013 released Microchip Technology Inc.  All rights reserved.

Microchip licenses to you the right to use, modify, copy and distribute
Software only when embedded on a Microchip microcontroller or digital signal
controller that is integrated into your product or third party product
(pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for
additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER
CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR
OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
*******************************************************************************/
// DOM-IGNORE-END


// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************
#include <p32xxxx.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <ctype.h>
#include <plib.h>
#include "system/system_services.h"
#include <system/system_userio.h>
#include "tcpip/tcpip.h"
#include "tcp6_demo_app.h"

// enable all console messages to be dumped to UART
//#define TCP6_DEMO_APP_VERBOSE

// *****************************************************************************
// *****************************************************************************
// Section: File Scope or Global Variables
// *****************************************************************************
// *****************************************************************************

// generic output string buffer
char  OutString[512];
char  WeatherString[80] = "---\0";

// build info
const char Version[]        = "0.1";
const char CompileDate[]    =__DATE__;
const char CompileTime[]    =__TIME__;

// handle to current TCPIP interface
TCPIP_NET_HANDLE myNetHandle;
uint8_t  bWiFiConnected = 0;
// DNS resolution variables
DNS_RESULT dnsRes;                      // holds return value of DNSx APIs
uint32_t nAttempts =0;                  // counter for number of DNS attempts before quitting

// TCP socket variables
IPV4_ADDR  serverIP;                    // used to hold server's IP address
uint16_t serverPort;                    // used to hold the server's port number for the current scheme
TCP_SOCKET mySocket = INVALID_SOCKET;   // socket handle
TCP_SOCKET_INFO mySocketInfo;           // information on current socket (for debugging)
SYS_TICK clientTimer;                   // used to track wait delay for opening a client socket

// TCP & HTTP Communications Variables
uint8_t TCPReadByte;                    // used to hold latest byte read from socket
char TCPInBuffer[4096];                 // used to hold reveived TCP data from server
char TCPOutBuffer[512];                 // used to hold TCP send data line
int TCPInBufferIndex;                   // index to current location in TCPInBuffer
SYS_TICK HTTPReplyDelay;                // track elapsed time between http tx and rx
unsigned int HTTPReplyLineCnt;          // used to indicate the number of HTTP response lines received
uint16_t TCPIsPutReadyCnt;
int httpTransactionIndex = 0;           // used to identify which http transaction the state machine is working with
HTTP_TRANSACTION_DYNAMIC httpTransaction;   // holds data for current http transaction


// control processing variables
unsigned char *TCPInBufPtr;            // points to "<ledtoggle>" field in XML reply body
unsigned char temp[80];
volatile unsigned int toggleLed;        // used to store the current toggle LED value
int i, j;

// base64 temporary buffers
char mysrc[sizeof(httpTransaction.username)+sizeof(httpTransaction.password)];
char myb64[sizeof(httpTransaction.username)+sizeof(httpTransaction.password)];

// Default HTTP transaction list executed by the client
// ref: TCP6 Web API v016
// **EDIT THIS LIST FOR YOUR SPECIFIC TRANSACTIONS
const HTTP_TRANSACTION_STATIC HTTP_TRANSACTION_LIST[] =
{
    {
        // TCP6 Web API
        // russianmasters-pve.rhcloud.com/html/api
        "14",                                        // id# (from Web API spec)
        "GET",                                       // method
        "http",                                      // scheme
        "russianmasters-pve.rhcloud.com",            // host (DNS name or IP address)
        "80",                                        // port
        "Введите имя ресурса",                       // Например: "/xml/vit/class/board00/"
        "HTTP/1.1",                                  // version
        "Введите имя пользователя",                  // http basic auth username (NULL if none required)
        "Введите пароль",                            // http basic auth password (NULL if none required)
        {
           "User-Agent: MicrochipHTTPClient/0.1",    // up to 8 additional headers supported per transaction ("Host: xxxx" header automatically sent)
           "Connection: close",
           "\0",
           "\0",                                     // unused header slots *MUST* be NULL terminated!
           "\0",
           "\0",
           "\0",
           "\0",
        },
        0,                                           // requires a body?
        0,                                           // dispatch delay (seconds)
    },

    {
        // TCP6 Web API
        // russianmasters-pve.rhcloud.com/html/api
        "15",                                         // id# (from Web API spec)
        "PUT",                                        // method
        "http",                                       // scheme
        "russianmasters-pve.rhcloud.com",             // host (DNS name or IP address)
        "80",                                         // port
        "Введите имя ресурса",                       // Например: "/xml/vit/class/board00/"
        "HTTP/1.1",                                  // version
        "Введите имя пользователя",                  // http basic auth username (NULL if none required)
        "Введите пароль",                            // http basic auth password (NULL if none required)
        {
            "User-Agent: MicrochipHTTPClient/0.1",    // up to 8 additional headers supported per transaction ("Host: xxxx" header automatically sent)
            "Content-Type: text/xml",
            "Connection: close",
            "\0",                                      // unused header slots *MUST* be NULL terminated!
            "\0",
            "\0",
            "\0",
            "\0",
        },
        1,                                             // requires a body?
        0,                                             // dispatch delay (seconds)
    },

    {
        // Yahoo Weather API
        // Retrieve Toronto's Temperature in Degrees Celcius
        // http://developer.yahoo.com/weather/
        "20",                                           // id
        "GET",                                          // method
        "http",                                         // scheme
        "weather.yahooapis.com",                        // host
        "80",                                           // port
        "/forecastrss?w=2123260&u=c",                      // path
        "HTTP/1.1",                                     // version
        "\0",                                           // http basic auth username (NULL if none required)
        "\0",                                           // http basic auth password (NULL if none required)
        {
            "User-Agent: MicrochipHTTPClient/0.1",      // up to 8 additional headers supported per transaction ("Host: xxxx" header automatically sent)
            "Accept: text/xml",
            "Connection: close",
            "\0",
            "\0",                                       // unused header slots *MUST* be NULL terminated!
            "\0",
            "\0",
            "\0",
        },
        0,                                             // requires a body?
        30,                                            // dispatch delay (seconds)
    },
    // Android notification POST transaction
//    {
//        "25",                                           // id
//        "POST",                                          // method
//        "http",                                         // scheme
//        "russianmasters-pve.rhcloud.com",                        // host
//        "80",                                           // port
//        "/send.php",                      // path
//        "HTTP/1.1",                                     // version
//        "\0",                                           // http basic auth username (NULL if none required)
//        "\0",                                           // http basic auth password (NULL if none required)
//        {
//            "User-Agent: MicrochipHTTPClient/0.1",      // up to 8 additional headers supported per transaction ("Host: xxxx" header automatically sent)
//            "Connection: close",
//            "\0",
//            "\0",                                       // unused header slots *MUST* be NULL terminated!
//            "\0",
//            "\0",
//            "\0",
//            "\0",
//        },
//        1,                                             // requires a body?
//        0,                                            // dispatch delay (seconds)
//    },
};

// tcp6 demo app state machine state "SM_HTTPC_TRANSACTION_DISPATCH"
SYS_TICK WaitDispatch2Timer = 0;
SYS_TICK WaitAssociate = 0;

// tcp6_demo_app local resource data structure
// represents resource "/username/collection/board"
TCP6_WEBAPI_RESOURCE_BOARD myResourceState;   // variable holds the current local copy of the resource state

// tcp6_demo_app state machine variables
TCP6_DEMO_APP_SM smMyDemoAppTasks = SM_IDLE;      // main state machine variable

// various
uint8_t *mac_addr_ptr;                  // pointer to mac address array (uint8_t array[6])
uint8_t mac_addr[6];                    // used to hold the mac address of the current interface
char mac_addr_string[16];               // used to hold MAC address string representation

// *****************************************************************************
// *****************************************************************************
// Section: File Scope or Global Constants
// *****************************************************************************
// *****************************************************************************


// *****************************************************************************
// *****************************************************************************
// Section: File Scope Data Types
// *****************************************************************************
// *****************************************************************************


// *****************************************************************************
// *****************************************************************************
// Section: File Scope Helper Macros
// *****************************************************************************
// *****************************************************************************

#define clrscrConsole() SYS_CONSOLE_MESSAGE("\x1b[2J")
#define homeConsole()   SYS_CONSOLE_MESSAGE("\x1b[H")
#define pcrConsole()    SYS_CONSOLE_MESSAGE('\r')

// *****************************************************************************
/*  Function:
        void tcp6_demo_app_tasks(TCPIP_NET_HANDLE netH);

    Summary:
        Main application state machine

    Description:
        This section is for a description of the function.  It should be in
        complete sentences describing the meaning and usage of the function.

    Remarks:
        The "remarks" section is always optional.  Use it to provide additional
        information such as restrictions, special cases, and related information.
*/

void tcp6_demo_app_tasks(TCPIP_NET_HANDLE netH)
{
     myNetHandle = netH;     // update tcpip interface handle

    switch(smMyDemoAppTasks)
    {
        case SM_IDLE:

            // nothing to do here, so start the initialization
            smMyDemoAppTasks = SM_INIT;
            break;

        case SM_INIT:

            // initialize the peripherals, variables, httpc and the resource state
            tcp6_demo_app_init();
            smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
            // initiallize http transaction dispatch timers
            WaitDispatch2Timer = SYS_TICK_Get();
            break;

        case SM_HTTPC_TRANSACTION_DISPATCH:

            // point to the next http transaction for the state machine
            // algorithm: simple round-robin
            // http transactions requiring a delay will be delayed based on application needs
            httpTransactionIndex++;
            if(httpTransactionIndex == (sizeof (HTTP_TRANSACTION_LIST) / sizeof (*HTTP_TRANSACTION_LIST)))
            {
                httpTransactionIndex = 0;
            }
            switch(httpTransactionIndex)
            {
                case 0 ... 1:
                    // no delay for these transactions - schedule immediately
                    smMyDemoAppTasks = SM_RESOURCE_UPDATE;
                    break;

                case 2:
                    // schedule transaction 2 according to its dispatch_delay
                    if((SYS_TICK_Get() - WaitDispatch2Timer) > (SYS_TICK_TicksPerSecondGet())*(HTTP_TRANSACTION_LIST[httpTransactionIndex].dispatch_delay))
                    {
                        WaitDispatch2Timer = SYS_TICK_Get();    // setup for next time delay
                        smMyDemoAppTasks = SM_RESOURCE_UPDATE;  // go to next state
                    }
                    else
                    {
                        // evaluate/schedule next transaction in the list
                        smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    }
                    break;

                case 3:
                    // no delay for these transactions - schedule immediately
                    if (BUTTON0_IO == 0) {
                        smMyDemoAppTasks = SM_RESOURCE_UPDATE;
                    } else  {
                        // evaluate/schedule next transaction in the list
                        smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    }
                    break;

                default:
                    break;
            }

            break;

        case SM_RESOURCE_UPDATE:

            // update resource data structure for the current http transaction
            switch(httpTransactionIndex)
            {
                
                case 0 ... 1:
                    strcpy(myResourceState.collection, "Введите имя коллекции");
                    strcpy(myResourceState.board, "Введите имя платы");
                    strcpy(myResourceState.username, "Введите имя пользователя");
                    strcpy(myResourceState.password, "Введите пароль");
                    strcpy(myResourceState.pot,WeatherString);
                    strcpy(myResourceState.macaddr, mac_addr_string);
                    myResourceState.leds = LED_GET();
                    myResourceState.buttons = (BUTTON0_IO); 
                    myResourceState.ledtoggle = 0;
                    myResourceState.status = 0;
                   
                    // transition to next state (SM_HTTPC_TRANSACTION_CONFIG)
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_CONFIG;
                    break;

                case 2:
                   // transition to next state (SM_HTTPC_TRANSACTION_CONFIG)
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_CONFIG;
                    break;
                case 3:
                    strcpy(myResourceState.board, "Введите имя платы");
                    strcpy(myResourceState.username, "Введите идентификатор приложения");
                    strcpy(myResourceState.pot, "Hi. Masters 2013");
                    // transition to next state (SM_HTTPC_TRANSACTION_CONFIG)
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_CONFIG;
                    break;
                default:
                    break;

            } // switch(httpTransactionIndex)
            break;

        case SM_HTTPC_TRANSACTION_CONFIG:

            // fill in the dynamic http transaction buffer with the current parameters

            // clear the http transaction buffer
            // memset(httpTransaction, '\0', sizeof(httpTransaction));
            // set the id
            strcpy(httpTransaction.id, HTTP_TRANSACTION_LIST[httpTransactionIndex].id);
            // set the request line
            strcpy(httpTransaction.method, HTTP_TRANSACTION_LIST[httpTransactionIndex].method);
            strcpy(httpTransaction.scheme, HTTP_TRANSACTION_LIST[httpTransactionIndex].scheme);
            strcpy(httpTransaction.host, HTTP_TRANSACTION_LIST[httpTransactionIndex].host);
            strcpy(httpTransaction.port, HTTP_TRANSACTION_LIST[httpTransactionIndex].port);
            strcpy(httpTransaction.path, HTTP_TRANSACTION_LIST[httpTransactionIndex].path);
            strcpy(httpTransaction.version, HTTP_TRANSACTION_LIST[httpTransactionIndex].version);
            strcpy(httpTransaction.username, HTTP_TRANSACTION_LIST[httpTransactionIndex].username);
            strcpy(httpTransaction.password, HTTP_TRANSACTION_LIST[httpTransactionIndex].password);
            // set the static header lines
            i = 0; // index to const char* header[] strings
            j = 0; // index to headers[][] array
            while(*HTTP_TRANSACTION_LIST[httpTransactionIndex].headers[i] != '\0')
            {
                strcpy(httpTransaction.headers[j], HTTP_TRANSACTION_LIST[httpTransactionIndex].headers[i]);
                j++; // point next location in headers[]
                i++; // point to next const char* header string
            }
            // for transactions requiring a body, prepare a formatted body and output a "Content-Length:" header
            if(HTTP_TRANSACTION_LIST[httpTransactionIndex].entity_body)
            {
                switch(httpTransactionIndex)
                {
                    // TCP6 WEB API PUT transaction requires XML-formatted body
                    case 1:
                        sprintf(httpTransaction.entity_body, "<?xml version=\"1.0\"?><record><username>%s</username><collection>%s</collection><board>%s</board><macaddr>%s</macaddr><pot>%s</pot><leds>%d</leds><buttons>%d</buttons></record>",
                        myResourceState.username, myResourceState.collection, myResourceState.board, myResourceState.macaddr,
                        myResourceState.pot , myResourceState.leds, myResourceState.buttons);
                        sprintf(httpTransaction.headers[j], "Content-Length: %d", (unsigned  int ) strlen(httpTransaction.entity_body));
                        j++;
                        break;

                    case 3:
                        sprintf(httpTransaction.entity_body, "{ \"androidId\":\"%s\",  \"message\":\"%s\" , \"board\":\"%s\" }",
                        myResourceState.username, myResourceState.pot, myResourceState.board);
                        sprintf(httpTransaction.headers[j], "Content-Length: %d", (unsigned  int ) strlen(httpTransaction.entity_body));
                        j++;
                        break;

                    default:
                        break;
                }                        
            }

            // for transactions requiring http basic authentication, prepare a base64 encoding of username:password and output a "Authorization: Basic xxx" header
            if(*HTTP_TRANSACTION_LIST[httpTransactionIndex].username != '\0')
            {
                sprintf(mysrc,"%s:%s",httpTransaction.username,httpTransaction.password);
                b64_encode(mysrc, myb64);
                sprintf (httpTransaction.headers[j], "Authorization: Basic %s",myb64);
                j++;
            }

            strcpy(httpTransaction.headers[j], "\0");  // terminate header array

            WaitAssociate = SYS_TICK_Get();
            smMyDemoAppTasks = SM_CONNECT_SERVER_WAIT_ASSOCIATE;
            break;

        case SM_CONNECT_SERVER_WAIT_ASSOCIATE:

            // no wifi, so skip to next state (CONNECT_SERVER_WAIT_DHCP)
            if ( bWiFiConnected != 0  ) smMyDemoAppTasks = SM_CONNECT_SERVER_WAIT_DHCP;
            if((SYS_TICK_Get() - WaitAssociate) > (SYS_TICK_TicksPerSecondGet())* 35 ) {
                smMyDemoAppTasks = SM_INIT;
            }

            break;

        case SM_CONNECT_SERVER_WAIT_DHCP:

            // check whether DHCP process is complete
            if (bWiFiConnected == 0 ) smMyDemoAppTasks = SM_INIT;
            if(DHCPIsBound(myNetHandle))
            {
                // DHCP process complete. Begin DNS process
                #if defined(TCP6_DEMO_APP_VERBOSE)               
                sprintf(OutString,"\r\nDHCP process complete...beginning DNS process...\r\n");
                SYS_CONSOLE_MESSAGE(OutString);
                #endif
                smMyDemoAppTasks = SM_CONNECT_SERVER_BEGIN_DNS;
            }
            break;

        case SM_CONNECT_SERVER_BEGIN_DNS:

            // if the server's IP address is specified in the transaction config, skip DNS process
            if(isdigit(httpTransaction.host[0]))
            {
                TCPIP_HELPER_StringToIPAddress(httpTransaction.host, &serverIP);
                smMyDemoAppTasks = SM_CONNECT_SERVER_OPEN_SOCKET;
                break;
            }
            
            // host name provided
            // start DNS name resolution process
            if(DNSBeginUsage(myNetHandle) != DNS_RES_OK)
            {
                break;
            }
            #if defined(TCP6_DEMO_APP_VERBOSE) 
            sprintf(OutString,"Resolving DNS name...");
            SYS_CONSOLE_MESSAGE(OutString);
            #endif
            DNSResolve(httpTransaction.host, DNS_TYPE_A);
            smMyDemoAppTasks = SM_CONNECT_SERVER_WAIT_DNS;
            break;

        case SM_CONNECT_SERVER_WAIT_DNS:

            dnsRes = DNSIsResolved(httpTransaction.host, &serverIP);
            if(dnsRes == DNS_RES_PENDING)
            {   // ongoing operation;
                break;
            }
            else if(dnsRes < 0)
            {   // some DNS error occurred; retry
                #if defined(TCP6_DEMO_APP_VERBOSE) 
                SYS_CONSOLE_MESSAGE((const char*)"TCP6 Demo App: DNS name resolving failed...\r\n");
                #endif
                TCPClose(mySocket);
                mySocket = INVALID_SOCKET;
                smMyDemoAppTasks = SM_CONNECT_SERVER_BEGIN_DNS;
                nAttempts++;
                if(nAttempts>8) // After 8 attempts, go to next state
                {
                    // reset DNS attempt counter
                    nAttempts = 0;
                    // dispatch next http transaction
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    // output to display
                    #if defined(TCP6_DEMO_APP_VERBOSE) 
                    sprintf(OutString,"CONN fail (DNS)\r\n");
                    SYS_CONSOLE_MESSAGE(OutString);
                    #endif
                    
                }
            }
            else
            {
                // DNS resolved - start timer for opening a socket
                #if defined(TCP6_DEMO_APP_VERBOSE) 
                sprintf(OutString,"DNS Resolved!...creating TCP Socket to %d.%d.%d.%d\r\n",
                        serverIP.v[0],serverIP.v[1], serverIP.v[2], serverIP.v[3]);
                SYS_CONSOLE_MESSAGE(OutString);
                #endif
                smMyDemoAppTasks = SM_CONNECT_SERVER_OPEN_SOCKET;
            }
            DNSEndUsage(myNetHandle);
            break;
        
        case SM_CONNECT_SERVER_OPEN_SOCKET:

            // Open a socket and begin a connection return request to the remote TCP server
            mySocket = TCPOpenClient(IP_ADDRESS_TYPE_IPV4, atoi(httpTransaction.port),
                    (IP_MULTI_ADDRESS*)&serverIP);

	    // Abort operation if no TCP socket could be opened.
            // If this ever happens, you need to update your tcp_config.h
            if(mySocket == INVALID_SOCKET)
            {   // retry
                break;
            }

            // valid socket opened
            #if defined(TCP6_DEMO_APP_VERBOSE) 
            sprintf(OutString,"Client socket created...waiting for server to accept\r\n");
            SYS_CONSOLE_MESSAGE(OutString);
            #endif
            smMyDemoAppTasks = SM_CONNECT_SERVER_WAIT_CONNECT;
            clientTimer = SYS_TICK_Get();
            break;

        case SM_CONNECT_SERVER_WAIT_CONNECT:

            // Wait for the remote server to accept our connection request
            if(!TCPIsConnected(mySocket))
            {
                // Time out if more than 5 seconds is spent in this state
                if((SYS_TICK_Get()-clientTimer) > 5 * SYS_TICK_TicksPerSecondGet() )
                {
                    // Close the socket so it can be used by other modules
                    TCPClose(mySocket);
                    mySocket = INVALID_SOCKET;
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    #if defined(TCP6_DEMO_APP_VERBOSE) 
                    sprintf(OutString,"HTTP Transaction id %s: Failed connecting to the remote server\r\n",
                            httpTransaction.id);
                    SYS_CONSOLE_MESSAGE(OutString);
                    #endif
                }
                break;
            }

            // remote server accepted our connection request
            #if defined(TCP6_DEMO_APP_VERBOSE) 
            sprintf(OutString,"Server accepted our connection!\r\n");
            SYS_CONSOLE_MESSAGE(OutString);
            #endif
            // prepare for entry into next state (SM_HTTPC_TRANSACTION_CONFIG)
            smMyDemoAppTasks = SM_HTTPC_TRANSACTION_SEND;
            break;

        case SM_HTTPC_TRANSACTION_SEND:

            // Make certain the socket can be written to
            if((TCPIsPutReadyCnt = TCPIsPutReady(mySocket)) < 384u)
                break;

            // for readability, insert 1 line between subsequent HTTP requests
            #if defined(TCP6_DEMO_APP_VERBOSE) 
            SYS_CONSOLE_MESSAGE("\r\n");
            #endif

            // send HTTP message request line (method, path, HTTP version)
            sprintf(TCPOutBuffer, "%s %s %s\r\n",
                    httpTransaction.method,
                    httpTransaction.path,
                    httpTransaction.version);
            TCPPutString(mySocket, ( const uint8_t *)&TCPOutBuffer);
            #if defined(TCP6_DEMO_APP_VERBOSE)
            SYS_CONSOLE_MESSAGE(TCPOutBuffer);
            #endif
                       
            // send HTTP header line 1: "Host: xxxxxx\r\n"
            sprintf(TCPOutBuffer, "Host: %s\r\n", httpTransaction.host);
            TCPPutString(mySocket, ( const uint8_t *) &TCPOutBuffer);
            #if defined(TCP6_DEMO_APP_VERBOSE)
            SYS_CONSOLE_MESSAGE(TCPOutBuffer);
            #endif
            
            // send the remaining HTTP header line(s) 
            j = 0;
            while(*httpTransaction.headers[j] != '\0')
            {
                sprintf(TCPOutBuffer, "%s\r\n", httpTransaction.headers[j]);
                TCPPutString(mySocket, ( const uint8_t *) &TCPOutBuffer);
                j++;    // point to next header
                #if defined(TCP6_DEMO_APP_VERBOSE)
                SYS_CONSOLE_MESSAGE(TCPOutBuffer);
                #endif
            }

            // send the HTTP blank line delimiter
            TCPPutString(mySocket, ( const uint8_t *) "\r\n");
            #if defined(TCP6_DEMO_APP_VERBOSE)
            SYS_CONSOLE_MESSAGE("\r\n");
            #endif

            // if the http transaction contains a body, send it
            if(HTTP_TRANSACTION_LIST[httpTransactionIndex].entity_body)
            {
                sprintf(TCPOutBuffer, "%s", httpTransaction.entity_body);
                TCPPutString(mySocket,( const uint8_t *) &TCPOutBuffer);
                #if defined(TCP6_DEMO_APP_VERBOSE)
                SYS_CONSOLE_MESSAGE(TCPOutBuffer);
                #endif
            }
            
            // Send the packet
            TCPFlush(mySocket);

            // initialize the tcp receive buffer
            memset(TCPInBuffer, '\0', sizeof(TCPInBuffer));
            // initialize the tcp receive buffer index
            TCPInBufferIndex = 0;

            // initialize HTTP reply line counter
            HTTPReplyLineCnt = 0;

            // initialize time delay measurement for waiting reply
            HTTPReplyDelay = SYS_TICK_Get();
            smMyDemoAppTasks = SM_HTTPC_TRANSACTION_RECEIVE;
            break;

        case SM_HTTPC_TRANSACTION_RECEIVE:

            // Check to see if the remote node has disconnected from us or sent us any application data
            // If application data is available, write it to the UART
            if(!TCPIsConnected(mySocket))
            {
                smMyDemoAppTasks = SM_HTTPC_TRANSACTION_CLEANUP;
                // Do not break;  We might still have data in the TCP RX FIFO waiting for us
            }

            // process received character
            if(TCPGet(mySocket, &TCPReadByte))
            {
                TCPInBuffer[TCPInBufferIndex] = (char)TCPReadByte;
                #if defined(TCP6_DEMO_APP_VERBOSE)
                SYS_CONSOLE_PUTC(TCPInBuffer[TCPInBufferIndex]);
                #endif
                TCPInBufferIndex++;
                if(TCPInBufferIndex > sizeof(TCPInBuffer))
                {
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_CLEANUP;
                }
            }
            break;

        case SM_HTTPC_TRANSACTION_CLEANUP:

            TCPClose(mySocket);
            mySocket = INVALID_SOCKET;
            smMyDemoAppTasks = SM_DISPATCH_RESPONSE_HANDLER;
            break;

        case SM_DISPATCH_RESPONSE_HANDLER:

            switch(httpTransactionIndex)
            {
                case 0:
                // #18: GET to TCP6 Web API
                //    - process the control variable "ledtoggle"
                // look for string <ledtoggle> in received data - it will contain any
                // required updates to the explorer16 LEDs
                // received field contains information as to which LED to toggle
                // explorer Leds D10 thru D4 are able to be toggled
                // i.e. "<ledtoggle>128</ledtoggle>" should toggle D10,
                // <ledtoggle>64</ledtoggle>" should toggle D9 etc

                // find the location of "ledset=" field in Rx'd data
                TCPInBufPtr = (unsigned char *)strstr(TCPInBuffer, "<ledtoggle>");

                if(TCPInBufPtr)
                {
                    j = 0;
                    // point to the ledtoggle value
                    TCPInBufPtr += 11;
                    // get the ledset value string
                    while(*TCPInBufPtr != '<')
                    {
                        temp[j] = *TCPInBufPtr;
                        TCPInBufPtr++;
                        j++;
                    }
                    // terminate string with NULL char
                    temp[j] = '\0';
                    // convert it to an unsigned int
                    toggleLed = (volatile unsigned int)strtol((const char *)temp, (char **)NULL, 10);
                    // apply it
 //                   LATA ^= toggleLed;
                    if ( toggleLed & 0x01 ) {
                        LED0_INV();
                    }
                    if ( toggleLed & 0x02 ) {
                        LED1_INV();
                    }
                    if ( toggleLed & 0x04 ) {
                        LED2_INV();
                    }
                    // next state
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                }
                else
                {
                    // error in received http message body, or invalid http reply
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;

                }
                break;

                case 1:
                    // #19: PUT to TCP6 Web API
                    // - no response body, simply restart
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    break;

                case 2:
                    // #20: GET to Yahoo Weather API
                    // Display Toronto temperature in line 1 of LCD
                    
                    // find the location of "temp=" field in Rx'd data
                    TCPInBufPtr = (unsigned char *)strstr(TCPInBuffer, "temp=");
                    if(TCPInBufPtr)
                    {
                        j = 0;
                        // point to the temperature value
                        TCPInBufPtr += 6;
                        // get the ledset value string
                        while(*TCPInBufPtr != '"')
                        {
                            temp[j] = *TCPInBufPtr;
                            TCPInBufPtr++;
                            j++;
                        }
                        // terminate string with NULL char
                        temp[j] = '\0';
                        // print to LCD
                        SYS_OUT_MESSAGE_LINE("Yahoo! Weather", 0);
                        sprintf(WeatherString,"St. Petersburg temperature: %s C", temp);
                        SYS_OUT_MESSAGE_LINE(OutString, 1);
                        // next state
                        smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    }
                    else
                    {
                        // error in received http message body, or invalid http reply
                        smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;

                    }
                    break;
                case 3:
                    // #19: PUT to TCP6 Web API
                    // - no response body, simply restart
                    smMyDemoAppTasks = SM_HTTPC_TRANSACTION_DISPATCH;
                    break;

                default:
                    break;

            } // switch(httptransactionIndex)
            break;

    } // switch(smMyDemoAppTasks)

} // tcp6_demo_app_tasks()

// *****************************************************************************
/*  Function:
        void tcp6_demo_app_init(void)

    Summary:
        One-time application initialization

    Description:
        This section is for a description of the function.  It should be in
        complete sentences describing the meaning and usage of the function.

    Remarks:
        The "remarks" section is always optional.  Use it to provide additional
        information such as restrictions, special cases, and related information.
*/

void tcp6_demo_app_init(void)
{
    // Dump some initialization text on the console
    clrscrConsole();
    homeConsole();
    SYS_CONSOLE_MESSAGE("\r\n ---  TCP6 Demo App Starts!  ---\r\n\r\n");
    sprintf(OutString,"version: %s\r\n", Version);
    SYS_CONSOLE_MESSAGE(OutString);
    sprintf(OutString,"build date: %s\r\n", CompileDate);
    SYS_CONSOLE_MESSAGE(OutString);
    sprintf(OutString,"build time: %s\r\n", CompileTime);
    SYS_CONSOLE_MESSAGE(OutString);
    sprintf(OutString,"netBIOS hostname: %s\r\n", TCPIP_STACK_NetBIOSName(myNetHandle));
    SYS_CONSOLE_MESSAGE(OutString);
    sprintf(OutString,"interface name: %s\r\n", TCPIP_STACK_NetName(myNetHandle));
    SYS_CONSOLE_MESSAGE(OutString);
    
    // Read/display mac address to UART & LCD line 1
    // Update MAC address field in resource state data structure
    sprintf(OutString,"mac address: ");
    SYS_CONSOLE_MESSAGE(OutString);
   
    mac_addr_ptr = (uint8_t *) TCPIP_STACK_NetMacAddress(myNetHandle);
    for(i=0;i<6;i++)
    {
        mac_addr[i] = *mac_addr_ptr++;
        sprintf(OutString,"%02X", mac_addr[i]);
        SYS_CONSOLE_MESSAGE(OutString);
        
    }
    
    sprintf(OutString,"\r\n\r\n");
    sprintf(mac_addr_string,"%02X%02X%02X%02X%02X%02X", mac_addr[0], mac_addr[1],
            mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
    SYS_CONSOLE_MESSAGE(OutString);
    sprintf(OutString,"MAC:%s", mac_addr_string);
    SYS_OUT_MESSAGE_LINE(OutString, 0);

    // initialize http transaction index to the first one
    httpTransactionIndex = 0;

} // tcp6_demo_app_init()

// *****************************************************************************
/*  Function:
        void encodeblock(unsigned char in[], char b64str[], int len)

    Summary:
        base64 encoding helper function

    Description:
        This section is for a description of the function.  It should be in
        complete sentences describing the meaning and usage of the function.

    Remarks:
        The "remarks" section is always optional.  Use it to provide additional
        information such as restrictions, special cases, and related information.
*/
/* ----Base64 Encoding / Decoding Table--- */
char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void encodeblock(unsigned char in[], char b64str[], int len)
{
    unsigned char out[5];
    out[0] = b64[ in[0] >> 2 ];
    out[1] = b64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? b64[ ((in[1] & 0x0f) << 2) |
            ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? b64[ in[2] & 0x3f ] : '=');
    out[4] = '\0';
    strncat((char*)b64str, (char*) out, sizeof (out));

} // encodeblock()

// *****************************************************************************
/*  Function:
        void b64_encode(char *clrstr, char *b64dst)

    Summary:
        base64 encode a stream, adding padding if needed

    Description:
        This section is for a description of the function.  It should be in
        complete sentences describing the meaning and usage of the function.

    Remarks:
        The "remarks" section is always optional.  Use it to provide additional
        information such as restrictions, special cases, and related information.
*/
void b64_encode(char *clrstr, char *b64dst)
{
    unsigned char in[3];
    int i, len = 0;
    int j = 0;

    b64dst[0] = '\0';
    while (clrstr[j]) {
        len = 0;
        for (i = 0; i < 3; i++) {
            in[i] = (unsigned char) clrstr[j];
            if (clrstr[j]) {
                len++;
                j++;
            } else in[i] = 0;
        }
        if (len) {
            encodeblock(in, b64dst, len);
        }
    }
} // b64_encode()

void  WFStateNotify(uint8_t state) {
    bWiFiConnected = state;
}


/*******************************************************************************
 End of File
*/

