/**
 * Copyright (c) 2012 - 2019, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, 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.
 *
 * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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.
 *
 */
/**
 * This project requires a device that runs the wban_gzll_device counterpart. Host listens
 * for a Packet (battery voltage) and sends an ACK (mode select) when a packet is received. 
 *
 */
#include "bsp.h"
#include "app_timer.h"
#include "app_button.h"
#include "app_error.h"
#include "nrf_gzll.h"
#include "nrf_gzll_error.h"
#include "mode_select_host.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"

APP_TIMER_DEF(m_battery_led_timer_id);                                /**< Battery LED indication timer instance. */

#define BATTERY_PRESENT_INTERVAL      APP_TIMER_TICKS(5000)           /**< Battery LED present interval (ticks). */
#define PIPE_NUMBER                   0                               /**< Pipe 0 is used for Gazell link. */
#define PACKET_LENGTH                 2                               /**< 2 bytes of payload length used when transmitting. */

static uint16_t                       batt_mV;                        /**< Placeholder for battery voltage in millivolts. */
static modes_wban_t                   actual_mode =  MODE_READY;      /**< Currently indicated state. */
static const char * indications_list[] = MODES_WBAN_LIST;             /**< List of all indicated states. */
static uint8_t                        m_data_payload[PACKET_LENGTH];  /**< Placeholder for data payload received from Device. */
static uint8_t                        m_ack_payload[PACKET_LENGTH];   /**< Placeholder for data payload to be sent to Device. */

static void button_event_handler(uint8_t pin_no, uint8_t button_action);

static const app_button_cfg_t app_buttons[BUTTONS_NUMBER] = 
{
    {BUTTON_UP, false, BUTTON_PULL, button_event_handler},
    {BUTTON_DN, false, BUTTON_PULL, button_event_handler},
};


/**@brief Function called when button is pressed.
 */
static void button_event_handler(uint8_t pin_no, uint8_t button_action)
{
    switch (pin_no)
    {
        case BUTTON_UP:
            if (button_action == APP_BUTTON_PUSH)
            {
                if (actual_mode != MODE_FIRST)
                    actual_mode--;
                else
                    actual_mode = MODE_MOT;

                NRF_LOG_INFO("%s\r\n", (uint32_t)indications_list[actual_mode]);
            }
            break;

        case BUTTON_DN:
            if (button_action == APP_BUTTON_PUSH)
            {
                if (actual_mode != MODE_MOT)
                    actual_mode++;
                else
                    actual_mode = MODE_FIRST;

                NRF_LOG_INFO("%s\r\n", (uint32_t)indications_list[actual_mode]);
            }
            break;

        default:
            return;
    }
}


/**@brief Function to display the battery State of Charge.
 *
 * @input Battery voltage in millivolts.
 */
void battery_led_present(uint16_t vbatt)
{  
    NRF_LOG_INFO("Battery Voltage Handler.\r\n");

    if (vbatt >= 3700)
    {
        bsp_board_led_on(BSP_BOARD_LED_2);
        bsp_board_led_on(BSP_BOARD_LED_3);
        bsp_board_led_on(BSP_BOARD_LED_4);
    }
    else if (vbatt > 3400)
    {
        bsp_board_led_on(BSP_BOARD_LED_2);
        bsp_board_led_on(BSP_BOARD_LED_3);
        bsp_board_led_off(BSP_BOARD_LED_4);
    }
    else
    {
        bsp_board_led_on(BSP_BOARD_LED_2);
        bsp_board_led_off(BSP_BOARD_LED_3);
        bsp_board_led_off(BSP_BOARD_LED_4);
    }
}


/**@brief Function to display the battery State of Charge.
 *
 * @input Battery voltage in millivolts.
 */
void battery_led_handler()
{
    battery_led_present(batt_mV);
}



/**@brief Function for initializing low frequency clock.
 */
void clock_init()
{
    NRF_CLOCK->LFCLKSRC            = (CLOCK_LFCLKSRC_SRC_Xtal << CLOCK_LFCLKSRC_SRC_Pos);
    NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
    NRF_CLOCK->TASKS_LFCLKSTART    = 1;

    while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0)
    {
        // Do nothing.
    }
}


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

    NRF_LOG_DEFAULT_BACKENDS_INIT();
}


/**@brief Initialize the BSP modules.
 */
static void leds_init(void)
{
    ret_code_t err_code;

    err_code = bsp_init(BSP_INIT_LEDS, NULL);
    APP_ERROR_CHECK(err_code);

    bsp_board_led_on(BSP_BOARD_LED_2);
    bsp_board_led_on(BSP_BOARD_LED_3);
    bsp_board_led_on(BSP_BOARD_LED_4);
}


/**@brief Initialize the BSP modules.
 */
static void apps_init(void)
{
    uint32_t err_code;

    // Initialize application timer.
    err_code = app_timer_init();
    APP_ERROR_CHECK(err_code);

    // Create battery LED present timer.
    err_code = app_timer_create(&m_battery_led_timer_id, APP_TIMER_MODE_REPEATED, battery_led_handler);
    APP_ERROR_CHECK(err_code);

    //Initialize app_button module, 25ms detection delay
    err_code = app_button_init((app_button_cfg_t *)app_buttons,
                                BUTTONS_NUMBER,
                                APP_TIMER_TICKS(25));
    APP_ERROR_CHECK(err_code);

    err_code = app_button_enable();
    APP_ERROR_CHECK(err_code);
}


/**@brief Initialize the Gazell protocol stack.
 */
static void gzll_init(void)
{
    bool result_value = nrf_gzll_init(NRF_GZLL_MODE_HOST);
    GAZELLE_ERROR_CODE_CHECK(result_value);

    nrf_gzll_set_base_address_0(0xE7E7E7E7);
}


/**@brief Start Gazell communication.
 */
static void gzll_start(void)
{
    bool result_value = nrf_gzll_enable();
    GAZELLE_ERROR_CODE_CHECK(result_value);
}


/**@brief Function for starting timers.
 */
static void application_timers_start(void)
{
    uint32_t err_code;
    err_code = app_timer_start(m_battery_led_timer_id, BATTERY_PRESENT_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
}


/**@brief   RX data ready callback.
 * @details If a data packet was received, battery voltage is saved and presented by LEDs. 
 *          Host acknowledges with mode number.
 */
void nrf_gzll_host_rx_data_ready(uint32_t pipe, nrf_gzll_host_rx_info_t rx_info)
{
    if (nrf_gzll_fetch_packet_from_rx_fifo(PIPE_NUMBER, m_data_payload, PACKET_LENGTH))
    {
        m_ack_payload[0] = actual_mode;

        bool res_value = nrf_gzll_add_packet_to_tx_fifo(PIPE_NUMBER, m_ack_payload, PACKET_LENGTH);
                
        if(!res_value)
        {
            NRF_LOG_INFO("Fetching ACK packed failed.\r\n");
        }

        batt_mV = ((uint16_t)m_data_payload[0] << 8) | m_data_payload[1];
                
        NRF_LOG_INFO("Battery Voltage In Millivolts: %d\r\n", batt_mV);
        NRF_LOG_FLUSH();
    }        
}


/**@brief Gazell callback required for Gazell to be initialized.
 */
void nrf_gzll_device_tx_success(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
{
}


/**@brief Gazell callback required for Gazell to be initialized.
 */
void nrf_gzll_device_tx_failed(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info)
{
}


/**@brief Gazell callback required for Gazell to be initialized.
 */
void nrf_gzll_disabled()
{
}


/**@brief Function for application main entry.
 */
int main(void)
{

    // Initialize.
    log_init();
    clock_init();
    apps_init();
    leds_init();
    gzll_init();

    // Start execution.
    NRF_LOG_INFO("WBAN application started: Host side.\r\n");
    gzll_start();
    application_timers_start();

    // Main loop.
    while (true)
    {
        NRF_LOG_FLUSH();
        __WFE();
    }
}