/** 
 * @file    button.c
 * @brief   Button module
 * @author  Kenny Tran
 * @date    Mar 18 2024
 * @version 0.0.1
 *
 * @section LICENSES
 * Copyright (C) Morgan Advanced Programmable Systems, Inc.
 */


/*--------------------------------------------------------------------------------------------------
 * INCLUDE FILES
 *------------------------------------------------------------------------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "../lib/errno.h"
#include "../lib/print.h"

#include "../wrappers/gpio.h"

#include "button.h"


/*--------------------------------------------------------------------------------------------------
 * TYPE DEFINITIONS & ENUMERATIONS
 *------------------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------------------
 * MACROS & CONSTANTS
 *------------------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static          bool     _button_init           = false;
static volatile uint32_t _button_left_pressed   = 0;
static volatile uint32_t _button_center_pressed = 0;
static volatile uint32_t _button_right_pressed  = 0;


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *------------------------------------------------------------------------------------------------*/
static void _button_left_isr (void);
static void _button_center_isr (void);
static void _button_right_isr (void);


/*--------------------------------------------------------------------------------------------------
 * PUBLIC FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
ERRNO_e BUTTON_init (void)
{
    if (_button_init)
    {
        return ERRNO_ALREADY_INITIALIZED;
    }

    if (GPIO_init () < ERRNO_SUCCESS)
    {
        return ERRNO_GPIO_FAILURE;
    }

    /* Configure buttons and ISR. */
    if (GPIO_cfg (GPIO_PIN_BUTTON_LEFT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_left_isr))
    {
        return ERRNO_GPIO_FAILURE;
    }

    if (GPIO_cfg (GPIO_PIN_BUTTON_CENTER, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_center_isr))
    {
        return ERRNO_GPIO_FAILURE;
    }

    if (GPIO_cfg (GPIO_PIN_BUTTON_RIGHT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_STATE_CHANGE_LL_HIGH, _button_right_isr))
    {
        return ERRNO_GPIO_FAILURE;
    }

    /* Set initialized flag. */
    _button_init = true;

    return ERRNO_SUCCESS;
}

ERRNO_e BUTTON_wake_on_press (BUTTON_e button)
{
    if (!_button_init)
    {
        return ERRNO_NOT_INITIALIZED;
    }

    /* Reconfigure GPIO interrupt for buttons to be used to wake from sleep. */
    switch (button)
    {
        case BUTTON_LEFT:
            if (GPIO_cfg (GPIO_PIN_BUTTON_LEFT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL))
            {
                return ERRNO_GPIO_FAILURE;
            }
            break;

        case BUTTON_CENTER:
            if (GPIO_cfg (GPIO_PIN_BUTTON_CENTER, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL))
            {
                return ERRNO_GPIO_FAILURE;
            }
            break;

        case BUTTON_RIGHT:
            if (GPIO_cfg (GPIO_PIN_BUTTON_RIGHT, GPIO_DIRECTION_IN, GPIO_PULLNONE, GPIO_INIT_LOW, GPIO_INT_LL_HIGH, NULL))
            {
                return ERRNO_GPIO_FAILURE;
            }
            break;

        default:
            return ERRNO_INVALID_PARAM;
    }

    return ERRNO_SUCCESS;
}

ERRNO_e BUTTON_is_pressed (BUTTON_e button, bool *p_pressed)
{
    if (!_button_init)
    {
        return ERRNO_NOT_INITIALIZED;
    }

    switch (button)
    {
        case BUTTON_LEFT:
            *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_LEFT);
            /* Reset flag. */
            _button_left_pressed = 0;
            break;

        case BUTTON_CENTER:
            *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_CENTER);
            /* Reset flag. */
            _button_center_pressed = 0;
            break;

        case BUTTON_RIGHT:
            *p_pressed = GPIO_level_get (GPIO_PIN_BUTTON_RIGHT);
            /* Reset flag. */
            _button_right_pressed = 0;
            break;

        default:
            *p_pressed = false;
            return ERRNO_INVALID_PARAM;
    }

    return ERRNO_SUCCESS;
}

ERRNO_e BUTTON_was_pressed (BUTTON_e button, bool *p_pressed)
{
    bool pressed = false;

    if (!_button_init)
    {
        return ERRNO_NOT_INITIALIZED;
    }

    switch (button)
    {
        case BUTTON_LEFT:
            pressed = (bool) _button_left_pressed;
            /* Reset flag. */
            _button_left_pressed = 0;
            break;

        case BUTTON_CENTER:
            pressed = (bool) _button_center_pressed;
            /* Reset flag. */
            _button_center_pressed = 0;
            break;

        case BUTTON_RIGHT:
            pressed = (bool) _button_right_pressed;
            /* Reset flag. */
            _button_right_pressed = 0;
            break;

        default:
            *p_pressed = false;
            return ERRNO_INVALID_PARAM;
    }

    *p_pressed = pressed;

    return ERRNO_SUCCESS;
}

void BUTTON_cli_cmd (int32_t argc, char **argv)
{
    if (argc == 0)
    {
        bool was_left;
        bool was_center;
        bool was_right;
        bool is_left;
        bool is_center;
        bool is_right;

        PRINT (INFO, "BUTTON\r\n");
        PRINT (INFO, "-------------------------------------------\r\n");
        PRINT (INFO, "ISR Count\r\n");
        PRINT (INFO, "  Left   : %d\r\n", _button_left_pressed);
        PRINT (INFO, "  Center : %d\r\n", _button_center_pressed);
        PRINT (INFO, "  Right  : %d\r\n", _button_right_pressed);

        (void) BUTTON_was_pressed (BUTTON_LEFT, &was_left);
        (void) BUTTON_was_pressed (BUTTON_CENTER, &was_center);
        (void) BUTTON_was_pressed (BUTTON_RIGHT, &was_right);
        (void) BUTTON_is_pressed (BUTTON_LEFT, &is_left);
        (void) BUTTON_is_pressed (BUTTON_CENTER, &is_center);
        (void) BUTTON_is_pressed (BUTTON_RIGHT, &is_right);
        
        PRINT (INFO, "was pressed\r\n");
        PRINT (INFO, "  Left   : %s\r\n", (was_left   ? "true" : "false"));
        PRINT (INFO, "  Center : %s\r\n", (was_center ? "true" : "false"));
        PRINT (INFO, "  Right  : %s\r\n", (was_right  ? "true" : "false"));
        PRINT (INFO, "is pressed\r\n");
        PRINT (INFO, "  Left   : %s\r\n", (is_left   ? "true" : "false"));
        PRINT (INFO, "  Center : %s\r\n", (is_center ? "true" : "false"));
        PRINT (INFO, "  Right  : %s\r\n", (is_right  ? "true" : "false"));
    }
    else if (argc == 1)
    {
        if (!strcmp (argv[0], "--init") || !strcmp (argv[0], "-i"))
        {
            PRINT (INFO, "Ret code : %d\r\n", BUTTON_init ());
        }
        else
        {
            goto USAGE;
        }
    }
    else
    {
USAGE:
        PRINT (INFO, "Usage: button [OPTION...]\r\n");
        PRINT (INFO, "  -i | --init          Initializes button module\r\n");
    }
}


/*--------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *------------------------------------------------------------------------------------------------*/
static void _button_left_isr (void)
{
    _button_left_pressed++;
}

static void _button_center_isr (void)
{
    _button_center_pressed++;
}

static void _button_right_isr (void)
{
    _button_right_pressed++;
}
