/*
* FreeRTOS
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://aws.amazon.com/freertos
 * http://www.FreeRTOS.org
 */

/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

/* Demo includes */
#include "aws_demo.h"

/* AWS library includes. */
#include "iot_logging_task.h"
#include "aws_clientcredential.h"

/* Nordic BSP includes */
#include "bsp.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
#include "nrf_sdh.h"
#include "nrf_sdh_soc.h"
#include "nrf_sdh_ble.h"
#include "nrf_sdh_freertos.h"
#include "sensorsim.h"
#include "timers.h"
#include "app_timer.h"
#include "ble_conn_state.h"
#include "nrf_drv_clock.h"
#include "nrf_ble_gatt.h"
#include "nrf_ble_qwr.h"
#include "ble_conn_params.h"
#include "peer_manager.h"
#include "peer_manager_handler.h"
#include "bsp_btn_ble.h"
#include "app_uart.h"
#include "queue.h"

#include "iot_ble.h"
#include "iot_ble_numericComparison.h"
#include "iot_network_manager_private.h"
#include "SEGGER_RTT.h"
#include "iot_taskpool.h"
#if defined( UART_PRESENT )
    #include "nrf_uart.h"
#endif
#if defined( UARTE_PRESENT )
    #include "nrf_uarte.h"
#endif

/* clang-format off */

/* Logging Task Defines. */
#define mainLOGGING_MESSAGE_QUEUE_LENGTH    ( 15 )
#define mainLOGGING_TASK_STACK_SIZE         ( configMINIMAL_STACK_SIZE * 4 )

/* BLE Lib defines. */
#define mainBLE_SERVER_UUID                 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }

/* UART Buffer sizes. */
#define UART_TX_BUF_SIZE                    ( 256 )                                 /**< UART TX buffer size. */
#define UART_RX_BUF_SIZE                    ( 256 )                                 /**< UART RX buffer size. */

/* LED-associated defines */
#define LED_ONE                             BSP_LED_0_MASK
#define LED_TWO                             BSP_LED_1_MASK
#define LED_THREE                           BSP_LED_2_MASK
#define LED_FOUR                            BSP_LED_3_MASK
#define ALL_APP_LED                     \
    ( BSP_LED_0_MASK | BSP_LED_1_MASK | \
      BSP_LED_2_MASK | BSP_LED_3_MASK )                                        /**< Define used for simultaneous operation of all application LEDs. */
#define LED_BLINK_INTERVAL_MS               ( 300 )                            /**< LED blinking interval. */

SemaphoreHandle_t xUARTTxComplete;
QueueHandle_t UARTqueue = NULL;

/* clang-format on */

/**
 * @brief Application task startup hook for applications using Wi-Fi. If you are not
 * using Wi-Fi, then start network dependent applications in the vApplicationIPNetorkEventHook
 * function. If you are not using Wi-Fi, this hook can be disabled by setting
 * configUSE_DAEMON_TASK_STARTUP_HOOK to 0.
 */
void vApplicationDaemonTaskStartupHook( void );

/**
 * @brief Initializes the board.
 */
static void prvMiscInitialization( void );

/*-----------------------------------------------------------*/

void vUartWrite( uint8_t * pucData )
{
    uint32_t xErrCode;

    SEGGER_RTT_WriteString(0, pucData);
    for( uint32_t i = 0; i < configLOGGING_MAX_MESSAGE_LENGTH; i++ )
    {
        if(pucData[ i ] == 0)
        {
            break;
        }

        do
        {
            xErrCode = app_uart_put(pucData[ i ]);
            if(xErrCode == NRF_ERROR_NO_MEM)
            {
            xErrCode = 0;
            }
        } while( xErrCode == NRF_ERROR_BUSY );
        xSemaphoreTake(xUARTTxComplete, portMAX_DELAY );

    }
}

/*-----------------------------------------------------------*/

/**@brief Function for putting the chip into sleep mode.
 * /**@brief Function for putting the chip into sleep mode
 *
 * @note This function will not return.
 */
static void prvSleeModeEnter( void )
{
    ret_code_t xErrCode;

    xErrCode = bsp_indication_set( BSP_INDICATE_IDLE );
    APP_ERROR_CHECK( xErrCode );

    /* Prepare wakeup buttons. */
    xErrCode = bsp_btn_ble_sleep_mode_prepare();
    APP_ERROR_CHECK( xErrCode );

    /* Go to system-off mode (this function will not return; wakeup will cause a reset). */
    xErrCode = sd_power_system_off();
    APP_ERROR_CHECK( xErrCode );
}

/*-----------------------------------------------------------*/



/*-----------------------------------------------------------*/

/**@brief Function for handling a Connection Parameters error.
 *
 * @param[in]   nrf_error   Error code containing information about what went wrong.
 */
static void conn_params_error_handler( uint32_t nrf_error )
{
    APP_ERROR_HANDLER( nrf_error );
}

/*-----------------------------------------------------------*/

/**@brief   Function for handling uart events.
 *
 * /**@snippet [Handling the data received over UART] */

void prvUartEventHandler( app_uart_evt_t * pxEvent )
{
    /* Declared as static so it can be pushed into the queue from the ISR. */
    static volatile uint8_t ucRxByte = 0;
   BaseType_t xHigherPriorityTaskWoken;
    switch( pxEvent->evt_type )
    {
        case APP_UART_DATA_READY:
            app_uart_get( (uint8_t *)&ucRxByte );
            app_uart_put( ucRxByte );

            xQueueSendFromISR(UARTqueue, (void * )&ucRxByte, &xHigherPriorityTaskWoken);
            /* Now the buffer is empty we can switch context if necessary. */
            //portYIELD_FROM_ISR (xHigherPriorityTaskWoken);

            break;

        case APP_UART_COMMUNICATION_ERROR:
            APP_ERROR_HANDLER( pxEvent->data.error_communication );
            break;

        case APP_UART_FIFO_ERROR:
            APP_ERROR_HANDLER( pxEvent->data.error_code );
            break;

        case APP_UART_TX_EMPTY:
             xSemaphoreGiveFromISR(xUARTTxComplete, &xHigherPriorityTaskWoken);
            break;

        default:
            break;
    }
}

/*-----------------------------------------------------------*/

/**@brief Function for handling events from the BSP module.
 *
 * @param[in]   event   Event generated by button press.
 */
static void prvBSPEventHandler( bsp_event_t xEvent )
{
    ret_code_t xErrCode;
    const BTInterface_t * pxIface = BTGetBluetoothInterface();
    const BTBleAdapter_t * pxAdapter = pxIface->pxGetLeAdapter();

    switch( xEvent )
    {
        case BSP_EVENT_SLEEP:
            prvSleeModeEnter();
            break;

        case BSP_EVENT_DISCONNECT:

            pxAdapter->pxDisconnect( 0, NULL, 0 );

            break;

        default:
            break;
    }
}

/*-----------------------------------------------------------*/


/**@brief  Function for initializing the UART module.
 */
static void prvUartInit( void )
{
    uint32_t xErrCode;

    app_uart_comm_params_t const xConnParams =
    {
        .rx_pin_no        = RX_PIN_NUMBER,
        .tx_pin_no        = TX_PIN_NUMBER,
        .rts_pin_no       = RTS_PIN_NUMBER,
        .cts_pin_no       = CTS_PIN_NUMBER,
        .flow_control     = APP_UART_FLOW_CONTROL_DISABLED,
        .use_parity       = false,
        #if defined( UART_PRESENT )
            .baud_rate    = NRF_UART_BAUDRATE_115200
        #else
               .baud_rate = NRF_UARTE_BAUDRATE_115200
                            #endif
    };

    APP_UART_FIFO_INIT( &xConnParams,
                        UART_RX_BUF_SIZE,
                        UART_TX_BUF_SIZE,
                        prvUartEventHandler,
                        _PRIO_APP_HIGH,
                        xErrCode );
    APP_ERROR_CHECK( xErrCode );
}

/*-----------------------------------------------------------*/

/**@brief Function for initializing the nrf log module.
 */
static void prvLogInit( void )
{
    ret_code_t xErrCode = NRF_LOG_INIT( NULL );

    APP_ERROR_CHECK( xErrCode );

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}

/*-----------------------------------------------------------*/

/**@brief Function for initializing the clock.
 */
static void prvClockInit( void )
{
    ret_code_t xErrCode = nrf_drv_clock_init();

    APP_ERROR_CHECK( xErrCode );
}

/*-----------------------------------------------------------*/

/**@brief Function for the Timer initialization.
 *
 * @details Initializes the timer module. This creates and starts application timers.
 */
static void prvTimersInit( void )
{
    /* Initialize timer module. */
    ret_code_t xErrCode = app_timer_init();

    APP_ERROR_CHECK( xErrCode );
}

/*-----------------------------------------------------------*/

static void prvMiscInitialization( void )
{
    /* Initialize modules.*/
    xUARTTxComplete = xSemaphoreCreateBinary();
    prvUartInit();
    prvClockInit();

    /* Activate deep sleep mode. */
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    prvTimersInit();
    UARTqueue = xQueueCreate( 1, sizeof( uint8_t ) );
}
/*-----------------------------------------------------------*/

/**
 * @brief Application runtime entry point.
 */
int main( void )
{
    /* Perform any hardware initialization that does not require the RTOS to be
     * running.  */
    prvMiscInitialization();

    xLoggingTaskInitialize( mainLOGGING_TASK_STACK_SIZE,
                            tskIDLE_PRIORITY,
                            mainLOGGING_MESSAGE_QUEUE_LENGTH );

    nrf_sdh_freertos_init( NULL, NULL );
    ret_code_t xErrCode = pm_init();
    vTaskStartScheduler();

    return 0;
}
/*-----------------------------------------------------------*/
int32_t xPortGetUserInput( uint8_t * pMessage,
                           uint32_t messageLength,
                           TickType_t timeoutTicks )
{
      int32_t bytesRead = 0;
      configASSERT((messageLength == 1 ));
      if (xQueueReceive(UARTqueue, (void * )pMessage, (portTickType) timeoutTicks ) == pdTRUE )
      {
          bytesRead = messageLength;
      }

      return bytesRead;
}
/**@brief Clear bond information from persistent storage. */
static void prvDeleteBonds( void )
{
    ret_code_t xErrCode;

    NRF_LOG_INFO( "Erase bonds!" );

    xErrCode = pm_peers_delete();
    APP_ERROR_CHECK( xErrCode );
}

void vApplicationDaemonTaskStartupHook( void )
{
    /* Start the demo tasks. */
    DEMO_RUNNER_RunDemos();
}

/*-----------------------------------------------------------*/

/**
 * @brief User defined Idle task function.
 *
 * @note Do not make any blocking operations in this function.
 */
void vApplicationIdleHook( void )
{
    /* FIX ME. If necessary, update to application idle periodic actions. */

    static TickType_t xLastPrint = 0;
    TickType_t xTimeNow;
    const TickType_t xPrintFrequency = pdMS_TO_TICKS( 5000 );

    xTimeNow = xTaskGetTickCount();

    if( ( xTimeNow - xLastPrint ) > xPrintFrequency )
    {
        configPRINT( "." );
        xLastPrint = xTimeNow;
    }
}

/*-----------------------------------------------------------*/

/**
 * @brief User defined assertion call. This function is plugged into configASSERT.
 * See FreeRTOSConfig.h to define configASSERT to something different.
 */
void vAssertCalled( const char * pcFile,
                    uint32_t ulLine )
{
    /* FIX ME. If necessary, update to applicable assertion routine actions. */

    const uint32_t ulLongSleep = 1000UL;
    volatile uint32_t ulBlockVariable = 0UL;
    volatile char * pcFileName = ( volatile char * ) pcFile;
    volatile uint32_t ulLineNumber = ulLine;

    ( void ) pcFileName;
    ( void ) ulLineNumber;

    printf( "vAssertCalled %s, %ld\n", pcFile, ( long ) ulLine );
    fflush( stdout );

    /* Setting ulBlockVariable to a non-zero value in the debugger will allow
     * this function to be exited. */
    taskDISABLE_INTERRUPTS();
    {
        while( ulBlockVariable == 0UL )
        {
            vTaskDelay( pdMS_TO_TICKS( ulLongSleep ) );
        }
    }
    taskENABLE_INTERRUPTS();
}

/*-----------------------------------------------------------*/

/**
 * @brief User defined application error handler required by NRF52SDK
 */
void app_error_handler( uint32_t error_code,
                        uint32_t line_num,
                        const uint8_t * p_file_name )
{
}