/**
* \file  main.c
*
* \brief Gatewayless LoRa Peer to Peer Demo Application
*
* Copyright (c) 2021 Europalab Devices ApS
*
* \asf_license_start
*
* \page License
*
* This file is part of Sendcomm.
*
* Sendcomm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Sendcomm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Sendcomm. If not, see <https://www.gnu.org/licenses/>.
*
* \asf_license_stop
*
*/

/****************************** Includes **************************************/
#include <asf.h>
#include "sio2host.h"
#include "stdio_serial.h"
#include "radio_interface.h"
#include "radio_driver_hal.h"
#include "aes_engine.h"
#include "sw_timer.h"
#include "system_init.h"
#include "lorawan.h"

/************************** Macro definition ***********************************/
#define APP_DEBOUNCE_TIME   50  // button debounce time in ms
#define BUFFER_SIZE         50

/************************** Global variables ***********************************/
uint8_t buttonPressed = 0 ;
uint8_t buttonCounter = 0 ;
uint8_t buffer[BUFFER_SIZE] ;

/****************************** Prototypes *************************************/
static void init(void) ;
static void configure_led(void) ;
static void configure_extint(void) ;
static void configure_radio(void) ;
static void configure_eic_callback(void) ;
static void extint_callback(void) ;
void print_menu(void) ;
void serial_data_handler(void) ;
void radio_transmit_uplink(uint8_t *data, uint16_t len) ;
void radio_enter_receive_mode(void) ;
void radio_exit_receive_mode(void) ;
SYSTEM_TaskStatus_t APP_TaskHandler(void) ;
void appData_callback(void *appHandle, appCbParams_t *appdata) ;
void print_array(uint8_t *array, uint8_t length) ;

/****************************** Functions *************************************/
static void init(void)
{
    system_init() ;
    delay_init() ;
    board_init() ;
    configure_led() ;
    sio2host_init() ;
    INTERRUPT_GlobalInterruptEnable() ;
    // LoRaWAN Stack driver init
    HAL_RadioInit() ;
    AESInit() ;
    SystemTimerInit() ;
    Stack_Init() ;
    LORAWAN_Init(appData_callback, NULL) ;
}

void print_menu(void)
{
    printf("\r\n-- Sendcomm LoRa P2P Sample Application (NNN MHz) --\r\n") ;
    printf("- Press user (white) button to transmit a counter [%d]\r\n", buttonCounter) ;
    printf("- Type any character to transmit over LoRa Radio\r\n") ;
}

int main (void)
{
    init();
    configure_led();
    configure_extint();
    configure_eic_callback();

    configure_radio();
    radio_enter_receive_mode();

    print_menu();
    while (1)
    {
        serial_data_handler();
        SYSTEM_RunTasks();
    }
}

static void configure_led(void)
{
    struct port_config pin_conf ;
    port_get_config_defaults(&pin_conf) ;
    pin_conf.direction  = PORT_PIN_DIR_OUTPUT ;
    port_pin_set_config(LED_0_PIN, &pin_conf) ;
    port_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE) ;
}

static void configure_extint(void)
{
    struct extint_chan_conf eint_chan_conf;
    extint_chan_get_config_defaults(&eint_chan_conf);
    eint_chan_conf.gpio_pin           = BUTTON_0_EIC_PIN;
    eint_chan_conf.gpio_pin_mux       = BUTTON_0_EIC_MUX;
    eint_chan_conf.detection_criteria = EXTINT_DETECT_FALLING;
    eint_chan_conf.filter_input_signal = true;
    extint_chan_set_config(BUTTON_0_EIC_LINE, &eint_chan_conf);
}

static void configure_eic_callback(void)
{
    extint_register_callback(
        extint_callback,
        BUTTON_0_EIC_LINE,
        EXTINT_CALLBACK_TYPE_DETECT
    );
    extint_chan_enable_callback(BUTTON_0_EIC_LINE, EXTINT_CALLBACK_TYPE_DETECT);
}

static void extint_callback(void)
{
    /* Read the button level */
    if (port_pin_get_input_level(BUTTON_0_PIN) == BUTTON_0_ACTIVE)
    {
        /* Wait for button debounce time */
        delay_ms(APP_DEBOUNCE_TIME);
        /* Check whether button is in default state */
        while(port_pin_get_input_level(BUTTON_0_PIN) == BUTTON_0_ACTIVE)
        {
            delay_ms(500);
        }
        buttonPressed = true;
        /* Post task to application handler on button press */
        SYSTEM_PostTask(APP_TASK_ID);
    }
}

SYSTEM_TaskStatus_t APP_TaskHandler(void)
{
    if (buttonPressed == true)
    {
        buttonPressed = false ;
        buttonCounter++ ;
        if (buttonCounter > 255) buttonCounter = 0 ;
        printf("Button pressed %d times\r\n", buttonCounter) ;

        // Exit receive mode
        radio_exit_receive_mode() ;

        // Prepare and transmit buttonCounter
        buffer[0] = buttonCounter ;
        radio_transmit_uplink(buffer, 1) ;
    }
    return SYSTEM_TASK_SUCCESS;
}

void serial_data_handler(void)
{
    int rxChar ;
    char serialData ;
    /* Verify if there was any character received*/
    if((-1) != (rxChar = sio2host_getchar_nowait()))
    {
        serialData = (char)rxChar;
        if((serialData != '\r') && (serialData != '\n') && (serialData != '\b'))
        {
            printf("\r\n") ;
            // Exit receive mode
            radio_exit_receive_mode() ;
            // Prepare and transmit character received
            buffer[0] = serialData ;
            radio_transmit_uplink(buffer, 1) ;
        }
    }
}

/* Configure LoRa Radio for P2P */
static void configure_radio(void)
{
#if true
KRASH_ME  // Forces attention placed on a hard coded frequency risk
    // Remember to replace text output banner (NNN MHz) in main
    LORAWAN_Reset(ISM_EU868) ;  // Refer to stack_common.h for the definition of IsmBand_t
    // Cmd mac reset 868 - reset the LoRaWAN stack and initialize it with the parameters of the selected ISM band
    LORAWAN_Reset(ISM_EU868) ;  // Refer to stack_common.h for the definition of IsmBand_t
#endif  // #if true

    // Cmd mac pause
    uint32_t time_ms = LORAWAN_Pause() ;
    printf("MAC Pause %ld\r\n", time_ms) ;

    // Set some radio parameters manually, and others parameters are following the selected ISM band

    // Cmd radio set pwr 15
    int16_t outputPwr = 15 ;
    RADIO_SetAttr(OUTPUT_POWER,(void *)&outputPwr) ;
    printf("Configuring Radio Output Power %d\r\n", outputPwr) ;

    // Cmd radio set sf sf12
    int16_t sf = SF_12 ;
    RADIO_SetAttr(SPREADING_FACTOR,(void *)&sf) ;
    printf("Configuring Radio SF %d\r\n", sf) ;

    // Cmd radio set wdt 60000
    uint32_t wdt = 60000 ;
    RADIO_SetAttr(WATCHDOG_TIMEOUT,(void *)&wdt) ;
    printf("Configuring Radio Watch Dog Timeout %ld\r\n", wdt) ;
}

/* Transmit LoRa Radio Uplink */
void radio_transmit_uplink(uint8_t *data, uint16_t len)
{
    printf("[Transmit Uplink] ") ;
    print_array(data, len) ;
    RadioError_t radioStatus ;
    RadioTransmitParam_t RadioTransmitParam ;
    RadioTransmitParam.bufferLen = len ;
    RadioTransmitParam.bufferPtr = data ;
    radioStatus = RADIO_Transmit(&RadioTransmitParam) ;
    switch(radioStatus)
    {
        case ERR_NONE:
            printf("Radio Transmit Success \r\n") ;
            print_menu() ;
            break;
        case ERR_DATA_SIZE:
            // Do nothing (NOP), status already set to invalid
            break;
        default:
            printf("Radio busy \r\n") ;
    }
}

/* LoRa Radio enter into Receive Mode */
void radio_enter_receive_mode(void)
{
    RadioReceiveParam_t radioReceiveParam ;
    uint32_t rxTimeout = 0 ;  // Forever
    radioReceiveParam.action = RECEIVE_START ;
    radioReceiveParam.rxWindowSize = rxTimeout ;
    if (RADIO_Receive(&radioReceiveParam) == 0)
    {
        printf("Radio in Receive mode\r\n") ;
    }
}

/* LoRa Radio exit from Receive Mode */
void radio_exit_receive_mode(void)
{
    RadioReceiveParam_t radioReceiveParam ;
    radioReceiveParam.action = RECEIVE_STOP ;
    if (RADIO_Receive(&radioReceiveParam) == 0)
    {
        printf("Radio Exit Receive mode\r\n") ;
    }
}

/* Uplink/Downlink Callback */
void appData_callback(void *appHandle, appCbParams_t *appdata)
{
    StackRetStatus_t status = LORAWAN_INVALID_REQUEST ;

    if (appdata->evt == LORAWAN_EVT_RX_DATA_AVAILABLE)
    {
        // Downlink Event
        status = appdata->param.rxData.status ;
        switch(status)
        {
            case LORAWAN_RADIO_SUCCESS:
            {
                uint8_t dataLength = appdata->param.rxData.dataLength ;
                uint8_t *pData = appdata->param.rxData.pData ;
                if((dataLength > 0U) && (NULL != pData))
                {
                    int8_t rssi_value, snr_value ;
                    RADIO_GetAttr(PACKET_RSSI_VALUE, &rssi_value) ;
                    RADIO_GetAttr(PACKET_SNR, &snr_value) ;

                    printf(">> Payload received: ") ;
                    print_array(pData, dataLength) ;
                    printf("RSSI Value: %d\r\n", rssi_value) ;
                    printf("SNR Value: %d", snr_value) ;
                    printf("\r\n*******************\r\n") ;

                    LED_On(LED_0_PIN) ;
                    delay_ms(50) ;
                    LED_Off(LED_0_PIN) ;

                    radio_enter_receive_mode() ;
                    print_menu() ;
                }
            }
            break;
            case LORAWAN_RADIO_NO_DATA:
            {
                printf("\n\rRADIO_NO_DATA \n\r");
            }
            break;
            case LORAWAN_RADIO_DATA_SIZE:
                printf("\n\rRADIO_DATA_SIZE \n\r");
            break;
            case LORAWAN_RADIO_INVALID_REQ:
                printf("\n\rRADIO_INVALID_REQ \n\r");
            break;
            case LORAWAN_RADIO_BUSY:
                printf("\n\rRADIO_BUSY \n\r");
            break;
            case LORAWAN_RADIO_OUT_OF_RANGE:
                printf("\n\rRADIO_OUT_OF_RANGE \n\r");
            break;
            case LORAWAN_RADIO_UNSUPPORTED_ATTR:
                printf("\n\rRADIO_UNSUPPORTED_ATTR \n\r");
            break;
            case LORAWAN_RADIO_CHANNEL_BUSY:
                printf("\n\rRADIO_CHANNEL_BUSY \n\r");
            break;
            case LORAWAN_INVALID_PARAMETER:
                printf("\n\rINVALID_PARAMETER \n\r");
            break;
            default:
                printf("UNKNOWN ERROR %d\r\n", status) ;
            break;
        }
    }
    else if(appdata->evt == LORAWAN_EVT_TRANSACTION_COMPLETE)
    {
        // Uplink event
        switch(status = appdata->param.transCmpl.status)
        {
            case LORAWAN_SUCCESS:
            case LORAWAN_RADIO_SUCCESS:
                printf("Transmission success\r\n") ;
                radio_enter_receive_mode() ;
                break;
            case LORAWAN_RADIO_NO_DATA:
                printf("\r\nRADIO_NO_DATA\r\n") ;
                break;
            case LORAWAN_RADIO_BUSY:
                printf("\r\nRADIO_BUSY\r\n") ;
                break;
            default:
                break;
        }
    }
}

/*********************************************************************//*
 \brief      Function to Print array of characters
 \param[in]  *array  - Pointer of the array to be printed
 \param[in]   length - Length of the array
 ************************************************************************/
void print_array(uint8_t *array, uint8_t length)
{
    printf("0x") ;
    for (uint8_t i = 0; i < length; i++)
    {
        printf("%02x", *array) ;
        array++ ;
    }
    printf("\n\r") ;
}
