/*
 * Copyright (C) 2015-2018 Alibaba Group Holding Limited
 */
#include "iot_infra.h"
#if !defined(DM_MESSAGE_CACHE_DISABLED)
#include "iotx_dm_internal.h"

dm_msg_cache_ctx_t g_dm_msg_cache_ctx;

dm_msg_cache_ctx_t *_dm_msg_cache_get_ctx(void)
{
    return &g_dm_msg_cache_ctx;
}

static void _dm_msg_cache_mutex_lock(void)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    if (ctx->mutex) {
        HAL_MutexLock(ctx->mutex);
    }
}

static void _dm_msg_cache_mutex_unlock(void)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    if (ctx->mutex) {
        HAL_MutexUnlock(ctx->mutex);
    }
}

int dm_msg_cache_init(void)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();

    memset(ctx, 0, sizeof(dm_msg_cache_ctx_t));

    /* Create Mutex */
    ctx->mutex = HAL_MutexCreate();
    if (ctx->mutex == NULL) {
        return DM_MEMORY_NOT_ENOUGH;
    }

    /* Init Message Cache List */
    INIT_LIST_HEAD(&ctx->dmc_list);

    return SUCCESS_RETURN;
}

int dm_msg_cache_deinit(void)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    dm_msg_cache_node_t *node = NULL;
    dm_msg_cache_node_t *next = NULL;

    _dm_msg_cache_mutex_lock();
    list_for_each_entry_safe(node, next, &ctx->dmc_list, linked_list, dm_msg_cache_node_t) {
        list_del(&node->linked_list);
        if (node->data) {
            DM_free(node->data);
        }
        DM_free(node);
        _dm_msg_cache_mutex_unlock();
    }
    _dm_msg_cache_mutex_unlock();

    if (ctx->mutex) {
        HAL_MutexDestroy(ctx->mutex);
    }

    return SUCCESS_RETURN;
}

int dm_msg_cache_insert(int msgid, int devid, iotx_dm_event_types_t type, char *data)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    dm_msg_cache_node_t *node = NULL;

    dm_log_debug("dmc list size: %d", ctx->dmc_list_size);
    if (ctx->dmc_list_size >= CONFIG_MSGCACHE_QUEUE_MAXLEN) {
        return FAIL_RETURN;
    }

    node = DM_malloc(sizeof(dm_msg_cache_node_t));
    if (node == NULL) {
        return DM_MEMORY_NOT_ENOUGH;
    }
    memset(node, 0, sizeof(dm_msg_cache_node_t));

    node->msgid = msgid;
    node->devid = devid;
    node->response_type = type;
    node->data = data;
    node->ctime = HAL_UptimeMs();
    INIT_LIST_HEAD(&node->linked_list);

    _dm_msg_cache_mutex_lock();
    list_add_tail(&node->linked_list, &ctx->dmc_list);
    ctx->dmc_list_size++;
    _dm_msg_cache_mutex_unlock();

    return SUCCESS_RETURN;
}

int dm_msg_cache_search(_IN_ int msgid, _OU_ dm_msg_cache_node_t **node)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    dm_msg_cache_node_t *search_node = NULL;

    if (msgid <= 0 || node == NULL || *node != NULL) {
        return DM_INVALID_PARAMETER;
    }

    _dm_msg_cache_mutex_lock();
    list_for_each_entry(search_node, &ctx->dmc_list, linked_list, dm_msg_cache_node_t) {
        if (search_node->msgid == msgid) {
            *node = search_node;
            _dm_msg_cache_mutex_unlock();
            return SUCCESS_RETURN;
        }
    }

    _dm_msg_cache_mutex_unlock();
    return FAIL_RETURN;
}

int dm_msg_cache_remove(int msgid)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    dm_msg_cache_node_t *node = NULL;
    dm_msg_cache_node_t *next = NULL;

    _dm_msg_cache_mutex_lock();
    list_for_each_entry_safe(node, next, &ctx->dmc_list, linked_list, dm_msg_cache_node_t) {
        if (node->msgid == msgid) {
            list_del(&node->linked_list);
            if (node->data) {
                DM_free(node->data);
            }
            ctx->dmc_list_size--;
            DM_free(node);
            dm_log_debug("Remove Message ID: %d", msgid);
            _dm_msg_cache_mutex_unlock();
            return SUCCESS_RETURN;
        }
    }

    _dm_msg_cache_mutex_unlock();
    return FAIL_RETURN;
}

void dm_msg_cache_tick(void)
{
    dm_msg_cache_ctx_t *ctx = _dm_msg_cache_get_ctx();
    dm_msg_cache_node_t *node = NULL;
    dm_msg_cache_node_t *next = NULL;
    uint64_t current_time = HAL_UptimeMs();

    _dm_msg_cache_mutex_lock();
    list_for_each_entry_safe(node, next, &ctx->dmc_list, linked_list, dm_msg_cache_node_t) {
        if (current_time < node->ctime) {
            node->ctime = current_time;
        }
        if (current_time - node->ctime >= DM_MSG_CACHE_TIMEOUT_MS_DEFAULT) {
            dm_log_debug("Message ID Timeout: %d", node->msgid);
            /* Send Timeout Message To User */
            dm_msg_send_msg_timeout_to_user(node->msgid, node->devid, node->response_type);
            list_del(&node->linked_list);
            if (node->data) {
                DM_free(node->data);
            }
            DM_free(node);
        }
    }
    _dm_msg_cache_mutex_unlock();
}
#endif