/*
 * lwip_task
 *
 * Copyright (C) 2022 Texas Instruments Incorporated
 *
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *    Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *    Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the
 *    distribution.
 *
 *    Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
*/

/* Standard includes. */
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

/* Hardware includes. */
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/timer.h"
#include "utils/lwiplib.h"
#include "utils/locator.h"
#include "utils/ustdlib.h"
#include "utils/uartstdio.h"
#include "httpserver_raw/httpd.h"
#include "priorities.h"
#include "io.h"
#include "cgifuncs.h"
/*-----------------------------------------------------------*/

/*
 * Handle for the LED task.
 */
extern TaskHandle_t xTaskLedOnHandle;

/*
 * The system clock frequency.
 */
extern uint32_t g_ui32SysClock;

/*
 * Keeps track of elapsed time in milliseconds.
 */
uint32_t g_ui32SystemTimeMS = 0;

/*
 * A flag indicating the current link status.
 */
volatile bool g_bLinkStatusUp = false;

/*
 * SSI tag indices for each entry in the g_pcSSITags array.
 */
#define SSI_INDEX_LEDSTATE  0
#define SSI_INDEX_FORMVARS  1
#define SSI_INDEX_SPEED     2

/*
 * This array holds all the strings that are to be recognized as SSI tag
 * names by the HTTPD server.  The server will call SSIHandler to request a
 * replacement string whenever the pattern <!--#tagname--> (where tagname
 * appears in the following array) is found in ".ssi", ".shtml" or ".shtm"
 * files that it serves.
 */
static const char *g_pcConfigSSITags[] =
{
    "LEDtxt",        /* SSI_INDEX_LEDSTATE */
    "FormVars",      /* SSI_INDEX_FORMVARS */
    "speed"          /* SSI_INDEX_SPEED */
};

/*
 * The number of individual SSI tags that the HTTPD server can expect to
 * find in our configuration pages.
 */
#define NUM_CONFIG_SSI_TAGS     (sizeof(g_pcConfigSSITags) / sizeof (char *))

/*
 * Prototypes for the various CGI handler functions.
 */
static char *ControlCGIHandler(int32_t iIndex, int32_t i32NumParams,
                               char *pcParam[], char *pcValue[]);
static char *SetTextCGIHandler(int32_t iIndex, int32_t i32NumParams,
                               char *pcParam[], char *pcValue[]);

/*
 * Prototype for the main handler used to process server-side-includes for the
 * application's web-based configuration screens.
 */
static int32_t SSIHandler(int32_t iIndex, char *pcInsert, int32_t iInsertLen);

/*
 * CGI URI indices for each entry in the g_psConfigCGIURIs array.
 */
#define CGI_INDEX_CONTROL       0
#define CGI_INDEX_TEXT          1

/*
 * This array is passed to the HTTPD server to inform it of special URIs
 * that are treated as common gateway interface (CGI) scripts.  Each URI name
 * is defined along with a pointer to the function which is to be called to
 * process it.
 */
static const tCGI g_psConfigCGIURIs[] =
{
    { "/iocontrol.cgi", (tCGIHandler)ControlCGIHandler }, // CGI_INDEX_CONTROL
    { "/settxt.cgi", (tCGIHandler)SetTextCGIHandler }     // CGI_INDEX_TEXT
};

/*
 * The number of individual CGI URIs that are configured for this system.
 */
#define NUM_CONFIG_CGI_URIS     (sizeof(g_psConfigCGIURIs) / sizeof(tCGI))

/*
 * The file sent back to the browser by default following completion of any
 * of our CGI handlers.  Each individual handler returns the URI of the page
 * to load in response to it being called.
 */
#define DEFAULT_CGI_RESPONSE    "/io_cgi.ssi"

/*
 * The file sent back to the browser in cases where a parameter error is
 * detected by one of the CGI handlers.  This should only happen if someone
 * tries to access the CGI directly via the broswer command line and doesn't
 * enter all the required parameters alongside the URI.
 */
#define PARAM_ERROR_RESPONSE    "/perror.htm"

#define JAVASCRIPT_HEADER                                                     \
    "<script type='text/javascript' language='JavaScript'><!--\n"
#define JAVASCRIPT_FOOTER                                                     \
    "//--></script>\n"

/*
 * This CGI handler is called whenever the web browser requests iocontrol.cgi.
 */
static char *
ControlCGIHandler(int32_t iIndex, int32_t i32NumParams, char *pcParam[],
                  char *pcValue[])
{
    int32_t i32LEDState, i32Speed;
    bool bParamError;

    /* We have not encountered any parameter errors yet. */
    bParamError = false;

    /* Get each of the expected parameters. */
    i32LEDState = FindCGIParameter("LEDOn", pcParam, i32NumParams);
    i32Speed = GetCGIParam("speed_percent", pcParam, pcValue, i32NumParams,
            &bParamError);

    /* Was there any error reported by the parameter parser? */
    if(bParamError || (i32Speed < 0) || (i32Speed > 100))
    {
        return(PARAM_ERROR_RESPONSE);
    }

    /* We got all the parameters and the values were within the expected ranges
     * so go ahead and make the changes. */
    io_set_led((i32LEDState == -1) ? false : true);
    io_set_animation_speed(i32Speed);

    /* Send back the default response page. */
    return(DEFAULT_CGI_RESPONSE);
}

/*
 * This CGI handler is called whenever the web browser requests settxt.cgi.
 */
static char *
SetTextCGIHandler(int32_t i32Index, int32_t i32NumParams, char *pcParam[],
                  char *pcValue[])
{
    long lStringParam;
    char pcDecodedString[48];

    /* Find the parameter that has the string we need to display. */
    lStringParam = FindCGIParameter("DispText", pcParam, i32NumParams);

    /* If the parameter was not found, show the error page. */
    if(lStringParam == -1)
    {
        return(PARAM_ERROR_RESPONSE);
    }

    /* The parameter is present. We need to decode the text for display. */
    DecodeFormString(pcValue[lStringParam], pcDecodedString, 48);

    /* Print sting over the UART. */
    UARTprintf(pcDecodedString);
    UARTprintf("\n");

    /* Tell the HTTPD server which file to send back to the client. */
    return(DEFAULT_CGI_RESPONSE);
}

/*
 * This function is called by the HTTP server whenever it encounters an SSI
 * tag in a web page.  The iIndex parameter provides the index of the tag in
 * the g_pcConfigSSITags array. This function writes the substitution text
 * into the pcInsert array, writing no more than iInsertLen characters.
 */
static int32_t
SSIHandler(int32_t iIndex, char *pcInsert, int32_t iInsertLen)
{
    /* Which SSI tag have we been passed? */
    switch(iIndex)
    {
        case SSI_INDEX_LEDSTATE:
            io_get_ledstate(pcInsert, iInsertLen);
            break;

        case SSI_INDEX_FORMVARS:
            usnprintf(pcInsert, iInsertLen,
                    "%sls=%d;\nsp=%d;\n%s",
                    JAVASCRIPT_HEADER,
                    io_is_led_on(),
                    io_get_animation_speed(),
                    JAVASCRIPT_FOOTER);
            break;

        case SSI_INDEX_SPEED:
            io_get_animation_speed_string(pcInsert, iInsertLen);
            break;

        default:
            usnprintf(pcInsert, iInsertLen, "??");
            break;
    }

    /* Tell the server how many characters our insert string contains. */
    return(strlen(pcInsert));
}

/*
 * The interrupt handler for the timer used to pace the animation.
 */
void
AnimTimerIntHandler(void)
{
    BaseType_t  xHigherPriorityTaskWoken = pdFALSE;

    /* Clear the timer interrupt. */
    MAP_TimerIntClear(TIMER2_BASE, TIMER_TIMA_TIMEOUT);

    vTaskNotifyGiveFromISR(xTaskLedOnHandle, &xHigherPriorityTaskWoken);

    /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
     * should be performed to ensure the interrupt returns directly to the
     * highest priority task. */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}

/*
 * Required by lwIP library to support any host-related timer functions.  This
 * function is called periodically, from the lwIP (TCP/IP) timer task context,
 * every "HOST_TMR_INTERVAL" ms (defined in lwipopts.h file).
 */
void
lwIPHostTimerHandler(void)
{
    bool bLinkStatusUp;

    /* Get the current link status and see if it has changed. */
    bLinkStatusUp = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_0) ? true : false;
    if(bLinkStatusUp != g_bLinkStatusUp)
    {
        /* Save the new link status. */
        g_bLinkStatusUp = bLinkStatusUp;

    }

}

/*
 * Setup lwIP raw API services that are provided by the application.  The
 * services provided in this application are - http server, locator service
 * and Telnet server/client.
 */
void
SetupServices(void *pvArg)
{
    uint8_t pui8MAC[6];
    /* Setup the device locator service. */
    LocatorInit();
    lwIPLocalMACGet(pui8MAC);
    LocatorMACAddrSet(pui8MAC);
    LocatorAppTitleSet("EK-TM4C1294XL enet_lwip_freertos");

    /* Initialize the sample httpd server. */
    httpd_init();

    /* Pass our tag information to the HTTP server. */
    http_set_ssi_handler((tSSIHandler)SSIHandler, g_pcConfigSSITags,
            NUM_CONFIG_SSI_TAGS);

    /* Pass our CGI handlers to the HTTP server. */
    http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS);

    /* Initialize IO controls. */
    io_init();

}

/*
 * Initializes the lwIP tasks.
 */
uint32_t
lwIPTaskInit(void)
{
    uint32_t ui32User0, ui32User1;
    uint8_t pui8MAC[6];

    /* Get the MAC address from the user registers. */
    MAP_FlashUserGet(&ui32User0, &ui32User1);
    if((ui32User0 == 0xffffffff) || (ui32User1 == 0xffffffff))
    {
        return(1);
    }

    /* Convert the 24/24 split MAC address from NV ram into a 32/16 split MAC
     * address needed to program the hardware registers, then program the MAC
     * address into the Ethernet Controller registers. */
    pui8MAC[0] = ((ui32User0 >>  0) & 0xff);
    pui8MAC[1] = ((ui32User0 >>  8) & 0xff);
    pui8MAC[2] = ((ui32User0 >> 16) & 0xff);
    pui8MAC[3] = ((ui32User1 >>  0) & 0xff);
    pui8MAC[4] = ((ui32User1 >>  8) & 0xff);
    pui8MAC[5] = ((ui32User1 >> 16) & 0xff);

    /* Lower the priority of the Ethernet interrupt handler to less than
     * configMAX_SYSCALL_INTERRUPT_PRIORITY.  This is required so that the
     * interrupt handler can safely call the interrupt-safe FreeRTOS functions
     * if any. */
    MAP_IntPrioritySet(INT_EMAC0, ETHERNET_INT_PRIORITY);

    /* Set the link status based on the LED0 signal (which defaults to link
     * status in the PHY). */
    g_bLinkStatusUp = GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_3) ? false : true;

    /* Initialize lwIP. */
    lwIPInit(g_ui32SysClock, pui8MAC, 0, 0, 0, IPADDR_USE_DHCP);

    /* Setup the remaining services inside the TCP/IP thread's context. */
    tcpip_callback(SetupServices, 0);

    /* Success. */
    return(0);
}
