/*
 * Copyright (c) 2015-2017, Texas Instruments Incorporated
 * All rights reserved.
 *
 * 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.
 */

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>

/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>

/* BIOS Header files */
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/knl/Event.h>
#include <ti/sysbios/knl/Clock.h>

/* TI-RTOS Header files */ 
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
#include <ti/drivers/GPIO.h>
#include <ti/drivers/rf/RF.h>

/* Board Header files */
#include "ti_drivers_config.h"
#include <ti_radio_config.h>

/* Driverlib Header files */
#include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
#include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
#include DeviceFamily_constructPath(driverlib/cpu.h)

#include "smart_lock_ota.h"
#include "util_timer.h"
#include "RFQueue.h"

/***** Defines *****/
#define NUM_DATA_ENTRIES                2  /* NOTE: Only two data entries supported at the moment */
#define NUM_APPENDED_BYTES              1  /* Length byte included in the stored packet */

#define CS_END_TIME                     (uint32_t)(4000000*0.045f)  // .csEndTime
#define CORR_PERIOD                     0                           // .corrPeriod (not used when csConf.bEnaCorr = 0x0)
#define COMMAND_END_TIME                (uint32_t)(4000000*0.125f)  // .endTime

#define RSSI_THRESHOLD                  ((int8_t)(-100))

#define SNIFF_DATA_EVENT                Event_Id_00
#define PROCESS_DATA_RECEIVE_EVENT      Event_Id_01

#if defined (LATAM_CERTIFICATION)
#define ALL_EVENTS                      (SEND_UART_DATA_EVENT | RF_TEST_EVENT | PROCESS_UART_OTA_EVENT | STOP_ANTENNA_TEST_EVENT)
#else
#define ALL_EVENTS                      (SNIFF_DATA_EVENT | PROCESS_DATA_RECEIVE_EVENT)
#endif

#define SNIFF_DATA_PERIOD               2000 //2sec

#define MAX_LENGTH                  	110

/***** Variable declarations *****/
/* Events handler */
static Event_Handle eventHandle;

/* RF driver object and handle */
RF_Object rfObject;
RF_Handle rfHandle;
RF_CmdHandle rfCmdHandle;
RF_CmdHandle cmdTxHandle;
RF_CmdHandle cmdTxTestHandle;
RF_Params rfParams;
rfc_propRxOutput_t rxStatistics;

/* Buffer which contains all Data Entries for receiving data.
 * Pragmas are needed to make sure this buffer is 4 byte aligned (requirement from the RF Core) */
#if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
        static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                                 MAX_LENGTH,
                                                                 NUM_APPENDED_BYTES)];
#elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment = 4
        static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                                 MAX_LENGTH,
                                                                 NUM_APPENDED_BYTES)];
#elif defined(__GNUC__)
        static uint8_t rxDataEntryBuffer [RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
            MAX_LENGTH, NUM_APPENDED_BYTES)] __attribute__ ((aligned (4)));
#else
    #error This compiler is not supported.
#endif

/* RX Data Queue and Data Entry pointer to read out received packets */
static dataQueue_t dataQueue;
static rfc_dataEntryGeneral_t* currentDataEntry;

/* Received packet's length and pointer to the payload */
uint8_t packetLength;
uint8_t otaPacketLength;
static uint8_t* packetDataPointer;

volatile bool isRfBusy = false;
uint8_t rxPacket[MAX_LENGTH];
bool packetReceived = false;
uint8_t uart_temp_receive;

static ClockP_Struct sniffDataClkStruct;
static ClockP_Handle sniffDataClkHandle;

/***** Prototypes *****/
static void sniffCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
static void uartRxCallbackFxn(UART2_Handle handle, void *buffer, size_t count, void *userArg, int_fast16_t status);
static void initPeripheral(void);

static void processSniffDataCallback(UArg a0);

/*
 *  ======== main ========
 */
void *mainThread(void *arg0)
{
    uint32_t events;

    initPeripheral();

    while (1)
    {
        events = Event_pend(eventHandle, Event_Id_NONE, ALL_EVENTS, BIOS_WAIT_FOREVER);

        if(events)
        {
            if(events & SNIFF_DATA_EVENT)
            {
                if (!isRfBusy) {
                    isRfBusy = true;
                    rfCmdHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdPropRxSniff, RF_PriorityNormal, &sniffCallback, RF_EventRxEntryDone | RF_EventLastCmdDone);
                }
            }

            if(events & PROCESS_DATA_RECEIVE_EVENT)
            {
                RF_yield(rfHandle);
                if (packetReceived) {
                    packetReceived = false;
                }
            }
        }
    }
}

void sniffCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
{
    uint16_t status;
    static uint8_t sniff_retry = 0;

    /* If we've received a new packet and it's available to read out */
    if (e & RF_EventRxEntryDone)
    {
        do
        {
            sniff_retry = 0;
            /* Get current unhandled data entry */
            currentDataEntry = RFQueue_getDataEntry();

            /* Handle the packet data, located at &currentDataEntry->data:
             * - Length is the first byte with the current configuration
             * - Data starts from the second byte */
            packetLength      = *(uint8_t*)(&currentDataEntry->data);
            packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);

            if(packetLength < MAX_LENGTH)
            {
                packetReceived = true;
                memcpy(rxPacket, packetDataPointer, packetLength);
            }

        } while(RFQueue_nextEntry() == DATA_ENTRY_FINISHED);

        Event_post(eventHandle, PROCESS_DATA_RECEIVE_EVENT);
    }

    if(e & RF_EventLastCmdDone)
    {
        isRfBusy = false;
		
		status = RF_cmdPropRxSniff.status;

        if (status == PROP_DONE_RXERR || status == PROP_DONE_BUSYTIMEOUT)
        {
            if(sniff_retry < 3)
            {
                sniff_retry++;
                Event_post(eventHandle, SNIFF_DATA_EVENT);
            }
            else
            {
                sniff_retry = 0;
            }
        }
        else //normal stop or force stop
        {
            sniff_retry = 0;
        }
    }
}

static void initPeripheral(void)
{
    eventHandle = Event_create(NULL, NULL);
    if(eventHandle == NULL)
    {
        /* Event_create(NULL, NULL) failed */
        while (1);
    }

    RF_Params_init(&rfParams);

    /* Create queue and data entries */
    if (RFQueue_defineQueue(&dataQueue,
                            rxDataEntryBuffer,
                            sizeof(rxDataEntryBuffer),
                            NUM_DATA_ENTRIES,
                            MAX_LENGTH + NUM_APPENDED_BYTES))
    {
        /* Failed to allocate space for all data entries */
        while(1);
    }

    /* Set the Data Entity queue for received data */
    RF_cmdPropRx.pQueue = &dataQueue;
    /* Discard ignored packets from Rx queue */
    RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
    /* Discard packets with CRC error from Rx queue */
    RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
    /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
    RF_cmdPropRx.maxPktLen = MAX_LENGTH;
    RF_cmdPropRx.pktConf.bRepeatOk = 1;
    RF_cmdPropRx.pktConf.bRepeatNok = 1;
    RF_cmdPropRx.pOutput = (uint8_t*)&rxStatistics;

    /* Configuration of the Sniff Command
     * Configure all fields common for the WOR example, not set in SmartRF Studio
     */
    RF_cmdPropRxSniff.startTrigger.triggerType = TRIG_NOW;
    RF_cmdPropRxSniff.startTrigger.pastTrig = 0;
    RF_cmdPropRxSniff.pktConf.bUseCrc = 0x1;
    RF_cmdPropRxSniff.pktConf.bVarLen = 0x1;
    RF_cmdPropRxSniff.pktConf.bRepeatOk = 0;
    RF_cmdPropRxSniff.pktConf.bRepeatNok = 0;
    RF_cmdPropRxSniff.rxConf.bIncludeHdr = 0x1;
    RF_cmdPropRxSniff.rxConf.bAutoFlushIgnored = 1;
    RF_cmdPropRxSniff.rxConf.bAutoFlushCrcErr = 1;
    RF_cmdPropRxSniff.syncWord = 0x12341234;
    RF_cmdPropRxSniff.maxPktLen = MAX_LENGTH;
    RF_cmdPropRxSniff.endTrigger.triggerType = TRIG_REL_START;
    RF_cmdPropRxSniff.endTime = COMMAND_END_TIME;
    RF_cmdPropRxSniff.pQueue = &dataQueue;
    RF_cmdPropRxSniff.pOutput = (uint8_t*)&rxStatistics;
    RF_cmdPropRxSniff.csConf.bEnaRssi = 0x1;
    RF_cmdPropRxSniff.csConf.bEnaCorr = 0x0;
    RF_cmdPropRxSniff.csConf.operation = 0x1;
    RF_cmdPropRxSniff.csConf.idleOp = 0x1;
    RF_cmdPropRxSniff.csConf.busyOp = 0x0;
    RF_cmdPropRxSniff.csConf.timeoutRes = 0x1;
    RF_cmdPropRxSniff.rssiThr = RSSI_THRESHOLD;
    RF_cmdPropRxSniff.numRssiIdle = 0x01;
    RF_cmdPropRxSniff.numRssiBusy = 0x01;
    RF_cmdPropRxSniff.corrPeriod = CORR_PERIOD;
    RF_cmdPropRxSniff.corrConfig.numCorrInv = 0x1;
    RF_cmdPropRxSniff.corrConfig.numCorrBusy = 0x1;
    RF_cmdPropRxSniff.csEndTrigger.triggerType = TRIG_REL_START;
    RF_cmdPropRxSniff.csEndTime = CS_END_TIME;

    rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);

    if(rfHandle == NULL)
        while(1);

    /* Set the frequency */
    rfCmdHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);

    RF_yield(rfHandle);

    sniffDataClkHandle = UtilTimer_construct(&sniffDataClkStruct, processSniffDataCallback, SNIFF_DATA_PERIOD, SNIFF_DATA_PERIOD, false, 0);

    UtilTimer_start(&sniffDataClkStruct);

    GPIO_setConfigAndMux(CONFIG_GPIO_UART2_0_TX, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW, IOC_PORT_RFC_GPO0);
}

static void processSniffDataCallback(UArg a0)
{
    (void)a0; // Parameter is not used

    Event_post(eventHandle, SNIFF_DATA_EVENT);
}