// This code configures the device into Station Mode and also does the following:
// 1.


#include <ti/drivers/net/wifi/simplelink.h>
#include <ti/drivers/net/wifi/slnetifwifi.h>

#include <ti/display/Display.h>

#include <ti/drivers/SPI.h>

// TI-Driver includes
#include "ti_drivers_config.h"
#include "pthread.h"
#include <string.h>

#define APPLICATION_NAME                      ("UDP Echo")
#define APPLICATION_VERSION                   ("1.0.0.0")
#define DEVICE_ERROR                          ("Device error, please refer \"DEVICE ERRORS CODES\" section in errors.h")
#define WLAN_ERROR                            ("WLAN error, please refer \"WLAN ERRORS CODES\" section in errors.h")
#define SL_STOP_TIMEOUT                       (200)

#define UDPPORT                               (1000)
#define SPAWN_TASK_PRIORITY                   (9)
#define TASK_STACK_SIZE                       (2048)
#define SLNET_IF_WIFI_PRIO                    (5)
#define SLNET_IF_WIFI_NAME                    "CC32xx"

#define SSID_NAME                             "H_C_Trivedi"                             // AP SSID
#define SECURITY_TYPE                         SL_WLAN_SEC_TYPE_WPA_WPA2                 // Security type could be SL_WLAN_SEC_TYPE_OPEN
#define SECURITY_KEY                          "123456789"                               // Password of the secured AP

_u8 country_code[] = "US";                                                              // country code for regulatory domain
_u8 secutiry_type = SL_WLAN_SEC_TYPE_WPA_WPA2;                                          // update security parameters
_u8 STAPower = 0;                                                                       // set Tx power level
_u8 *device_name = "Trivedi_STA";                                                       // set new device name

long counter1 = 0;
long counter2 = 0;

pthread_t udpThread = (pthread_t)NULL;
pthread_t spawn_thread = (pthread_t)NULL;
int32_t             mode;
Display_Handle display;

extern void echoFxn(uint32_t arg0, uint32_t arg1);
extern int32_t ti_net_SlNet_initConfig();

void get_device_config_parameters(void)
{
    printf("\n\n \r Getting Station Configuration Parameters through get_device_config_parameters function...");

    _u8 macAddressVal[SL_MAC_ADDR_LEN];
    _u16 macAddressLen = SL_MAC_ADDR_LEN;
    _u16 ConfigOpt1 = 0;
    sl_NetCfgGet(SL_NETCFG_MAC_ADDRESS_GET, &ConfigOpt1, &macAddressLen,(_u8 *)macAddressVal);
    printf("\n\n \r STA MAC Address is: %02x:%02x:%02x:%02x:%02x:%02x", macAddressVal[0], macAddressVal[1], macAddressVal[2], macAddressVal[3], macAddressVal[4], macAddressVal[5]);

    _u16 len = sizeof(SlNetCfgIpV4Args_t);
    _u16 ConfigOpt2 = 0;   //return value could be one of the following: SL_NETCFG_ADDR_DHCP / SL_NETCFG_ADDR_DHCP_LLA / SL_NETCFG_ADDR_STATIC
    SlNetCfgIpV4Args_t ipV4 = {0};
    sl_NetCfgGet(SL_NETCFG_IPV4_STA_ADDR_MODE,&ConfigOpt2,&len,(_u8 *)&ipV4);

    printf("\n \r DHCP is %s \n \r IP %d.%d.%d.%d \n \r MASK %d.%d.%d.%d \n \r GW %d.%d.%d.%d \n \r DNS %d.%d.%d.%d",
        (ConfigOpt2 == SL_NETCFG_ADDR_DHCP) ? "ON" : "OFF",
        SL_IPV4_BYTE(ipV4.Ip,3),SL_IPV4_BYTE(ipV4.Ip,2),SL_IPV4_BYTE(ipV4.Ip,1),SL_IPV4_BYTE(ipV4.Ip,0),
        SL_IPV4_BYTE(ipV4.IpMask,3),SL_IPV4_BYTE(ipV4.IpMask,2),SL_IPV4_BYTE(ipV4.IpMask,1),SL_IPV4_BYTE(ipV4.IpMask,0),
        SL_IPV4_BYTE(ipV4.IpGateway,3),SL_IPV4_BYTE(ipV4.IpGateway,2),SL_IPV4_BYTE(ipV4.IpGateway,1),SL_IPV4_BYTE(ipV4.IpGateway,0),
        SL_IPV4_BYTE(ipV4.IpDnsServer,3),SL_IPV4_BYTE(ipV4.IpDnsServer,2),SL_IPV4_BYTE(ipV4.IpDnsServer,1),SL_IPV4_BYTE(ipV4.IpDnsServer,0));

    printf("\n");
    printf("\n");
}

/*
 *  ======== printError ========
 */
void printError(char *errString, int code)
{
    Display_printf(display, 0, 0, "Error! code = %d, Description = %s\n", code,
                   errString);
    while(1);
}

/*!
    \brief          SimpleLinkNetAppEventHandler

    This handler gets called whenever a Netapp event is reported by the host driver / NWP. Here user can implement he's own logic for any of these events. This handler is used by 'network_terminal' application to show case the following scenarios:

    1. Handling IPv4 / IPv6 IP address acquisition.
    2. Handling IPv4 / IPv6 IP address Dropping.

    \param          pNetAppEvent     -   pointer to Netapp event data.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 5.7
 */

void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent)
{
    int32_t             status = 0;
    pthread_attr_t      pAttrs;
    struct sched_param  priParam;

    if(pNetAppEvent == NULL)
    {
        return;
    }

    switch(pNetAppEvent->Id)
    {
    case SL_NETAPP_EVENT_IPV4_ACQUIRED:
    case SL_NETAPP_EVENT_IPV6_ACQUIRED:

        /* Initialize SlNetSock layer with CC3x20 interface                      */
        status = ti_net_SlNet_initConfig();
        if(0 != status)
        {
            Display_printf(display, 0, 0, "Failed to initialize SlNetSock\n\r");
        }

        if(mode != ROLE_AP)
        {
            Display_printf(display, 0, 0,"[NETAPP EVENT] IP Acquired: IP=%d.%d.%d.%d , "
                           "Gateway=%d.%d.%d.%d\n\r",
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,3),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,2),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,1),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,0),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,3),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,2),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,1),
                           SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,0));

            pthread_attr_init(&pAttrs);
            priParam.sched_priority = 1;
            status = pthread_attr_setschedparam(&pAttrs, &priParam);
            status |= pthread_attr_setstacksize(&pAttrs, TASK_STACK_SIZE);
            status = pthread_create(&udpThread, &pAttrs, (void *(*)(void *))echoFxn, (void*)UDPPORT);
            if(status)
            {
                printError("Task create failed", status);
            }
        }
        break;
    default:
        break;
    }
}
/*!
    \brief          SimpleLinkFatalErrorEventHandler

    This handler gets called whenever a socket event is reported by the NWP / Host driver. After this routine is called, the user's application must restart the device in order to recover.

    \param          slFatalErrorEvent    -   pointer to fatal error event.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 17.9.
 */

void SimpleLinkFatalErrorEventHandler(SlDeviceFatal_t *slFatalErrorEvent)
{
    /* Unused in this application */
}
/*!
    \brief          SimpleLinkNetAppRequestMemFreeEventHandler

    This handler gets called whenever the NWP is done handling with the buffer used in a NetApp request. This allows the use of dynamic memory with these requests.

    \param          pNetAppRequest     -   Pointer to NetApp request structure.
    \param          pNetAppResponse    -   Pointer to NetApp request Response.
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 17.9.
    \return         void
 */

void SimpleLinkNetAppRequestMemFreeEventHandler(uint8_t *buffer)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkNetAppRequestEventHandler

    This handler gets called whenever a NetApp event is reported by the NWP / Host driver. User can write he's logic to handle the event here.

    \param          pNetAppRequest     -   Pointer to NetApp request structure.
    \param          pNetAppResponse    -   Pointer to NetApp request Response.
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 17.9.
    \return         void
 */

void SimpleLinkNetAppRequestEventHandler(SlNetAppRequest_t *pNetAppRequest, SlNetAppResponse_t *pNetAppResponse)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkHttpServerEventHandler

    This handler gets called whenever a HTTP event is reported by the NWP internal HTTP server.

    \param          pHttpEvent       -   pointer to http event data.
    \param          pHttpEvent       -   pointer to http response.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) chapter 9.
 */

void SimpleLinkHttpServerEventHandler(SlNetAppHttpServerEvent_t *pHttpEvent,
                                      SlNetAppHttpServerResponse_t *pHttpResponse)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkWlanEventHandler

    This handler gets called whenever a WLAN event is reported by the host driver / NWP. Here user can implement he's own logic for any of these events. This handler is used by 'network_terminal' application to show case the following scenarios:

    1. Handling connection / Disconnection.
    2. Handling Addition of station / removal.
    3. RX filter match handler.
    4. P2P connection establishment.

    \param          pWlanEvent       -   pointer to Wlan event data.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) sections 4.3.4, 4.4.5 and 4.5.5.
    \sa             cmdWlanConnectCallback, cmdEnableFilterCallback, cmdWlanDisconnectCallback, cmdP2PModecallback.
 */

void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
{
    if(pWlanEvent == NULL)
    {
        return;
    }
    switch(pWlanEvent->Id)
    {
    case SL_WLAN_EVENT_CONNECT:
        printf("\n\r Connected to the Network with following configuration:");
        printf("\n\r SSID Name: %s", pWlanEvent->Data.Connect.SsidName);
        printf("\n\n");
        break;
    case SL_WLAN_EVENT_DISCONNECT:
        printf("\n\r Connected to the Network with following configuration:");
        printf("\n\r SSID Name: %s", pWlanEvent->Data.Disconnect.SsidName);
        break;
    default:
        break;
    }
}


/*
void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
{
    // here details can be added to check the connectivity status of STA...

    int32_t             status = 0;
    pthread_attr_t      pAttrs;
    struct sched_param  priParam;

    if(pWlanEvent == NULL)
    {
        return;
    }

    switch(pWlanEvent->Id)
    {
    case SL_WLAN_EVENT_CONNECT:
//        printf("\n\n\n SSID Name = %s ", pSLWLANEvent->EventData.STAandP2PModeWlanConnected.ssid_name);                     //=> copy such commands from webpage for further details...
        break;
    default:
        break;
}
 */


/*!
    \brief          SimpleLinkGeneralEventHandler

    This handler gets called whenever a general error is reported by the NWP / Host driver. Since these errors are not fatal, application can handle them.

    \param          pDevEvent    -   pointer to device error event.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 17.9.
 */

void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkSockEventHandler

    This handler gets called whenever a socket event is reported by the NWP / Host driver.

    \param          SlSockEvent_t    -   pointer to socket event data.
    \return         void
    \note           For more information, please refer to: user.h in the porting folder of the host driver and the  CC31xx/CC32xx NWP programmer's guide (SWRU455) section 7.6.
 */

void SimpleLinkSockEventHandler(SlSockEvent_t *pSock)
{
    /* Unused in this application */
}

void mainThread(void *pvParameters)
{
    int32_t             status = 0;
    pthread_attr_t      pAttrs_spawn;
    struct sched_param  priParam;
    unsigned int flag1=0;
    SlWlanSecParams_t   secParams = {0};

    SPI_init();
    Display_init();
    display = Display_open(Display_Type_UART, NULL);
    if (display == NULL) {
        /* Failed to open display driver */
        while(1);
    }

    /* Start the SimpleLink Host */
    pthread_attr_init(&pAttrs_spawn);
    priParam.sched_priority = SPAWN_TASK_PRIORITY;
    status = pthread_attr_setschedparam(&pAttrs_spawn, &priParam);
    status |= pthread_attr_setstacksize(&pAttrs_spawn, TASK_STACK_SIZE);

    status = pthread_create(&spawn_thread, &pAttrs_spawn, sl_Task, NULL);
    if(status)
    {
        printError("Task create failed", status);
    }

    /* Turn NWP on - initialize the device*/
    mode = sl_Start(0, 0, 0);
    Display_printf(display, 0, 0,"\n\rMode: %d", mode);
    if (mode < 0)
    {
        Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, DEVICE_ERROR);
    }
    else
    {
        printf("\n Device started with %d mode \n", mode);
    }

    switch(mode)
    {
    case 0:
        printf("\n \r Role = Station");
        break;
    case 1:
        printf("\n \r Role = Reserved!");
        break;
    case 2:
        printf("\n \r Role = Access Point!");
        break;
    case 3:
        printf("\n \r Role = ROLE_P2P!");
        break;
    case 4:
        printf("\n \r Role = ROLE_TAG!");
        break;
    default:
        printf("\n \r Some Error Occured! No such role!!");
        break;
    }

    if(mode != ROLE_STA)
    {
        /* Set NWP role as STA */
        mode = sl_WlanSetMode(ROLE_STA);
        if (mode < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, WLAN_ERROR);
        }

        /* For changes to take affect, we restart the NWP */
        status = sl_Stop(SL_STOP_TIMEOUT);
        if (status < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, status, DEVICE_ERROR);
        }

        mode = sl_Start(0, 0, 0);
        if (mode < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, DEVICE_ERROR);
        }
    }

    if(mode == ROLE_STA)
    {
        Display_printf(display, 0, 0, "Station Mode Established");

        // set device name

        status = sl_NetAppSet (SL_NETAPP_DEVICE_ID,SL_NETAPP_DEVICE_URN, strlen(device_name), (_u8 *) device_name);
        if( status )
        {
            Display_printf(display, 0, 0, "Device Name could not be updated");
        }
        else
        {
            printf("\n Device Name Updated \n");
        }

        status = sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, SL_WLAN_GENERAL_PARAM_OPT_STA_TX_POWER,1,(_u8*)& STAPower);
        if(status)
        {
            Display_printf(display, 0, 0, "Station Mode Established");
        }
        else
        {
            printf("\n Station power updated \n");
        }

        status = sl_WlanSet(SL_WLAN_CFG_GENERAL_PARAM_ID, SL_WLAN_GENERAL_PARAM_OPT_COUNTRY_CODE, 2, country_code);
        if(status)
        {
            Display_printf(display, 0, 0, "Country Code could not be Modified");
        }
        else
        {
            printf("\n Station country code updated \n");
        }

        // For changes to take affect, we restart the NWP

        status = sl_Stop(SL_STOP_TIMEOUT);
        if (status < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, status, DEVICE_ERROR);
        }

        mode = sl_Start(0, 0, 0);
        if (mode < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, DEVICE_ERROR);
        }
        else
        {
            printf("\n Station restarted after configuration \n");
        }
    }

    printf("\n IP Settings being updated... \n");

    SlNetCfgIpV4Args_t ipV4;
    ipV4.Ip = (_u32)SL_IPV4_VAL(10,1,1,200);                                    // _u32 IP address
    ipV4.IpMask = (_u32)SL_IPV4_VAL(255,255,255,0);                             // _u32 Subnet mask for this STA/P2P
    ipV4.IpGateway = (_u32)SL_IPV4_VAL(10,1,1,1);                               // _u32 Default gateway address
    ipV4.IpDnsServer = (_u32)SL_IPV4_VAL(8,8,8,8);                              // _u32 DNS server address
    status = sl_NetCfgSet(SL_NETCFG_IPV4_STA_ADDR_MODE,SL_NETCFG_ADDR_STATIC,sizeof(ipV4), (_u8*)&ipV4);

    if(status)
    {
        Display_printf(display, 0, 0, "IP address could not be updated");
    }
    else
    {
        Display_printf(display, 0, 0, "IP address updated");
        printf("\n IP address updated \n");
    }

    secParams.Key = (signed char*)SECURITY_KEY;
    secParams.KeyLen = strlen(SECURITY_KEY);
    secParams.Type = SECURITY_TYPE;
    printf("Connecting to : %s.\r\n",SSID_NAME);
    status = sl_WlanConnect((signed char*)SSID_NAME, strlen(SSID_NAME), 0, &secParams, 0);
    if (status)
    {
        Display_printf(display, 0, 0, "Could not Connect to : %s.\r\n",SSID_NAME);
    }
    /* else
    {
        Display_printf(display, 0, 0, "Connected to: %s.\r\n",SSID_NAME);
        printf("\n Connected to: %s.\r\n",SSID_NAME);
    } */

    get_device_config_parameters();

    // while(1);

    _i16 Sd;
    SlSockAddrIn_t Addr;
    _i8 SendBuf[] = "Hello from STA!!!";
    _i8 RecvBuf[1460] = {0};
    unsigned char AddrSize = 0;
    Sd = sl_Socket(SL_AF_INET, SL_SOCK_DGRAM, 0);
    if( Sd < 0)
    {
        printf("\n Socket creation failed!!! \n");
    }
    else
    {
        printf("\n Socket created successfully! \n");
    }

    Addr.sin_family = SL_AF_INET;
    Addr.sin_port = sl_Htons(5001);
    Addr.sin_addr.s_addr = SL_INADDR_ANY;
    status = sl_Bind(Sd, ( SlSockAddr_t *)&Addr, sizeof(SlSockAddrIn_t));
    if( status )
    {
        printf(" \n Socket could not be bound!! \n ");
    }
    else
    {
        printf("\n Socket bound successfully ! \n");
    }

    Addr.sin_addr.s_addr = sl_Htonl(SL_IPV4_VAL(10,1,1,201));              // connect with this specific address only.

    status = sl_Connect(Sd, ( SlSockAddr_t *)&Addr, sizeof(SlSockAddrIn_t));    // address specified in this statement is the same from which the data is to be received and to which the data is to be sent.

    if(status)
    {
        printf("\n\n\n Socket Connection failed");
    }




    while(1)
    {
        char msg1[] = "STA Message # ";
        char msg2[6], msg3[6];
        ltoa(counter1, msg2, 10);
        ltoa(counter2, msg3, 10);
        strcat(msg1, msg2);
        strcat(msg1, " & ");
        strcat(msg1, msg3);

        status = sl_Send(Sd, msg1, strlen(msg1), 0);
        if( strlen(msg1) != status )
        {
            printf("\n\n\n All packets could not be sent");
        }

        /*
                    Addr.sin_family = SL_AF_INET;
                    Addr.sin_port = sl_Htons(5001);
                    Addr.sin_addr.s_addr = sl_Htonl(SL_IPV4_VAL(192,168,1,31));
                    Status = sl_SendTo(Sd, SendBuf, strlen(SendBuf), 0, (SlSockAddr_t*)&Addr,sizeof(SlSockAddr_t));
                    if( strlen(SendBuf) != Status )
                    {
                    // error
                    }

         */

        status = sl_Recv(Sd, RecvBuf, 1460, 0);
        if (status < 0)
        {
            printf("\n\n\nPacket reception failed!!");
        }
        else
        {
            // printf("\n\n\n number of packets received = %d", status);
            // printf(" \n received messages is: %s", RecvBuf);
            // printf(" \n " );
            // printf(" \n " );
        }

        counter1++;
        if(counter1 == 50000)
        {
            printf(" \n received messages is: %s", RecvBuf);
            printf(" \n " );
            counter2++;
            counter1=0;
        }

        /*

                    AddrSize = sizeof(SlSockAddrIn_t);
                    status = sl_RecvFrom(Sd, RecvBuf, 1460, 0, ( SlSockAddr_t *)&Addr, &AddrSize);
                    if( 0 > Status )
                    {
                    // error
                    }

         */
    }


    status = sl_Close(Sd);
    if( status )
    {
        printf("Error Occured!!");
    }
    else
    {
        printf("\n Socket Closed \n");
    }

    /*

    Addr.sin_family = SL_AF_INET;
    Addr.sin_port = sl_Htons(5001);
    Addr.sin_addr.s_addr = sl_Htonl(SL_IPV4_VAL(10,1,1,201));
    status = sl_SendTo(Sd, SendBuf, strlen(SendBuf), 0, (SlSockAddr_t*)&Addr,sizeof(SlSockAddr_t));
    if( strlen(SendBuf) != status )
    {
        printf("\n Sending Failed!! \n");
    }
    else
    {
        printf("\n Message transmitted successfully! \n");
    }
    AddrSize = sizeof(SlSockAddrIn_t);
    status = sl_RecvFrom(Sd, RecvBuf, 1460, 0, ( SlSockAddr_t *)&Addr, &AddrSize);
    if( status < 0)
    {
        printf("\n Message could not be received! \n");
    }
    else
    {
        printf("\n Message received successfully! \n");
        printf("\n received message is %s", RecvBuf);
    }
    status = sl_Close(Sd);
    if( status )
    {
        printf("\n Socket could not be closed! \n");
    }*/
}
