/**
 ******************************************************************************
 * Copyright (c) 2025 Nordic Semiconductor ASA
 * Filename : main.c
 * Date(DD-MM-YYYY)        Modification        Name
 * ----------------        ------------        ----
 *    08-02-2025             Created            JF
 *******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <dk_buttons_and_leds.h>
#include <zephyr/sys/ring_buffer.h>
#include <zephyr/logging/log.h>
#if defined(CONFIG_CLOCK_CONTROL_NRF)
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#if defined(NRF54L15_XXAA)
#include <hal/nrf_clock.h>
#endif /* defined(NRF54L15_XXAA) */
#endif
#include <stdio.h>
#include <stdarg.h>

#include <nrfx_power.h>

/* Macro ---------------------------------------------------------------------*/
#define LOG_MODULE_NAME uart
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

#define RING_BUF_SIZE 256
#define RUN_STATUS_LED DK_LED1
#define RX_STATUS_LED DK_LED2
//#define USE_HF_CLK
//#define PM_MEASURE

/* Private typedef -----------------------------------------------------------*/
typedef struct uart_int_driven_s {
    struct ring_buf tx_ringbuf;
    struct ring_buf rx_ringbuf;
    uint8_t tx_buf[RING_BUF_SIZE];
	uint8_t rx_buf[RING_BUF_SIZE];
    atomic_t tx_busy;
    atomic_t rx_enable;
}uart_int_driven_t;

/* Private function prototypes------------------------------------------------*/
#ifdef USE_HF_CLK
static int clocks_start(void);
static int clocks_stop(void);
#endif
static void button_callback(uint32_t button_state, uint32_t has_changed);
static void uart_cb(const struct device *dev, void *user_data);
static int uart_send(const uint8_t * buf, size_t len);
static int print(char *string, ...);
static int uart_rx_start(void);
static void rx_on_off_toggle(void);
static void pm_update(void);

/* Global variables ----------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static const struct device *uart = DEVICE_DT_GET(DT_ALIAS(user_uart));
static uart_int_driven_t uart_int_driven;
static char uart_Hello_Message[] = "Hello from UART\r\n";
static K_SEM_DEFINE(uart_sem, 0, 1);

/* Macro ---------------------------------------------------------------------*/

/* Exported functions --------------------------------------------------------*/
int main(void)
{
    int err;
    uint32_t len;
    uint8_t *data;
    //nrfx_power_constlat_mode_request();
 
    err = dk_leds_init();
    if (err)
    {
        LOG_ERR("LEDs init failed (err %d)", err);
        return err;
    }

    if (!device_is_ready(uart))
    {
        return -ENODEV;
    }

#ifdef PM_MEASURE
//suspend log uart
    pm_device_action_run(DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), PM_DEVICE_ACTION_SUSPEND);
#else
    dk_set_led_on(RUN_STATUS_LED);
#endif
    pm_update();
    ring_buf_init(&uart_int_driven.tx_ringbuf, RING_BUF_SIZE, uart_int_driven.tx_buf);
    ring_buf_init(&uart_int_driven.rx_ringbuf, RING_BUF_SIZE, uart_int_driven.rx_buf);

    uart_irq_callback_user_data_set(uart, uart_cb, NULL); 

    rx_on_off_toggle();

    print("\r\n*********************************************\r\n");
    print("UART INT-DRIVEN sample\r\n");
    print("*********************************************\r\n");
    print("Press Button 1 to send Hello message\r\n");
    print("Press Button 2 to toggle UART RX\r\n");
    LOG_INF("UART INT-DRIVEN sample");
    LOG_INF("This console is for log, please connect to user UART port");

    dk_buttons_init(button_callback);

    while(1)
    {
        k_sem_take(&uart_sem, K_FOREVER);
        do
        {
            len = ring_buf_get_claim(&uart_int_driven.rx_ringbuf, (uint8_t **)&data, uart_int_driven.rx_ringbuf.size);
            if(len > 0)
            {
                //USER DATA PROCESS PUT HERE
                uart_send(data, len);
                //END
                int err = ring_buf_get_finish(&uart_int_driven.rx_ringbuf, len);
                if (err)
                {
                    LOG_ERR("ring buf get finish failed");
                }
            }
        } while (len > 0);
    }
    return 0;
}

/* Overrride functions -------------------------------------------------------*/

/* Private Functions ---------------------------------------------------------*/
static void button_callback(uint32_t button_state, uint32_t has_changed)
{
    uint32_t buttons = button_state & has_changed;

    if (buttons & DK_BTN1_MSK) 
    {
        print(uart_Hello_Message);
    }

    if (buttons & DK_BTN2_MSK) 
    {
        rx_on_off_toggle();
    }
}
#ifdef USE_HF_CLK
static int clocks_start(void)
{
    int err;
    int res;
    struct onoff_manager *clk_mgr;
    struct onoff_client clk_cli;

    clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    if (!clk_mgr) 
    {
        LOG_ERR("Unable to get the Clock manager");
        return -ENXIO;
    }

    sys_notify_init_spinwait(&clk_cli.notify);

    err = onoff_request(clk_mgr, &clk_cli);
    if (err < 0) 
    {
        LOG_ERR("Clock request failed: %d", err);
        return err;
    }

    do {
        err = sys_notify_fetch_result(&clk_cli.notify, &res);
        if (!err && res) 
        {
            LOG_ERR("Clock could not be started: %d", res);
            return res;
        }
    } while (err);

#if defined(NRF54L15_XXAA)
    /* MLTPAN-20 */
    nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_PLLSTART);
#endif /* defined(NRF54L15_XXAA) */

    LOG_DBG("HF clock started");
    return 0;
}

static int clocks_stop(void)
{
    int ret;
    struct onoff_manager *clk_mgr;
    struct onoff_client clk_cli;
    clk_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
    __ASSERT_NO_MSG(mgr != NULL);
    ret = onoff_cancel_or_release(clk_mgr, &clk_cli);
    __ASSERT_NO_MSG(ret >= 0);
    LOG_DBG("HF clock stopped");
    return ret;
}
#endif

static void uart_cb(const struct device *dev, void *user_data)
{
    uint32_t len;
    uint8_t *data;
    int32_t err;
    uint8_t rd_byte;

    uart_irq_update(dev);

    if (uart_irq_rx_ready(dev)) 
    {
        while ( uart_fifo_read(dev, &rd_byte, 1) )
        {
            len = ring_buf_put(&uart_int_driven.rx_ringbuf, &rd_byte, 1);
            if(len == 0)
            {
                LOG_ERR("RX overflow: ring buf full");
            }
        }
        k_sem_give(&uart_sem);
    }

    if (uart_irq_tx_ready(dev))
    {
        len = ring_buf_get_claim(&uart_int_driven.tx_ringbuf, (uint8_t **)&data, uart_int_driven.tx_ringbuf.size);
        if (len > 0)
        {
            len = uart_fifo_fill(dev, data, len);
            if (len <= 0) 
            {
                LOG_ERR("uart_fifo_fill failed");
            }
            else
            {
                err = ring_buf_get_finish(&uart_int_driven.tx_ringbuf, len);
                if (err)
                {
                    LOG_ERR("ring buf get finish failed");
                }
            }
        } 
        else 
        {
            uart_irq_tx_disable(dev);
            atomic_clear(&uart_int_driven.tx_busy);
            pm_update();
        }
    }
}

static int uart_send(const uint8_t * buf, size_t len)
{
    uint32_t count;
    if(len < 0)
    {
        return 0;
    }
    count = ring_buf_put(&uart_int_driven.tx_ringbuf, buf, len);
    if(count != 0)
    {
        if (atomic_set(&(uart_int_driven.tx_busy), 1) == 0)
        {
            pm_update();
            uart_irq_tx_enable(uart);
        }
        if(count!= len)
        {
            LOG_WRN("data losing due to tx ring buffer is full");
        }
    }
    return 0;
}

static int print(char *string, ...)
{
    char buffer[256]; 
    int n;
    va_list args;      
    va_start(args, string); 
    n = vsnprintf(buffer, sizeof(buffer), string, args);
    va_end(args); 

    if(n > sizeof(buffer))
    {
        n = sizeof(buffer);
    }
    
    if(n > 0)
    {
        return uart_send((uint8_t *)buffer, n);
    }
    else
    {
        return -ENOMEM;
    }
}

static int uart_rx_start(void)
{
    if(atomic_get(&uart_int_driven.rx_enable) == 1)
    {
        LOG_INF("rx has already enabled");
        return 0;
    }
    LOG_INF("rx enabled");
    atomic_set(&uart_int_driven.rx_enable, 1);
    pm_update();
#ifdef USE_HF_CLK
    clocks_start();
#endif
    uart_irq_rx_enable(uart);    
    return 0;
}

static int uart_rx_stop(void)
{
    if(atomic_get(&uart_int_driven.rx_enable) == 0)
    {
        LOG_INF("rx has already disabled");
        return 0;
    }
    LOG_INF("rx disabled");
    atomic_clear(&uart_int_driven.rx_enable);
    pm_update();
#ifdef USE_HF_CLK
    clocks_stop();
#endif
    uart_irq_rx_disable(uart);
    return 0;
}


static void rx_on_off_toggle(void)
{
    if(atomic_get(&uart_int_driven.rx_enable) == 0)
    {
        uart_rx_start();
#ifndef PM_MEASURE
        dk_set_led_on(RX_STATUS_LED);
#endif
    }
    else
    {
        uart_rx_stop();
#ifndef PM_MEASURE
        dk_set_led_off(RX_STATUS_LED);
#endif
    }
}

static void pm_update(void)
{
    if(atomic_get(&uart_int_driven.rx_enable)||atomic_get(&uart_int_driven.tx_busy))
    {
        pm_device_action_run(uart, PM_DEVICE_ACTION_RESUME);
    }
    else
    {
        pm_device_action_run(uart, PM_DEVICE_ACTION_SUSPEND);
    }
}
/* Macro ---------------------------------------------------------------------*/

/* End of File */
