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

#include "simple_hal.h"
#include "nrf_mesh_config_examples.h"

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#include "nrf.h"
#include "nrf_error.h"
#include "boards.h"
#include "nrf_delay.h"

#include "nrf_mesh_defines.h"
#include "timer.h"
#include "app_timer.h"
#include "app_error.h"
#include "nrfx_gpiote.h"

/*****************************************************************************
 * Definitions
 *****************************************************************************/

/*****************************************************************************
 * Static variables
 *****************************************************************************/

#if BUTTON_BOARD
static uint8_t m_buttons_list[BUTTONS_NUMBER] = BUTTONS_LIST;
static timestamp_t m_last_button_press;
static hal_button_handler_cb_t m_button_handler_cb;
#endif

#if SIMPLE_HAL_LEDS_ENABLED
APP_TIMER_DEF(m_blink_timer);
static uint32_t m_blink_count;
static uint32_t m_blink_mask;
static uint32_t m_prev_state;
static const uint8_t m_leds_list[LEDS_NUMBER] = LEDS_LIST;
#endif

static void button_event_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
    for (uint32_t i = 0; i < ARRAY_SIZE(m_buttons_list); i++)
    {
        if (pin == m_buttons_list[i])
        {
            /* Check that the event was generated by a button press, and reject if it's too soon (debounce).
             */
            if (TIMER_DIFF(m_last_button_press, timer_now()) > HAL_BUTTON_PRESS_DEBOUNCE_INTERVAL)
            {
                m_last_button_press = timer_now();
                m_button_handler_cb(i);
            }
        }
    }
}

static uint32_t hal_led_mask_state_get(uint32_t led_mask)
{
    uint32_t state = 0;
    for (uint32_t i = 0; i < ARRAY_SIZE(m_leds_list); ++i)
    {
        if (led_mask & (1 << i))
        {
            state = state | (hal_led_pin_get(m_leds_list[i]) << i);
        }
    }
    return state;
}

static void hal_led_mask_toggle(uint32_t led_mask)
{
    for (uint32_t i = 0; i < ARRAY_SIZE(m_leds_list); i++)
    {
        if (led_mask & (1 << i))
        {
            hal_led_pin_set(m_leds_list[i], !hal_led_pin_get(m_leds_list[i]));
        }
    }
}

/*****************************************************************************
 * Public API
 *****************************************************************************/

#if SIMPLE_HAL_LEDS_ENABLED
static void led_timeout_handler(void * p_context)
{
    APP_ERROR_CHECK_BOOL(m_blink_count > 0);
    hal_led_mask_toggle(m_blink_mask);

    m_blink_count--;
    if (m_blink_count == 0)
    {
        (void) app_timer_stop(m_blink_timer);
        for (uint32_t i = 0; i < ARRAY_SIZE(m_leds_list); i++)
        {
            if (m_blink_mask & (1 << i))
            {
                hal_led_pin_set(m_leds_list[i], ((m_prev_state >> i) & 1));
            }
        }
    }
}

/** Returns @c true if the led at pin_no is on. */
bool hal_led_pin_get(uint32_t pin)
{
    /* If pin_no is set _low_ (0) the led is on. */
    return (nrf_gpio_pin_out_read(pin) == 0);
}

void hal_led_pin_set(uint32_t pin, bool value)
{
    /* If pin_no is set _low_ (0) the led is on. */
    if (value)
    {
        nrfx_gpiote_out_clear(pin);
    }
    else
    {
        nrfx_gpiote_out_set(pin);
    }
}

void hal_led_mask_set(uint32_t led_mask, bool value)
{
    for (uint32_t i = 0; i < ARRAY_SIZE(m_leds_list); i++)
    {
        if (led_mask & (1 << i))
        {
            hal_led_pin_set(m_leds_list[i], value);
        }
    }
}

void hal_led_blink_ms(uint32_t led_mask, uint32_t delay_ms, uint32_t blink_count)
{
    if (blink_count == 0 || delay_ms < HAL_LED_BLINK_PERIOD_MIN_MS)
    {
        return;
    }

    m_blink_mask  = led_mask;
    m_blink_count = blink_count * 2 - 1;
    m_prev_state = hal_led_mask_state_get(m_blink_mask);

    if (app_timer_start(m_blink_timer, APP_TIMER_TICKS(delay_ms), NULL) == NRF_SUCCESS)
    {
        /* Start by "clearing" the mask, i.e., turn the LEDs on -- in case a user calls the
         * function twice. */
        hal_led_mask_set(m_blink_mask, LED_MASK_STATE_ON);
    }
}

void hal_led_blink_stop(void)
{
    (void) app_timer_stop(m_blink_timer);
    hal_led_mask_set(m_blink_mask, LED_MASK_STATE_OFF);
}

void hal_leds_init(void)
{
    if (!nrfx_gpiote_is_init())
    {
        APP_ERROR_CHECK(nrfx_gpiote_init());
    }
    nrfx_gpiote_out_config_t out_config = {.init_state = NRF_GPIOTE_INITIAL_VALUE_HIGH, .task_pin = false};
    for (uint32_t i = 0; i < ARRAY_SIZE(m_leds_list); i++)
    {
        APP_ERROR_CHECK(nrfx_gpiote_out_init(m_leds_list[i], &out_config));
    }
    APP_ERROR_CHECK(app_timer_create(&m_blink_timer, APP_TIMER_MODE_REPEATED, led_timeout_handler));
}
#endif /* SIMPLE_HAL_LEDS_ENABLED */

uint32_t hal_buttons_init(hal_button_handler_cb_t cb)
{
#if !BUTTON_BOARD
    return NRF_ERROR_NOT_SUPPORTED;
#else
    if (cb == NULL)
    {
        return NRF_ERROR_NULL;
    }

    if (GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS < BUTTONS_NUMBER)
    {
        return NRF_ERROR_NOT_SUPPORTED;
    }
    m_button_handler_cb = cb;

    if (!nrfx_gpiote_is_init())
    {
        APP_ERROR_CHECK(nrfx_gpiote_init());
    }

    nrfx_gpiote_in_config_t in_config = {
                                            .sense = NRF_GPIOTE_POLARITY_HITOLO,
                                            .pull = BUTTON_PULL,
                                            .is_watcher = false,
                                            .hi_accuracy = false,
                                            .skip_gpio_setup = false
                                        };
    for (uint32_t i = 0; i < BUTTONS_NUMBER; ++i)
    {
        APP_ERROR_CHECK(nrfx_gpiote_in_init(m_buttons_list[i], &in_config, button_event_handler));
        nrfx_gpiote_in_event_enable(m_buttons_list[i], true);
    }

    return NRF_SUCCESS;
#endif

}
