/**
 * \file    sys_task.c
 * \brief   System task implementation
 * 
 * \par     Copyright (c) 2018 UWE Technologies Corp., Ltd. All Rights Reserved.
 * \author  James
 * \date    2018-03-30
 * \version 
 * 
 * \history 
 *  Date        Note
 *  2018-03-30  Original Creation
 * 
 */

#include "libc.h"
#include "drivers.h"

#include "cli_engine.h"
#include "sys_low_power.h"
#include "sys_task.h"

/**
 * Function pointer for the UART1 handler 
 */
volatile v_func_ptr_t g_sys_task_uart0_handler = NULL;

/**
 * Function pointer for the UART1 handler 
 */
volatile v_func_ptr_t g_sys_task_uart1_handler = NULL;

volatile u32_t g_sys_task_id_bitmap = 0;

#if (!SYS_FREERTOS_ENABLED)

#include "msq.h"
#include "sys_spi.h"
#include "sys_i2c.h"
#include "sys_uart1.h"

/**
 * array of function pointers which contains the task handlers
 */
volatile v_func_ptr_t g_sys_task_handlers[SYS_TASK_ID_MAX];

/**
 * array of function argument
 */
volatile u32_t g_sys_task_handler_args[SYS_TASK_ID_MAX];


/** \brief System task initialization
 *
 */
void sys_task_init(void)
{
    //Init message queue
    msq_init();

    //Init system task handlers
    g_sys_task_id_bitmap = 0;
    for (int i = 0; i < SYS_TASK_ID_MAX; i++)
    {
        g_sys_task_handlers[i] = NULL;
        g_sys_task_handler_args[i] = 0;
    }
}

/** \brief System task initialization
 *
 */
void sys_task_start_scheduler(void)
{
sys_tid_t task_id;
msq_t msq_data;

    /* Enable GPIO interrupt */
    drv_gpio_intr_init(TRUE);

#if LOW_POWER_ENABLED
    drv_chip_wkup_flg_clear();
#endif /* LOW_POWER_ENABLED */

    while(TRUE)
    {
        //drv_wdt_feed();
        msq_pop(&msq_data);
        switch (SAL_MSQ_TYPE(&msq_data)) {

        case SAL_MSQ_NULL:
            task_id = SYS_TASK_ID_MAX;
            do 
            {
                task_id--;
                if (SAL_IS_BIT_SET(g_sys_task_id_bitmap, task_id) && 
                    (g_sys_task_handlers[task_id])) 
                {
                    g_sys_task_handlers[task_id](g_sys_task_handler_args[task_id]);
                }
            } while (task_id > 0);
            sys_state_check_and_sleep();
#if ((SPI_MODULE_ENABLED)&&(SPI_MODULE_SLAVE_ENABLED)&&(SPI_MODULE_SLAVE_DMA_ENABLED))
            sys_spi_dma_handler();
#endif
            break;

#if ((I2C_MODULE_ENABLED)&&(I2C_MODULE_SLA_ENABLED))
        case SAL_MSQ_I2C_SLA:
            sys_i2c_handler(DRV_I2C_ID_0);
            sys_lpm_activate();
            break;
#endif
#if ((I2C_MODULE_ENABLED)&&(I2C_MODULE_SLA_ENABLED))
        case SAL_MSQ_I2C1_SLA:
            sys_i2c_handler(DRV_I2C_ID_1);
            sys_lpm_activate();
            break;
#endif

#if SPI_MODULE_SLAVE_ENABLED
        case SAL_MSQ_SPI_SLA:
            sys_spi_handler();
            sys_lpm_activate();
            break;
#endif /* SPI_MODULE_SLAVE_ENABLED */

        case SAL_MSQ_UART:
            sys_lpm_activate();
            if (drv_uart_second_get() == DRV_UART_ID_0)
            {
                sys_task_uart1_handler(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
                {
                    sys_task_uart1_handler(SAL_MSQ_VALUE2(&msq_data));
                }
                if (SAL_MSQ_VALUE3_VALID(&msq_data))
                {
                    sys_task_uart1_handler(SAL_MSQ_VALUE3(&msq_data));
                }
            }
            else {
#if CLI_ENABLED
                cli_do_work(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
                {
                    cli_do_work(SAL_MSQ_VALUE2(&msq_data));
                }
                if (SAL_MSQ_VALUE3_VALID(&msq_data))
                {
                    cli_do_work(SAL_MSQ_VALUE3(&msq_data));
                }
#else
                sys_task_uart0_handler(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
				{
                	sys_task_uart0_handler(SAL_MSQ_VALUE2(&msq_data));
				}
				if (SAL_MSQ_VALUE3_VALID(&msq_data))
				{
					sys_task_uart0_handler(SAL_MSQ_VALUE3(&msq_data));
				}
#endif
            }
            break;

        case SAL_MSQ_UART1:
            sys_lpm_activate();
            if (drv_uart_second_get() == DRV_UART_ID_1)
            {
                sys_task_uart1_handler(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
                {
                    sys_task_uart1_handler(SAL_MSQ_VALUE2(&msq_data));
                }
                if (SAL_MSQ_VALUE3_VALID(&msq_data))
                {
                    sys_task_uart1_handler(SAL_MSQ_VALUE3(&msq_data));
                }
            }
            else {
#if CLI_ENABLED
                cli_do_work(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
                {
                    cli_do_work(SAL_MSQ_VALUE2(&msq_data));
                }
                if (SAL_MSQ_VALUE3_VALID(&msq_data))
                {
                    cli_do_work(SAL_MSQ_VALUE3(&msq_data));
                }
#else
                sys_task_uart0_handler(SAL_MSQ_VALUE1(&msq_data));
                if (SAL_MSQ_VALUE2_VALID(&msq_data))
				{
                	sys_task_uart0_handler(SAL_MSQ_VALUE2(&msq_data));
				}
				if (SAL_MSQ_VALUE3_VALID(&msq_data))
				{
					sys_task_uart0_handler(SAL_MSQ_VALUE3(&msq_data));
				}
#endif
            }
            break;

        default:
            break;
        }
    }
}

/** \brief Create system task 
 *
 * \param intr_func     Task handler
 * \param arg           Task argument
 * \return              Task ID
 */
sys_tid_t sys_task_create(v_func_ptr_t intr_func, u32_t arg)
{
    int i = SYS_TASK_ID_MAX;

    if (!intr_func)
        return S_NULL_POINTER;

    do 
    {
        i--;
        if (!SAL_IS_BIT_SET(g_sys_task_id_bitmap, i)) 
        {
            g_sys_task_handlers[i] = intr_func;
            g_sys_task_handler_args[i] = arg;
            SAL_BIT_SET(g_sys_task_id_bitmap, i);
            return i;
        }
    } while (i > 0);
    return S_NO_RESOURCE;
}


/** \brief Delete system task 
 *
 * \param id            Task ID
 */
void sys_task_delete(sys_tid_t id)
{
    if ((id < 0) || (id >= SYS_TASK_ID_MAX))
        return;

    if (SAL_IS_BIT_SET(g_sys_task_id_bitmap, id)) 
    {
        SAL_BIT_CLEAR(g_sys_task_id_bitmap, id);
        g_sys_task_handlers[id] = NULL;
        g_sys_task_handler_args[id] = 0;
    }
}

#else /* SYS_FREERTOS_ENABLED */

/* Kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

/** \brief System task initialization
 *
 */
void sys_task_start_scheduler(void)
{
    /* Enable GPIO interrupt */
    drv_gpio_intr_init(TRUE);

#if LOW_POWER_ENABLED
    drv_chip_wkup_flg_clear();
#endif /* LOW_POWER_ENABLED */

    /* Start the scheduler. */
    vTaskStartScheduler();

    /* The scheduler should now be running, so we will only ever reach here if we
    ran out of heap space. */
    /* If all is well, the scheduler will now be running, and the following
    line will never be reached.  If the following line does execute, then there
    was insufficient FreeRTOS heap memory available for the idle and/or timer
    tasks to be created.  See the memory management section on the FreeRTOS web
    site, or the FreeRTOS tutorial books for more details. */
    for( ;; );
}

/** \brief Create system task 
 *
 * \param intr_func     Task handler
 * \param arg           Task argument
 * \return              Task ID
 */
sys_tid_t sys_task_create(v_func_ptr_t intr_func, u32_t arg)
{
    int i = SYS_TASK_ID_MAX;

    if (!intr_func)
        return S_NULL_POINTER;

    do 
    {
        i--;
        if (!SAL_IS_BIT_SET(g_sys_task_id_bitmap, i)) 
        {
            SAL_BIT_SET(g_sys_task_id_bitmap, i);
            return i;
        }
    } while (i > 0);
    return S_NO_RESOURCE;
}


/** \brief Delete system task 
 *
 * \param id            Task ID
 */
void sys_task_delete(sys_tid_t id)
{
    if ((id < 0) || (id >= SYS_TASK_ID_MAX))
        return;

    if (SAL_IS_BIT_SET(g_sys_task_id_bitmap, id)) 
    {
        SAL_BIT_CLEAR(g_sys_task_id_bitmap, id);
    }
}



#endif /* if (!SYS_FREERTOS_ENABLED) */

