/*
 *
 *   Copyright (C) 2016 Texas Instruments Incorporated
 *
 *   All rights reserved. Property of Texas Instruments Incorporated.
 *   Restricted rights to use, duplicate or disclose this code are
 *   granted through contract.
 *
 *   The program may not be used without the written permission of
 *   Texas Instruments Incorporated or against the terms and conditions
 *   stipulated in the agreement under which this program has been supplied,
 *   and under no circumstances can it be used with non-TI connectivity device.
 *
 */

//#include "osi.h"
//#include "pinmux.h"
#include "radiotool.h"
#include "uart_cli.h"
#include "semaphore.h"
#include "pthread.h"
#include "time.h"
//#include <driverlib/utils.h>

#define DEVICE_BUILD_VERSION_MAJOR 0x00
#define DEVICE_BUILD_VERSION_MINOR 0x05

/* Definitions for template frame */
#define RATE RATE_1M
#define FRAME_TYPE          0xC8                /* QOS data */
#define FRAME_CONTROL       0x01                /* TO DS*/
#define DURATION            0x00, 0x00
#define RECEIVE_ADDR        0x55, 0x44, 0x33, 0x22, 0x11, 0x00
#define TRANSMITTER_ADDR    0x00, 0x11, 0x22, 0x33, 0x44, 0x55
#define DESTINATION_ADDR    0x55, 0x44, 0x33, 0x22, 0x11, 0x00
#define FRAME_NUMBER        0x00, 0x00
#define QOS_CONTROL         0x00, 0x00

#define RA_OFFSET               4
#define TA_OFFSET               10
#define DA_OFFSET               16
#define FRAME_SIZE              1500
#define RX_BUFFER_SIZE          1470
#define RX_RECV_TIMEOUT_SEC     0
#define RX_RECV_TIMEOUT_USEC    20000

#define TASK_STACK_SIZE			(2048)

#define CPU_CYCLES_1MSEC (80*1000)

extern unsigned int g_ConnectionStatus;

/**************************** Template frame **********************************/
_u8 TemplateFrame[] = {
    /*---- wlan header start -----*/
    FRAME_TYPE,         /* version type and sub type */
    FRAME_CONTROL,      /* Frame control flag*/
    DURATION,           /* duration */
    RECEIVE_ADDR,       /* Receiver Address */
    TRANSMITTER_ADDR,   /* Transmitter Address */
    DESTINATION_ADDR,   /* destination Address*/
    FRAME_NUMBER,       /* frame number */
    QOS_CONTROL         /* QoS control */
};

_i16 rxSocket;
_u8     DataFrame[FRAME_SIZE];
volatile _u8 txRequested=0;
volatile _u8 txFinished=1;
_u8 g_CurrentTxMode;
_u8 DataPacket[FRAME_SIZE];
_u8 ucTxBuffer[RADIO_CMD_BUFF_SIZE_MAX];

pthread_t 			gTX_thread = (pthread_t)NULL;
//pthread_mutex_t cmd_mutex;

typedef enum
{
    RADIO_CMD_START_TX      = 0x71,
    RADIO_CMD_STOP_TX       = 0x72,
    RADIO_CMD_START_RX      = 0x73,
    RADIO_CMD_GET_STATS     = 0x74,
    RADIO_CMD_STOP_RX       = 0x75,
    GET_MAC_ADDR_COMMAND    = 0x76,
    GET_DEV_VER_COMMAND     = 0x77,

    RADIO_RSP_GET_STAT      = 0xE1,

    GET_MAC_ADDR_RESPONSE   = 0xE3,
    GET_DEV_VER_RESPONSE    = 0xE4,

    MAX_RADIO_CMD_RSP_MAX   = 0xff
}RadioCmdRespOpCode_e;

static _i32 RadioStartTXTask(void *pvParameters);

/* */
/*
void RadioTxTask(void *pvParameters)
{
    tTxMsg Msg;
    int ret=0;

    while(1)
    {
        ret = osi_MsgQRead( &g_TxQueue, &Msg, OSI_WAIT_FOREVER );
        if(OSI_OK==ret)
        {
            Msg.pEntry(Msg.pValue);
        }
    }
}
*/
/* */

void RadioToolCreate()
{
	//cmd_mutex = PTHREAD_MUTEX_INITIALIZER;
    //osi_MsgQCreate(&g_TxQueue, "TxQueue", sizeof(tTxMsg), 2);
    //osi_TaskCreate(RadioTxTask, (signed char*)"RadioTxTask", 4096, NULL, 1, &g_TxTask );
}

/* */
void RadioToolDestroy()
{
	//sem_destroy(&tx_semaphore);
	//osi_MsgQDelete(&g_TxQueue);
    //osi_TaskDelete(&g_TxTask);
    //g_TxQueue = NULL;
    //g_TxTask = NULL;
}

/* */
RTAPI RadioToolCommand(_u8 aucOpCode, _u8 *apucPayload)
{
    _u8 rxStats[FRAME_SIZE];
    _u8 pMacAddress[SL_WLAN_BSSID_LENGTH];
    _u8 pDevVersion[DEV_VER_LENGTH];
    _u8 pHostDriverVersion[32];
    _u16 pHostDriverLength;
    _u8 index;
    int                 retc;
	pthread_attr_t      pAttrs;
	pthread_attr_t      pAttrs_spawn;
	struct sched_param  priParam;

    //pthread_mutex_lock(cmd_mutex);
    index=0;

    /* Initize return data */
    memset(DataPacket, 0, FRAME_SIZE);

    switch (aucOpCode)
    {
        case RADIO_CMD_START_TX:
        {
            memset(ucTxBuffer, 0, RADIO_CMD_BUFF_SIZE_MAX);
            memcpy(ucTxBuffer, apucPayload, RADIO_CMD_BUFF_SIZE_MAX);

            //Invoke

        	pthread_attr_init(&pAttrs);
        	priParam.sched_priority = 2;
        	retc = pthread_attr_setschedparam(&pAttrs, &priParam);
        	retc |= pthread_attr_setstacksize(&pAttrs, TASK_STACK_SIZE);

        	if(retc)
        	{
        		while(1);	/* error handling */
        	}

        	retc = pthread_create(&gTX_thread, &pAttrs, RadioStartTXTask, ucTxBuffer);
            //RadioStartTXTask(ucTxBuffer);

        }
        break;

        case RADIO_CMD_STOP_TX:
        {
            if(RadioStopTX((RadioTxMode_e)g_CurrentTxMode) < 0)
            {
                return -1;
            }
        }
        break;

        case RADIO_CMD_START_RX:
        {
            RadioStartRX(apucPayload[0]);
        }
        break;

        case RADIO_CMD_GET_STATS:
        {
            memset(rxStats, 0, FRAME_SIZE);
            RadioGetStats(rxStats);

            /* Assemble return data */
            DataPacket[0] = (_u8)RADIO_RSP_GET_STAT;
            DataPacket[1] = (_u8)0x00;
            memcpy(DataPacket+2, rxStats, sizeof(SlWlanGetRxStatResponse_t));

            SendPacket(DataPacket, sizeof(SlWlanGetRxStatResponse_t) + 2);
        }
        break;

        case RADIO_CMD_STOP_RX:
        {
            if(RadioStopRX() < 0)
            {
                return -1;
            }
        }
        break;

        case GET_MAC_ADDR_COMMAND:
        {
            memset(pMacAddress, 0 ,sizeof(_u8) * SL_WLAN_BSSID_LENGTH);
            RadioGetMacAddr(pMacAddress);

            /* Assemble return data */
            DataPacket[0] = (_u8)GET_MAC_ADDR_RESPONSE;
            DataPacket[1] = (_u8)0x00;
            memcpy(DataPacket+2, pMacAddress, SL_WLAN_BSSID_LENGTH);

            SendPacket(DataPacket, SL_WLAN_BSSID_LENGTH + 2);
        }
        break;

        case GET_DEV_VER_COMMAND:
        {
            memset(pDevVersion, 0, sizeof(SlDeviceVersion_t));
            RadioGetDeviceVersion(pDevVersion, &pHostDriverLength, pHostDriverVersion);

            /* Assemble return data */
            index = 0;
            DataPacket[index++] = (_u8)GET_DEV_VER_RESPONSE;
            DataPacket[index++] = (_u8)0x00;

            memcpy(&DataPacket[2], pDevVersion, sizeof(SlDeviceVersion_t));
            index += sizeof(SlDeviceVersion_t);

            DataPacket[index++] = (_u8)DEVICE_BUILD_VERSION_MAJOR;
            DataPacket[index++] = (_u8)DEVICE_BUILD_VERSION_MINOR;

            memcpy(&DataPacket[index], &pHostDriverLength, 2);
            index += 2;

            memcpy(&DataPacket[index], pHostDriverVersion, pHostDriverLength);
            index += pHostDriverLength;

            SendPacket(DataPacket, index);
        }
        break;

        default:
            break;
    }

    //pthread_mutex_unlock(cmd_mutex);
    return 0;
}

/* */
RTAPI RadioStopTX(RadioTxMode_e eTxMode)
{
    _i32 retVal;

    switch(eTxMode)
    {
        case RADIO_TX_PACKETIZED:
            txRequested = 0;
            while(txFinished==0);
            retVal = 0;
            break;

        case RADIO_TX_CW:
            sl_Send(rxSocket, NULL, 0, CW_STOP);
            //MSEC_SLEEP(30);
            retVal = sl_Close(rxSocket);
            break;

        case RADIO_TX_CONTINUOUS:
            retVal = sl_Close(rxSocket);
            break;

        default:
            retVal = RADIO_TOOL_ERROR_TX_TYPE_UNKNOWN;
    }

    return retVal;
}

/* */
RTAPI RadioStartTX(RadioTxMode_e eTxMode,
    _i8                ePowerLevel_Tone,
    _u8                eChannel,
    SlWlanRateIndex_e  eRate,
    _u8                ePreamble,
    RadioDataPattern_e eDataPattern,
    _u16               eSize,
    _u32               eDelay,
    _u32               eAmount,
    _u8                eOverrideCCA,
    SlTxInhibitThreshold_e eCCAThreshold,
    _u8                eEnableACKs,
    _u8                *epDstMac)
{
    _u16 loopIdx;
    _i32 length;
    _u16 pConfigLen = SL_WLAN_BSSID_LENGTH;
    _u32 amount = eAmount;
    _i32 minDelay;
    _u32 thrshld = eCCAThreshold;
    _u32 enableACKs = eEnableACKs;

    _i32 retVal = 0;

    _u32 counter = 0;

    g_CurrentTxMode = (_u8) eTxMode;

    /* Input checkings */
    if (eTxMode == RADIO_TX_CW)
    {
        if (ePowerLevel_Tone < RADIO_TOOL_TX_CW_TONE_MIN || ePowerLevel_Tone > RADIO_TOOL_TX_CW_TONE_MAX)
            return -1;
    }
    else if (eTxMode != RADIO_TX_CONTINUOUS || eTxMode != RADIO_TX_PACKETIZED)
    {
        if (ePowerLevel_Tone < RADIO_TOOL_TX_POWER_LEVEL_MIN || ePowerLevel_Tone > RADIO_TOOL_TX_POWER_LEVEL_MAX)
            return -1;
    }
    else
    {
        return -1;
    }

    if (eChannel < 1 || eChannel > 13)
        return -1;

    // Consider checking all others...

    // Common settings for both Packetized and Continuous modes
    if ((RADIO_TX_PACKETIZED == eTxMode) || (RADIO_TX_CONTINUOUS == eTxMode))
    {
        /* build the frame */
        switch (eDataPattern)
        {
            case PATTERN_ALL_0:
                memset(DataFrame, 0, FRAME_SIZE);
                break;

            case PATTERN_ALL_1:
                memset(DataFrame, 1, FRAME_SIZE);
                break;

            case PATTERN_INCREMENTAL:
                for (loopIdx = 0; loopIdx < FRAME_SIZE; loopIdx++)
                    DataFrame[loopIdx] = (_u8)loopIdx;
                break;

            case PATTERN_DECREMENTAL:
                for (loopIdx = 0; loopIdx < FRAME_SIZE; loopIdx++)
                    DataFrame[loopIdx] = (_u8)(FRAME_SIZE - 1 - loopIdx);
                break;

            case PATTERN_PN9:
                //TODO
                break;

            case PATTERN_PN15:
                //TODO
                break;

            case PATTERN_PN23:
                //TODO
                break;

            default:
                memset(DataFrame, 0, FRAME_SIZE);
        }

        /* Insert Source and Target MAC addresses into data frame */
        retVal = sl_NetCfgGet(SL_NETCFG_MAC_ADDRESS_GET, NULL, &pConfigLen, &TemplateFrame[TA_OFFSET]);
        if (retVal < 0)
        {
            return retVal;
        }
        memcpy(&TemplateFrame[RA_OFFSET], epDstMac, SL_WLAN_BSSID_LENGTH);
        memcpy(&TemplateFrame[DA_OFFSET], epDstMac, SL_WLAN_BSSID_LENGTH);
        memcpy(DataFrame, TemplateFrame, sizeof(TemplateFrame));

        /* open a RAW/DGRAM socket based on CCA override */
        rxSocket = sl_Socket(SL_AF_RF, (eOverrideCCA==1 ? SL_SOCK_RAW:SL_SOCK_DGRAM), eChannel);
        if (rxSocket < 0)
        {
            return RADIO_TOOL_ERROR_TX_CREATING_RAW_SOCKET;
        }

        /* Set CCA threshold, if not overriding CCA */
        if (eOverrideCCA == 0)
        {
            retVal = sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT, SL_SO_PHY_TX_INHIBIT_THRESHOLD, &thrshld, sizeof(thrshld));
            if (retVal < 0)
            {
                return retVal;
            }
        }
    }

    /* Individual testing cases */
    switch (eTxMode)
    {
        case RADIO_TX_PACKETIZED:
        {
            length = eSize;
            txRequested = 1;

            /* Enable/Disable ACKs */
            retVal = sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT,  SL_SO_PHY_ALLOW_ACKS ,&enableACKs, sizeof(enableACKs));
            if (retVal < 0)
            {
                return retVal;
            }

            while ((txRequested) && (length == eSize))
            {
                minDelay = (eDelay % 50);

                /* transmit the frame */
                length = sl_Send(rxSocket, DataFrame, eSize, SL_WLAN_RAW_RF_TX_PARAMS(eChannel, eRate, ePowerLevel_Tone, ePreamble));

                /* sleep for sub 50mSec duration*/
                MSEC_SLEEP(minDelay);
                minDelay = (eDelay - minDelay);
                while ((minDelay > 0) && (txRequested))
                {
                MSEC_SLEEP(50);
                    minDelay -= 50;
                }

                /* If specifying amount of packets to transfer, count */
                if (eAmount > 0)
                {
                    counter++;
                    if (counter >= eAmount)
                        break;
                }
            }

            if (length != eSize)
            {
                RadioStopTX(eTxMode);
                return RADIO_TOOL_ERROR_TX_FULL_SIZE_DATA;
            }

            retVal = sl_Close(rxSocket);
            break;
        }

        case RADIO_TX_CONTINUOUS:
        {
            sl_SetSockOpt(rxSocket, SL_SOL_PHY_OPT, SL_SO_PHY_NUM_FRAMES_TO_TX, &amount, sizeof(_u32));
            sl_Send(rxSocket, DataFrame, eSize, SL_WLAN_RAW_RF_TX_PARAMS(eChannel, eRate, ePowerLevel_Tone, ePreamble));
            break;
        }

        case RADIO_TX_CW:
        {
            rxSocket = sl_Socket(SL_AF_RF, SL_SOCK_RAW, eChannel);
            sl_Send(rxSocket, NULL, 0, ePowerLevel_Tone);
            break;
        }

        default:
        {
            retVal = RADIO_TOOL_ERROR_TX_TYPE_UNKNOWN;
        }
    }

    return retVal;
}

/* */
static _i32 RadioStartTXTask(void *pvParameters)
{
    _u8* pucTxData = (_u8*) pvParameters;

    RadioTxMode_e       eTxMode;
    _i8                 ePowerLevel_Tone;
    _u8                 eChannel;
    SlWlanRateIndex_e   eRate;
    _u8                 ePreamble;
    RadioDataPattern_e  eDataPattern;
    _u16                eSize;
    _u32                eDelay;
    _u32                eAmount;
    _u8                 eOverrideCCA;
    SlTxInhibitThreshold_e eCCAThreshold;
    _u8                 eEnableACKs;
    _u8                 epDstMac[SL_WLAN_BSSID_LENGTH];

    /* Cast data */
    eTxMode             = (RadioTxMode_e) pucTxData[0];
    ePowerLevel_Tone    = pucTxData[1];
    eChannel            = pucTxData[2];
    eRate               = (SlWlanRateIndex_e) pucTxData[3];
    ePreamble           = pucTxData[4];
    eDataPattern        = (RadioDataPattern_e) pucTxData[5];
    eSize               = (pucTxData[6]<<8) | pucTxData[7];
    eDelay              = (pucTxData[8]<<24) | (pucTxData[9]<<16) | (pucTxData[10]<<8) | (pucTxData[11]);
    eAmount             = (pucTxData[12]<<24) | (pucTxData[13]<<16) | (pucTxData[14]<<8) | (pucTxData[15]);
    eOverrideCCA        = pucTxData[16];
    eCCAThreshold       = (SlTxInhibitThreshold_e) pucTxData[17];
    eEnableACKs         = pucTxData[18];
    memcpy(epDstMac, &pucTxData[19], SL_WLAN_BSSID_LENGTH);

    return RadioStartTX(eTxMode, ePowerLevel_Tone, eChannel, eRate, ePreamble, eDataPattern, eSize, eDelay, eAmount, eOverrideCCA, eCCAThreshold, eEnableACKs, epDstMac);
}

RTAPI RadioStopRX ()
{
    _i32 retVal;

    sl_WlanRxStatStop();
    MSEC_SLEEP(30);
    retVal = sl_Close(rxSocket);

    return retVal;
}

/* */
RTAPI RadioStartRX(_u8 eChannel)
{
    _i32 retVal = 0;
    struct SlTimeval_t timeval;

    timeval.tv_sec = RX_RECV_TIMEOUT_SEC;
    timeval.tv_usec = RX_RECV_TIMEOUT_USEC;

    retVal = sl_WlanRxStatStart();
    if (retVal < 0)
        return retVal;

    rxSocket = sl_Socket(SL_AF_RF, SL_SOCK_RAW, eChannel);
    if (rxSocket < 0)
        return RADIO_TOOL_ERROR_RX_CREATING_RAW_SOCKET;

    retVal = sl_SetSockOpt(rxSocket, SL_SOL_SOCKET, SL_SO_RCVTIMEO, &timeval, sizeof(timeval));    // Enable receive timeout
    if (retVal < 0)
        return retVal;

    // Return value of -11 is expected
    sl_Recv(rxSocket, DataFrame, RX_BUFFER_SIZE, 0);

    return 0;
}

/* */
RTAPI RadioGetStats(_u8 *pRxStats)
{
    _i32 retVal = 0;

    retVal = sl_WlanRxStatGet((SlWlanGetRxStatResponse_t *)pRxStats, 0);
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_RX_STATS;

    return 0;
}

/* */
RTAPI RadioGetMacAddr(_u8 *pMacAddress)
{
    _i32    retVal = 0;
    _u16    pConfigLen = SL_MAC_ADDR_LEN;

    retVal = sl_NetCfgGet(SL_NETCFG_MAC_ADDRESS_GET, NULL, &pConfigLen, pMacAddress);
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_MAC_ADDR;

    return 0;
}

/* */
RTAPI RadioGetDeviceVersion(_u8 *pDevVersion, _u16 *pHDLength, _u8 *pHDVersion)
{
    _i32    retVal = 0;
    _u8     pConfigOpt;
    _u16    pConfigLen;

    *pHDLength = strlen(SL_DRIVER_VERSION);
    SlDeviceVersion_t deviceVersion;

    pConfigOpt = SL_DEVICE_GENERAL_VERSION;
    pConfigLen = sizeof(SlDeviceVersion_t);

    /* Acquire device version */
    retVal = sl_DeviceGet(SL_DEVICE_GENERAL, &pConfigOpt, &pConfigLen, (_u8 *)(&deviceVersion));
    if (retVal < 0)
        return RADIO_TOOL_ERROR_GETTING_DEV_VERSION;

    memcpy(pDevVersion, &deviceVersion, pConfigLen);
    memcpy(pHDVersion, SL_DRIVER_VERSION, strlen(SL_DRIVER_VERSION));

    return 0;
}
