/**
 * @file    uart.c
 * @brief   UART module
 * @author  Kenny Tran
 * @date    Jul 05 2023
 * @version 0.0.1
 *
 * @section LICENSES
 * Copyright (C) Morgan Advanced Programmable Systems, Inc.
 */


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

#include <zephyr/pm/device.h>
#include <zephyr/drivers/uart.h>

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

#include "uart.h"


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


/*--------------------------------------------------------------------------------------------------
 * TYPE DEFINITIONS, STRUCTURES & ENUMERATIONS
 *------------------------------------------------------------------------------------------------*/


/*--------------------------------------------------------------------------------------------------
 * PRIVATE CONSTANTS & VARIABLES
 *------------------------------------------------------------------------------------------------*/
static const struct device *_uart_dev                   = DEVICE_DT_GET(DT_NODELABEL(uart0));
static       uint8_t        _uart_rx_buf[UART_BUF_SIZE] = { 0 };
static       uint8_t        _uart_rx_ch;
static       bool           _uart_rx_rdy                = false;
static       bool           _uart_init                  = false;
static       bool           _uart_suspend               = false;


/*-------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION PROTOTYPES
 *-----------------------------------------------------------------------------------------------*/
static void _uart_cb (const struct device *dev, struct uart_event *evt, void *user_data);


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

    if (!device_is_ready (_uart_dev))
    {
        return ERRNO_NOT_READY;
    }

    if (uart_callback_set (_uart_dev, _uart_cb, NULL) != 0)
    {
        return ERRNO_UART_FAILURE;
    }

    if (uart_rx_enable(_uart_dev, _uart_rx_buf, UART_BUF_SIZE, UART_TIMEOUT_MS) != 0)
    {
        return ERRNO_UART_FAILURE;
    }

    k_busy_wait(100000);

    _uart_init = true;

    return ERRNO_SUCCESS;
}

bool UART_getc (uint8_t *ch)
{
    if (_uart_rx_rdy)
    {
        *ch = _uart_rx_ch;

        _uart_rx_rdy = false;

        return true;
    }

    return false;
}

ERRNO_e UART_suspend (void)
{
    if (!_uart_init)
    {
        return ERRNO_NOT_INITIALIZED;
    }

    _uart_suspend = true;

    return ERRNO_SUCCESS;
}

ERRNO_e UART_resume (void)
{
    if (!_uart_init)
    {
        return ERRNO_NOT_INITIALIZED;
    }

    if (pm_device_action_run (_uart_dev, PM_DEVICE_ACTION_RESUME) < 0)
    {
        PRINT (ERROR, "Failure to suspend UART.\r\n");
    }

    _uart_suspend = false;

    return ERRNO_SUCCESS;
}

/*-------------------------------------------------------------------------------------------------
 * PRIVATE FUNCTION DEFINITIONS
 *-----------------------------------------------------------------------------------------------*/
static void _uart_cb (const struct device *dev, struct uart_event *evt, void *user_data)
{
	switch (evt->type)
    {
        // case UART_TX_DONE:
        	// break;

        // case UART_TX_ABORTED:
        // 	break;
            
        case UART_RX_RDY:
            _uart_rx_ch = evt->data.rx.buf[evt->data.rx.offset];
            _uart_rx_rdy = true;
            break;

        // case UART_RX_BUF_REQUEST:
        // 	break;

        // case UART_RX_BUF_RELEASED:
        // 	break;
        
        /* Continuous reception is not enabled by default, which means once the receive buffer is full,
           you must manually enable reception. Inside the UART_RX_DISABLED case of the UART callback,
           you must re-enable UART to have continuous reception */
        case UART_RX_DISABLED:
            if (_uart_suspend)
            {
                /* Disable RX if it's enabled (important to avoid issues during suspend). */
                if (uart_rx_disable(_uart_dev) != 0)
                {
                    PRINT (ERROR, "Failure to disable UART RX.\r\n");
                }
                /* UART has finished and is disables, suspend. */
                if (pm_device_action_run (_uart_dev, PM_DEVICE_ACTION_SUSPEND) < 0)
                {
                    
                    PRINT (ERROR, "Failure to suspend UART.\r\n");
                }
            }
            else
            {
                uart_rx_enable (dev, _uart_rx_buf, UART_BUF_SIZE, UART_TIMEOUT_MS);
            }
         	break;

        // case UART_RX_STOPPED:
        // 	break;
            
        default:
            break;
	}
}
