/*----------------------------------------------------------------------------
 * 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"

#if TOS_CFG_MAIL_QUEUE_EN > 0u

__API__ k_err_t tos_mail_q_create(k_mail_q_t *mail_q, void *pool, size_t mail_cnt, size_t mail_size)
{
    k_err_t err;

    TOS_PTR_SANITY_CHECK(mail_q);

    err = tos_ring_q_create(&mail_q->ring_q, pool, mail_cnt, mail_size);
    if (err != K_ERR_NONE) {
        return err;
    }

    pend_object_init(&mail_q->pend_obj);

    TOS_OBJ_INIT(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);
    knl_object_alloc_set_static(&mail_q->knl_obj);

    return K_ERR_NONE;
}

__API__ k_err_t tos_mail_q_destroy(k_mail_q_t *mail_q)
{
    TOS_CPU_CPSR_ALLOC();
    k_err_t err;

    TOS_PTR_SANITY_CHECK(mail_q);
    TOS_OBJ_VERIFY(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);

    if (!knl_object_alloc_is_static(&mail_q->knl_obj)) {
        return K_ERR_OBJ_INVALID_ALLOC_TYPE;
    }

    TOS_CPU_INT_DISABLE();

    err = tos_ring_q_destroy(&mail_q->ring_q);
    if (err != K_ERR_NONE) {
        TOS_CPU_INT_ENABLE();
        return err;
    }

    pend_wakeup_all(&mail_q->pend_obj, PEND_STATE_DESTROY);

    pend_object_deinit(&mail_q->pend_obj);

    TOS_OBJ_DEINIT(mail_q);
    knl_object_alloc_reset(&mail_q->knl_obj);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

__API__ k_err_t tos_mail_q_create_dyn(k_mail_q_t *mail_q, size_t mail_cnt, size_t mail_size)
{
    k_err_t err;

    TOS_PTR_SANITY_CHECK(mail_q);

    err = tos_ring_q_create_dyn(&mail_q->ring_q, mail_cnt, mail_size);
    if (err != K_ERR_NONE) {
        return err;
    }

    pend_object_init(&mail_q->pend_obj);

    TOS_OBJ_INIT(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);
    knl_object_alloc_set_dynamic(&mail_q->knl_obj);

    return K_ERR_NONE;
}

__API__ k_err_t tos_mail_q_destroy_dyn(k_mail_q_t *mail_q)
{
    TOS_CPU_CPSR_ALLOC();
    k_err_t err;

    TOS_PTR_SANITY_CHECK(mail_q);
    TOS_OBJ_VERIFY(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);

    if (!knl_object_alloc_is_dynamic(&mail_q->knl_obj)) {
        return K_ERR_OBJ_INVALID_ALLOC_TYPE;
    }

    TOS_CPU_INT_DISABLE();

    err = tos_ring_q_destroy_dyn(&mail_q->ring_q);
    if (err != K_ERR_NONE) {
        TOS_CPU_INT_ENABLE();
        return err;
    }

    pend_wakeup_all(&mail_q->pend_obj, PEND_STATE_DESTROY);

    pend_object_deinit(&mail_q->pend_obj);

    TOS_OBJ_DEINIT(mail_q);
    knl_object_alloc_reset(&mail_q->knl_obj);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

__API__ k_err_t tos_mail_q_flush(k_mail_q_t *mail_q)
{
    TOS_PTR_SANITY_CHECK(mail_q);
    TOS_OBJ_VERIFY(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);

    return tos_ring_q_flush(&mail_q->ring_q);
}

__API__ k_err_t tos_mail_q_pend(k_mail_q_t *mail_q, void *mail_buf, size_t *mail_size, k_tick_t timeout)
{
    TOS_CPU_CPSR_ALLOC();
    k_err_t err;

    TOS_IN_IRQ_CHECK();
    TOS_PTR_SANITY_CHECK(mail_q);
    TOS_PTR_SANITY_CHECK(mail_buf);
    TOS_OBJ_VERIFY(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);

    TOS_CPU_INT_DISABLE();

    if (tos_ring_q_dequeue(&mail_q->ring_q, mail_buf, mail_size) == K_ERR_NONE) {
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (timeout == TOS_TIME_NOWAIT) {
        *mail_size = 0;
        TOS_CPU_INT_ENABLE();
        return K_ERR_PEND_NOWAIT;
    }

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

    k_curr_task->mail = mail_buf;
    pend_task_block(k_curr_task, &mail_q->pend_obj, timeout);

    TOS_CPU_INT_ENABLE();
    knl_sched();

    err = pend_state2errno(k_curr_task->pend_state);
    if (err == K_ERR_NONE) {
        *mail_size              = k_curr_task->mail_size;
        k_curr_task->mail       = K_NULL;
        k_curr_task->mail_size  = 0;
    }

    return err;
}

__STATIC__ void mail_task_recv(k_task_t *task, void *mail_buf, size_t mail_size)
{
    memcpy(task->mail, mail_buf, mail_size);
    task->mail_size = mail_size;
    pend_task_wakeup(task, PEND_STATE_POST);
}

__STATIC__ k_err_t mail_q_do_post(k_mail_q_t *mail_q, void *mail_buf, size_t mail_size, opt_post_t opt)
{
    TOS_CPU_CPSR_ALLOC();
    k_err_t err;
    k_task_t *task, *tmp;

    TOS_PTR_SANITY_CHECK(mail_q);
    TOS_PTR_SANITY_CHECK(mail_buf);
    TOS_OBJ_VERIFY(mail_q, KNL_OBJ_TYPE_MAIL_QUEUE);

    TOS_CPU_INT_DISABLE();

    if (pend_is_nopending(&mail_q->pend_obj)) {
        err = tos_ring_q_enqueue(&mail_q->ring_q, mail_buf, mail_size);
        if (err != K_ERR_NONE) {
            TOS_CPU_INT_ENABLE();
            return err;
        }
        TOS_CPU_INT_ENABLE();
        return K_ERR_NONE;
    }

    if (opt == OPT_POST_ONE) {
        mail_task_recv(TOS_LIST_FIRST_ENTRY(&mail_q->pend_obj.list, k_task_t, pend_list),
                            mail_buf, mail_size);
    } else { // OPT_POST_ALL
        TOS_LIST_FOR_EACH_ENTRY_SAFE(task, tmp, k_task_t, pend_list, &mail_q->pend_obj.list) {
            mail_task_recv(task, mail_buf, mail_size);
        }
    }

    TOS_CPU_INT_ENABLE();
    knl_sched();

    return K_ERR_NONE;
}

__API__ k_err_t tos_mail_q_post(k_mail_q_t *mail_q, void *mail_buf, size_t mail_size)
{
    return mail_q_do_post(mail_q, mail_buf, mail_size, OPT_POST_ONE);
}

__API__ k_err_t tos_mail_q_post_all(k_mail_q_t *mail_q, void *mail_buf, size_t mail_size)
{
    return mail_q_do_post(mail_q, mail_buf, mail_size, OPT_POST_ALL);
}

#endif

