/*----------------------------------------------------------------------------
 * Tencent is pleased to support the open source community by making TencentOS
 * available.
 *
 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 * If you have downloaded a copy of the TencentOS binary from Tencent, please
 * note that the TencentOS binary is licensed under the BSD 3-Clause License.
 *
 * If you have downloaded a copy of the TencentOS source code from Tencent,
 * please note that TencentOS source code is licensed under the BSD 3-Clause
 * License, except for the third-party components listed below which are
 * subject to different license terms. Your integration of TencentOS into your
 * own projects may require compliance with the BSD 3-Clause License, as well
 * as the other licenses applicable to the third-party components included
 * within TencentOS.
 *---------------------------------------------------------------------------*/

#include "tos_k.h"

__API__ k_err_t tos_knl_init(void)
{
    k_err_t err;

    cpu_init();

    readyqueue_init();

#if TOS_CFG_MMHEAP_EN > 0
#if TOS_CFG_MMHEAP_DEFAULT_POOL_EN > 0u
    err = mmheap_init_with_pool(k_mmheap_default_pool, TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE);
#else
    err = mmheap_init();
#endif
    if (err != K_ERR_NONE) {
        return err;
    }
#endif

    err = knl_idle_init();
    if (err != K_ERR_NONE) {
        return err;
    }

#if TOS_CFG_TIMER_EN > 0
    err = timer_init();
    if (err != K_ERR_NONE) {
        return err;
    }
#endif

#if TOS_CFG_PWR_MGR_EN > 0U
    pm_init();
#endif

#if TOS_CFG_TICKLESS_EN > 0u
    tickless_init();
#endif

    return K_ERR_NONE;
}

__API__ void tos_knl_irq_enter(void)
{
    if (!tos_knl_is_running()) {
        return;
    }

    if (unlikely(k_irq_nest_cnt >= K_NESTING_LIMIT_IRQ)) {
        return;
    }

    ++k_irq_nest_cnt;
}

__API__ void tos_knl_irq_leave(void)
{
    TOS_CPU_CPSR_ALLOC();

    if (!tos_knl_is_running()) {
        return;
    }

    TOS_CPU_INT_DISABLE();
    if (!knl_is_inirq()) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    --k_irq_nest_cnt;

    if (knl_is_inirq()) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    if (knl_is_sched_locked()) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    k_next_task = readyqueue_highest_ready_task_get();
    if (knl_is_self(k_next_task)) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    cpu_irq_context_switch();
    TOS_CPU_INT_ENABLE();
}

__API__ k_err_t tos_knl_sched_lock(void)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_IN_IRQ_CHECK();

    if (!tos_knl_is_running()) {
        return K_ERR_KNL_NOT_RUNNING;
    }

    if (k_sched_lock_nest_cnt >= K_NESTING_LIMIT_SCHED_LOCK) {
        return K_ERR_LOCK_NESTING_OVERFLOW;
    }

    TOS_CPU_INT_DISABLE();
    ++k_sched_lock_nest_cnt;
    TOS_CPU_INT_ENABLE();
    return K_ERR_NONE;
}

__API__ k_err_t tos_knl_sched_unlock(void)
{
    TOS_CPU_CPSR_ALLOC();

    TOS_IN_IRQ_CHECK();

    if (!tos_knl_is_running()) {
        return K_ERR_KNL_NOT_RUNNING;
    }

    if (!knl_is_sched_locked()) {
        return K_ERR_SCHED_NOT_LOCKED;
    }

    TOS_CPU_INT_DISABLE();
    --k_sched_lock_nest_cnt;
    TOS_CPU_INT_ENABLE();

    knl_sched();
    return K_ERR_NONE;
}

__API__ k_err_t tos_knl_start(void)
{
    if (unlikely(tos_knl_is_running())) {
        return K_ERR_KNL_RUNNING;
    }

    k_next_task = readyqueue_highest_ready_task_get();
    k_curr_task = k_next_task;
    k_knl_state = KNL_STATE_RUNNING;

    cpu_sched_start();

    return K_ERR_NONE;
}

__API__ int tos_knl_is_running(void)
{
    return k_knl_state == KNL_STATE_RUNNING;
}

#if TOS_CFG_TICKLESS_EN > 0u

/**
 * @brief Get the remain ticks of the first oncoming task.
 *
 * @return The remian ticks of the first oncoming task to be scheduled.
 */
__KNL__ k_tick_t knl_next_expires_get(void)
{
    k_tick_t tick_next_expires;
#if TOS_CFG_TIMER_EN > 0u
    k_tick_t timer_next_expires;
#endif

    tick_next_expires = tick_next_expires_get();

#if TOS_CFG_TIMER_EN > 0u
    timer_next_expires = timer_next_expires_get();
#endif

#if TOS_CFG_TIMER_EN > 0u
    return tick_next_expires < timer_next_expires ? tick_next_expires : timer_next_expires;
#else
    return tick_next_expires;
#endif
}

#endif

__KNL__ void knl_sched(void)
{
    TOS_CPU_CPSR_ALLOC();

    if (unlikely(!tos_knl_is_running())) {
        return;
    }

    if (knl_is_inirq()) {
        return;
    }

    if (knl_is_sched_locked()) {
        return;
    }

    TOS_CPU_INT_DISABLE();
    k_next_task = readyqueue_highest_ready_task_get();
    if (knl_is_self(k_next_task)) {
        TOS_CPU_INT_ENABLE();
        return;
    }

    cpu_context_switch();
    TOS_CPU_INT_ENABLE();
}

__KNL__ int knl_is_sched_locked(void)
{
    return k_sched_lock_nest_cnt > 0u;
}

__KNL__ int knl_is_inirq(void)
{
    return k_irq_nest_cnt > 0u;
}

__KNL__ int knl_is_idle(k_task_t *task)
{
    return task == &k_idle_task;
}

__KNL__ int knl_is_self(k_task_t *task)
{
    return task == k_curr_task;
}

__STATIC__ void knl_idle_entry(void *arg)
{
    arg = arg; // make compiler happy

    while (K_TRUE) {
#if TOS_CFG_TASK_DYNAMIC_CREATE_EN > 0u
        task_free_all();
#endif

#if TOS_CFG_PWR_MGR_EN > 0u
        pm_power_manager();
#endif
    }
}

__KNL__ k_err_t knl_idle_init(void)
{
    return tos_task_create(&k_idle_task, "idle",
            knl_idle_entry, K_NULL,
            K_TASK_PRIO_IDLE,
            k_idle_task_stk_addr,
            k_idle_task_stk_size,
            0);
}

