#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include "kmdw_console.h"
#include "project.h"

#include "cmsis_os2.h"
#include "kdrv_cmsis_core.h"

#define UART_RX_ECHO_GET_DBG 0

#define BACKSP_KEY 0x08
#define RETURN_KEY 0x0D
#define DELETE_KEY 0x7F
#define BELL 0x07

#define MAX_LOG_LENGTH (256) //14bytes are necessary amount structure information "sizeof(os_message_t)" for each msg element in rtx_msgqueue.c
#define MAX_LOG_LENGTH_MSGQ (MAX_LOG_LENGTH+14)
#define DDR_LOG_BUFFER_SIZE (1 * 1024 * 1024)
#define DDR_MAX_LOG_COUNT (DDR_LOG_BUFFER_SIZE/MAX_LOG_LENGTH_MSGQ)// if using DDR
static uint32_t scpu_debug_flags = 0;

static kdrv_uart_handle_t handle0 = MSG_PORT;
static osMessageQueueId_t log_msgq = NULL;

print_callback _print_callback = NULL;

#if (defined(UART_RX_ECHO_GET_DBG) && UART_RX_ECHO_GET_DBG == 1)
uint32_t buf_tmp[1024];
char buf_tmp_char[1024];
uint32_t uart_rx_cnt = 0;
uint32_t uart_rx_cnt_tmp = 0;
#endif

osEventFlagsId_t uart_console_evid;
osSemaphoreId_t uart_send_mutex;

__weak uint32_t osKernelIrqStatus(void)
{
    return false;
}

static void _print_to_uart(const char *str)
{
    osMutexAcquire(uart_send_mutex, osWaitForever);
    kdrv_uart_write(handle0, (uint8_t *)str, strlen(str));
    osMutexRelease(uart_send_mutex);
}

kmdw_status_t kmdw_printf(const char *fmt, ...)
{
    va_list arg_ptr;
    static char *buffer_s = NULL;

    if (NULL == buffer_s)
        buffer_s = (char *)malloc(sizeof(char) * MAX_LOG_LENGTH);

    sprintf(buffer_s, "[%.03f] ", (float)osKernelGetTickCount() / osKernelGetTickFreq());
    int pre_len = strlen(buffer_s);

    va_start(arg_ptr, fmt);
    vsnprintf(buffer_s + pre_len, MAX_LOG_LENGTH - 1, fmt, arg_ptr);
    va_end(arg_ptr);

    buffer_s[MAX_LOG_LENGTH - 1] = 0; // just in case

    if (log_msgq == NULL || osThreadGetId() == NULL)
        _print_to_uart(buffer_s);
    else
    {
        osStatus_t oss = osMessageQueuePut(log_msgq, buffer_s, NULL, 0);
        if (oss != osOK)
        {
            //_print_to_uart("[logger] enqueue log1 failed\n");
            return KMDW_STATUS_ERROR;
        }
    }

    return KMDW_STATUS_OK;
}

kmdw_status_t kmdw_level_printf(int level, const char *fmt, ...)
{
    static char *buffer_s = NULL;
    uint32_t lvl = kmdw_console_get_log_level_scpu();
    lvl >>= 16;

    if ((level == LOG_PROFILE && level == lvl) || (level > 0 && level <= lvl))
    {
        va_list arg_ptr;

        if (NULL == buffer_s)
            buffer_s = (char *)malloc(sizeof(char) * MAX_LOG_LENGTH);

        sprintf(buffer_s, "[%.03f] ", (float)osKernelGetTickCount() / osKernelGetTickFreq());
        int pre_len = strlen(buffer_s);

        va_start(arg_ptr, fmt);
        vsnprintf(buffer_s + pre_len, MAX_LOG_LENGTH - 1, fmt, arg_ptr);
        va_end(arg_ptr);

        buffer_s[MAX_LOG_LENGTH - 1] = 0; // just in case

        if (log_msgq == NULL || osThreadGetId() == NULL)
            _print_to_uart(buffer_s);
        else
        {
            osStatus_t oss = osMessageQueuePut(log_msgq, buffer_s, NULL, 0);
            if (oss != osOK)
            {
                //_print_to_uart("[logger] enqueue log failed\n");
                return KMDW_STATUS_ERROR;
            }
        }
    }

    return KMDW_STATUS_OK;
}

void logger_thread(void *arg)
{
    uint8_t log[MAX_LOG_LENGTH];

    void *log_pool = (void *)kmdw_ddr_reserve(DDR_LOG_BUFFER_SIZE);

    if (log_pool)
    {
        osMessageQueueAttr_t msgq_attr;
        memset(&msgq_attr, 0, sizeof(msgq_attr));
        msgq_attr.mq_mem = log_pool;
        msgq_attr.mq_size = DDR_LOG_BUFFER_SIZE;
        memset(log_pool, 0, DDR_LOG_BUFFER_SIZE);

        log_msgq = osMessageQueueNew(DDR_MAX_LOG_COUNT, MAX_LOG_LENGTH, &msgq_attr);
        if (log_msgq == NULL)
        {
            printf("[logger] osMessageQueueNew failed\n");
        }
    }
    while (1)
    {
        osStatus_t oss = osMessageQueueGet(log_msgq, &log[0], NULL, osWaitForever);
        if (oss != osOK)
        {
            _print_to_uart("[logger] dequeue log failed\n");
            // if (_print_callback)
            //     _print_callback("[logger] dequeue log failed\n");
            continue;
        }

        _print_to_uart((const char *)log);

        if (_print_callback)
            _print_callback((const char *)log);
    }
}

__weak uint32_t kmdw_ddr_reserve(uint32_t numbyte)
{
    return 0;
}

void kmdw_console_hook_callback(print_callback print_cb)
{
    _print_callback = print_cb;
}

char kmdw_console_getc(void)
{
    char c;
    kdrv_uart_read(handle0, (uint8_t *)&c, 1);
    return c;
}

void kmdw_console_putc(char Ch)
{
    char cc;

    if (Ch != '\0')
    {
        cc = Ch;
        kdrv_uart_write(handle0, (uint8_t *)&cc, 1);
    }

    if (Ch == '\n')
    {
        cc = '\r';
        kdrv_uart_write(handle0, (uint8_t *)&cc, 1);
    }
}

void kmdw_console_puts(char *str)
{
    char *cp;
    for (cp = str; *cp != 0; cp++)
        kmdw_console_putc(*cp);
}

int kmdw_console_echo_gets(char *buf, int len)
{
    char *cp;
    char data[MAX_FIFO_RX];
    uint32_t count;
    uint32_t exit_while = 0;
    count = 0;
    cp = buf;
    len = 1024;
    #if (defined(UART_RX_ECHO_GET_DBG) && UART_RX_ECHO_GET_DBG == 1)
    uart_rx_cnt=0;
    memset(buf_tmp, 0, sizeof(buf_tmp));
    memset(buf_tmp_char, 0xff, sizeof(buf_tmp_char));
    #endif
    do
    {
        memset(data, 0, MAX_FIFO_RX);
        kdrv_uart_get_char(handle0, data);
        //kdrv_uart_read(handle0, (uint8_t*)data, 1);
        for(uint32_t i = 0; i< gDrvCtx.uart_dev[handle0]->info.xfer.rx_cnt; i++)
        {

            #if (defined(UART_RX_ECHO_GET_DBG) && UART_RX_ECHO_GET_DBG == 1)
            buf_tmp[uart_rx_cnt] = uart_rx_cnt;
            buf_tmp_char[uart_rx_cnt] = data[i];
            uart_rx_cnt++;
            #endif
            if(data[i] == BACKSP_KEY || data[i] == DELETE_KEY)
            {
                if ((count > 0) && (count < len))
                {
                    count--;
                    *(--cp) = '\0';
                    //kmdw_console_puts("\b \b");
                }
                break;
            }
            if(data[i] == RETURN_KEY)
            {
                if (count < len)
                {
                    *cp = '\0';
                    kmdw_console_putc('\n');
                }
                exit_while = 1;
                break;
            }
            else
            {
                if(count < len)
                {
                    *cp = (char)data[i];
                    cp++;
                    count++;
                    kmdw_console_putc(data[i]);
                }
            }
        }
    } while (exit_while == 0);
    #if (defined(UART_RX_ECHO_GET_DBG) && UART_RX_ECHO_GET_DBG == 1)
    uart_rx_cnt_tmp = uart_rx_cnt;
    #endif
    return (count);
}

__weak void kdrv_ncpu_set_scpu_debug_lvl(uint32_t lvl)
{
}

__weak void kdrv_ncpu_set_ncpu_debug_lvl(uint32_t lvl)
{
}

void kmdw_console_set_log_level_scpu(uint32_t level)
{
    scpu_debug_flags = (scpu_debug_flags & ~0x000F0000) | (((level) << 16) & 0x000F0000);
    kdrv_ncpu_set_scpu_debug_lvl(level);
}

uint32_t kmdw_console_get_log_level_scpu(void)
{
    return scpu_debug_flags;
}

void kmdw_console_set_log_level_ncpu(uint32_t level)
{
    kdrv_ncpu_set_ncpu_debug_lvl(level);
}

void kmdw_console_callback(uint32_t event)
{

    if(osKernelGetState() == osKernelRunning && uart_console_evid != NULL)
    {
        if (event & UART_RX_DONE)
        {
            osEventFlagsSet(uart_console_evid, UART_RX_DONE);
        }
        if (event & UART_TX_DONE)
        {
            osEventFlagsSet(uart_console_evid, UART_TX_DONE);
        }
        if (event & UART_RX_TIMEOUT)
        {
            osEventFlagsSet(uart_console_evid, UART_RX_DONE);
        }
    }
    if (event & UART_REVEIVE_COMPLETE)
    {
        kmdw_console_wait_rx_done(MSG_PORT);
    };
    if (event & UART_TRANSFER_COMPLETE)
    {
        kmdw_console_wait_tx_done(MSG_PORT);
    };
}

void kmdw_console_wait_rx_done(kdrv_uart_handle_t handle)
{
    if(osKernelGetState() == osKernelRunning && uart_console_evid != NULL)
    {
        int32_t evt_flg = osEventFlagsWait(uart_console_evid, UART_RX_DONE, osFlagsWaitAny , osWaitForever);
    }
    else
    {
        while((uart_get_status((DRVUART_PORT)handle) & SERIAL_LSR_DR) != SERIAL_LSR_DR)
        {
            if(osKernelIrqStatus() == false)
                __WFI();
            else
                __NOP();
        }
    }
}

void kmdw_console_wait_tx_done(kdrv_uart_handle_t handle)
{
    if(osKernelIrqStatus() == true)
    {
        while((uart_get_status((DRVUART_PORT)handle) & SERIAL_LSR_THRE) != SERIAL_LSR_THRE)
        {
            __NOP();
        }
        gDrvCtx.uart_dev[handle0]->info.status.tx_busy = 0;
    }
    else if(osKernelGetState() == osKernelRunning && uart_console_evid != NULL)
    {
        int32_t evt_flg = osEventFlagsWait(uart_console_evid, UART_TX_DONE, osFlagsWaitAny , osWaitForever);
    }
    else
    {
        while((uart_get_status((DRVUART_PORT)handle) & SERIAL_LSR_THRE) != SERIAL_LSR_THRE)
        {
            __WFI();
        }
    }
}

kmdw_status_t kmdw_uart_console_init(uint8_t uart_dev, uint32_t baudrate)
{
    kdrv_status_t sts = kdrv_uart_console_init(uart_dev, baudrate, kmdw_console_callback);//NULL);//

    if (sts != KDRV_STATUS_OK)
        return KMDW_STATUS_ERROR;

    uart_send_mutex = osMutexNew(NULL); // for uart send usage
    uart_console_evid  = osEventFlagsNew(0);

    if(uart_console_evid == NULL)
        return KMDW_STATUS_ERROR;

    return KMDW_STATUS_OK;
}


kmdw_status_t kmdw_uart_uninitialize(void)
{
    return KMDW_STATUS_OK;
}
