/*
 * Copyright (c) 2015-2016, 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.
 */

/*
 *  ======== rfEasyLinkTx.c ========
 */
/* XDCtools Header files */
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Error.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/Clock.h>

/* TI-RTOS Header files */
#include <ti/drivers/PIN.h>

/* Board Header files */
#include "Board.h"

/* EasyLink API Header files */
#include "easylink/EasyLink.h"

#include "UartTask.h"   //add by barbara

/***** Defines *****/

/* Undefine to remove address filter and async mode */
//#define RFEASYLINKRX_ASYNC    //del by barbara
#define RFEASYLINKRX_ADDR_FILTER

#define RFEASYLINKEX_TASK_STACK_SIZE 1024
#define RFEASYLINKEX_TASK_PRIORITY   2

/* Pin driver handle */
static PIN_Handle ledPinHandle;
static PIN_State ledPinState;

//add by barbara -- begin
static PIN_Handle buttonPinHandle;
static PIN_State buttonPinState;




/*
 * Application button pin configuration table:
 *   - Buttons interrupts are configured to trigger on falling edge.
 */
PIN_Config buttonPinTable[] = {
    Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
    PIN_TERMINATE
};

bool TxSendFlag = FALSE;
EasyLink_TxPacket txPacket = {0};
static uint16_t seqNumber;
#define RFEASYLINKTXPAYLOAD_LENGTH      30
#define RFEASYLINKTX_BURST_SIZE         10
uint8_t txBurstSize = 0;
//add by barbara -- end

/*
 * Application LED pin configuration table:
 *   - All LEDs board LEDs are off.
 */
PIN_Config pinTable[] = {
    Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_LED3 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,     //add by barbara
    Board_LED4 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,     //add by barbara
    PIN_TERMINATE
};

/***** Variable declarations *****/
static Task_Params rxtxTaskParams;
Task_Struct rxtxTask;    /* not static so you can see in ROV */
static uint8_t rxtxTaskStack[RFEASYLINKEX_TASK_STACK_SIZE];

/* The RX Output struct contains statistics about the RX operation of the radio */
PIN_Handle pinHandle;

#ifdef RFEASYLINKRX_ASYNC
static Semaphore_Handle rxDoneSem;
#endif

//add by barbara -- begin
static Semaphore_Handle txDoneSem;

//add by barbara -- end

/***** Function definitions *****/

//add by barbara -- begin
/* Pin interrupt Callback function board buttons configured in the pinTable. */
void buttonCallbackFunction(PIN_Handle handle, PIN_Id pinId) {

    /* Simple debounce logic, only toggle if the button is still pushed (low) */
//    CPUdelay((uint32_t)((48000000/3)*0.050f));
    if (!PIN_getInputValue(pinId)) {
        /* Post TX semaphore to TX task */
        //Semaphore_post(txSemaphoreHandle);   Call TX
        if(!TxSendFlag)
        {
          PIN_setOutputValue(pinHandle, Board_LED4,!PIN_getOutputValue(Board_LED4));
          TxSendFlag = TRUE;
        }
    }
}
//add by barbara -- end

#ifdef RFEASYLINKRX_ASYNC
void rxDoneCb(EasyLink_RxPacket * rxPacket, EasyLink_Status status)
{
    if (status == EasyLink_Status_Success)
    {
        /* Toggle LED2 to indicate RX */
        PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
    }
    else if(status == EasyLink_Status_Aborted)
    {
        /* Toggle LED1 to indicate command aborted */
        PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
    }
    else
    {
        /* Toggle LED1 and LED2 to indicate error */
        PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
        PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
    }

    Semaphore_post(rxDoneSem);
}

//add by barbara -- begin
void txDoneCb(EasyLink_Status status)
{
    if (status == EasyLink_Status_Success)
    {
        /* Toggle LED1 to indicate TX */
        PIN_setOutputValue(pinHandle, Board_LED3,!PIN_getOutputValue(Board_LED3));
    }
    else if(status == EasyLink_Status_Aborted)
    {
        /* Toggle LED2 to indicate command aborted */
        PIN_setOutputValue(pinHandle, Board_LED4,!PIN_getOutputValue(Board_LED4));
    }
    else
    {
        /* Toggle LED1 and LED2 to indicate error */
        PIN_setOutputValue(pinHandle, Board_LED3,!PIN_getOutputValue(Board_LED3));
        PIN_setOutputValue(pinHandle, Board_LED4,!PIN_getOutputValue(Board_LED4));
    }

    Semaphore_post(txDoneSem);
}
//add by barbara -- end

#endif

static void rfEasyLinkRxTxFnx(UArg arg0, UArg arg1)
{
#ifndef RFEASYLINKRX_ASYNC
    EasyLink_RxPacket rxPacket = {0};
#endif

#ifdef RFEASYLINKRX_ASYNC
    /* Create a semaphore for Async*/
    Semaphore_Params paramsRx;
    Error_Block ebRx;

    /* Init params */
    Semaphore_Params_init(&paramsRx);
    Error_init(&ebRx);

    /* Create semaphore instance */
    rxDoneSem = Semaphore_create(0, &paramsRx, &ebRx);
        
#endif //RFEASYLINKRX_ASYNC

    //add by barbara -- begin
        /* Create a semaphore for Async*/
    Semaphore_Params paramsTx;
    Error_Block ebTx;

    /* Init params */
    Semaphore_Params_init(&paramsTx);
    Error_init(&ebTx);

    /* Create semaphore instance */
    txDoneSem = Semaphore_create(0, &paramsTx, &ebTx);
    //add by barbara -- end
    
    EasyLink_init(EasyLink_Phy_50kbps2gfsk);
    EasyLink_setFrequency(868000000);

#ifdef RFEASYLINKRX_ADDR_FILTER
    uint8_t addrFilter = 0xaa;
    EasyLink_enableRxAddrFilter(&addrFilter, 1, 1);
#endif //RFEASYLINKRX_ADDR_FILTER
    
    //add by barbara -- begin
        /* Setup callback for button pins */
    if (PIN_registerIntCb(buttonPinHandle, &buttonCallbackFunction) != 0) {
        System_abort("Error registering button callback function");
    }
    

    //add by barbara -- end

    while(1) {
#ifdef RFEASYLINKRX_ASYNC
      if(!TxSendFlag)
      {  
        EasyLink_receiveAsync(rxDoneCb, 0);
//
//        /* Wait 300ms for Rx */
//        if(Semaphore_pend(rxDoneSem, (300000 / Clock_tickPeriod)) == FALSE)           //delete by barbara
       if(Semaphore_pend(rxDoneSem, (100000 / Clock_tickPeriod)) == FALSE)             //add by barbara
       {         
           /* RX timed out abort */
           if(EasyLink_abort() == EasyLink_Status_Success)
           {
              /* Wait for the abort */
              Semaphore_pend(rxDoneSem, BIOS_WAIT_FOREVER);
           }
           
       }  
      }
            else //if(TxSendFlag)      //
            {
                      /* Create packet with incrementing sequence number and random payload */
                txPacket.payload[0] = (uint8_t)(seqNumber >> 8);
                txPacket.payload[1] = (uint8_t)(seqNumber++);
                uint8_t i;
                for (i = 2; i < RFEASYLINKTXPAYLOAD_LENGTH; i++)
                {
                    txPacket.payload[i] = rand();
                }

                txPacket.len = RFEASYLINKTXPAYLOAD_LENGTH;
                txPacket.dstAddr[0] = 0xaa;

                /* Add a Tx delay for > 500ms, so that the abort kicks in and brakes the burst */
                if(txBurstSize++ >= RFEASYLINKTX_BURST_SIZE)
                {
                    /* Set Tx absolute time to current time + 1s */
                    txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(50);
                    txBurstSize = 0;
                }
                /* Else set the next packet in burst to Tx in 100ms */
                else
                {
                /* Set Tx absolute time to current time + 100ms */
                    txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(5);
                }
               EasyLink_transmitAsync(&txPacket, txDoneCb);
               
               if(Semaphore_pend(txDoneSem, (15000 / Clock_tickPeriod)) == FALSE)             //add by barbara
              {         
                  /* RX timed out abort */
                  if(EasyLink_abort() == EasyLink_Status_Success)
                  {
                  /* Wait for the abort */
                    Semaphore_pend(txDoneSem, BIOS_WAIT_FOREVER);
                  }
              //     while(EasyLink_abort() != EasyLink_Status_Success);
              ///* Wait for the abort */
              //    Semaphore_pend(txDoneSem, BIOS_WAIT_FOREVER);
                  TxSendFlag = FALSE;
              }
            }
        
#else
      if(!TxSendFlag)
      { 
          //System_printf("TxSendFlag is FAlSE, EasyLink_receive will be call\n");
        rxPacket.absTime = 0;
        EasyLink_Status result = EasyLink_receive(&rxPacket);

        if (result == EasyLink_Status_Success)
        {
            len = rxPacket.len;
            memcpy(payload, rxPacket.payload, len);
            Uart_PostEvent(UART_EVENT_PRINT);
            /* Toggle LED2 to indicate RX */
            PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
        }
        else
        {
            /* Toggle LED1 and LED2 to indicate error */
            PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
            PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
        }
        //System_printf("TxSendFlag is FAlSE, EasyLink_receive call returned\n");
      }
      else
      {
           PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
           PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
           Semaphore_pend(txDoneSem, BIOS_WAIT_FOREVER);   //if in TX mode pend event;
      }
#endif //RX_ASYNC
    }
}

void rxTask_init(PIN_Handle ledPinHandle) {
    pinHandle = ledPinHandle;

    Task_Params_init(&rxtxTaskParams);
    rxtxTaskParams.stackSize = RFEASYLINKEX_TASK_STACK_SIZE;
    rxtxTaskParams.priority = RFEASYLINKEX_TASK_PRIORITY;
    rxtxTaskParams.stack = &rxtxTaskStack;
    rxtxTaskParams.arg0 = (UInt)1000000;

    Task_construct(&rxtxTask, rfEasyLinkRxTxFnx, &rxtxTaskParams, NULL);
}


void SwithtoTx(uint8_t* packet, uint8_t len)
{
  TxSendFlag = TRUE;
  //System_printf("SwithtoTx enter\n");
    /* Create packet with incrementing sequence number and random payload */
    txPacket.payload[0] = (uint8_t)(seqNumber >> 8);
    txPacket.payload[1] = (uint8_t)(seqNumber++);
    uint8_t i;
    for (i = 0; i < len; i++)
    {
      txPacket.payload[i+2] = packet[i];
    }

    txPacket.len = len+2;
    txPacket.dstAddr[0] = 0xaa;

    /* Add a Tx delay for > 500ms, so that the abort kicks in and brakes the burst */
    if(txBurstSize++ >= RFEASYLINKTX_BURST_SIZE)
    {
      /* Set Tx absolute time to current time + 1s */
      txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(500);
      txBurstSize = 0;
    }
    /* Else set the next packet in burst to Tx in 100ms */
    else
    {
      /* Set Tx absolute time to current time + 100ms */
      txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(50);
    }


    if(EasyLink_abort() == EasyLink_Status_Success)
    {
       /* Wait for the abort */
//         Semaphore_pend(rxDoneSem, BIOS_WAIT_FOREVER);  
        EasyLink_Status result = EasyLink_transmit(&txPacket);
          

        if (result == EasyLink_Status_Success)
        {
            /* Toggle LED1 to indicate TX */
         PIN_setOutputValue(pinHandle, Board_LED3,!PIN_getOutputValue(Board_LED3));
        }
        else
        {
            /* Toggle LED1 and LED2 to indicate error */
          PIN_setOutputValue(pinHandle, Board_LED3,!PIN_getOutputValue(Board_LED3));
          PIN_setOutputValue(pinHandle, Board_LED4,!PIN_getOutputValue(Board_LED4));
        }
        
        Semaphore_post(txDoneSem);
     }
     TxSendFlag = FALSE;
    
  //System_printf("SwithtoTx exit\n");
}

/*
 *  ======== main ========
 */
int main(void)
{
  
    /* Call board init functions. */
    Board_initGeneral();

    /* Open LED pins */
    ledPinHandle = PIN_open(&ledPinState, pinTable);
    if(!ledPinHandle) {
        System_abort("Error initializing board LED pins\n");
    }

    //add by barbara -- begin
        /* Open Button pins */
    buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
    if (!buttonPinHandle) {
        System_abort("Error initializing button pins\n");
    }
    //add by barbara -- end
    //System_printf("main\n");
    
    /* Clear LED pins */
    PIN_setOutputValue(ledPinHandle, Board_LED1, 0);
    PIN_setOutputValue(ledPinHandle, Board_LED2, 0);

    rxTask_init(ledPinHandle);

    UartTask_init();			//add by barbara
    
    /* Start BIOS */
    BIOS_start();

    return (0);
}
