/**
 *******************************************************************************
 * @file  cli_cmd.c
 * @brief This file provides firmware functions to manage the CLI.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-06-30       CDT             First version
 @endverbatim
 *******************************************************************************
 * Copyright (C) 2022-2023, Xiaohua Semiconductor Co., Ltd. All rights reserved.
 *
 * This software component is licensed by XHSC under BSD 3-Clause license
 * (the "License"); You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at:
 *                    opensource.org/licenses/BSD-3-Clause
 *
 *******************************************************************************
 */

/*******************************************************************************
 * Include files
 ******************************************************************************/
#include "cli_cmd.h"
#include "console_slist.h"

/**
 * @addtogroup CLI_Console
 * @{
 */

/**
 * @defgroup CLI_Command CLI Command
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/
/**
 * @defgroup CLI_Command_Local_Types CLI Command Local Types
 * @{
 */

/**
 * @brief CLI command structure definition
 */
typedef struct {
    stc_slist_t   stcNode;

    stc_cli_cmd_t *pstcCmd;
} stc_cli_cmd_node_t;

/**
 * @}
 */

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/

/*******************************************************************************
 * Global variable definitions (declared in header file with 'extern')
 ******************************************************************************/

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/
/**
 * @defgroup CLI_Command_Local_Variables CLI Command Local Variables
 * @{
 */
static stc_slist_t m_stcCliCmdContainer;
/**
 * @}
 */

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup CLI_Command_Global_Functions CLI Command Global Functions
 * @{
 */

/**
 * @brief  Register command.
 * @param  [in] pstcCmd             Pointer to a @ref stc_cli_cmd_t structure
 * @retval int32_t:
 *           - OPCODE_OK:           Register successfully
 *           - OPCODE_ERR:          If one of following cases matches:
 *                                  - The command have been registered
 *                                  - Memory allocate unsuccessfully
 *                                  - The pointer pstcCmd value is NULL
 */
int32_t CLI_RegisterCmd(stc_cli_cmd_t *pstcCmd)
{
    int32_t i32Ret = OPCODE_ERR;
    stc_cli_cmd_node_t *pstcCmdNode;

    if (pstcCmd != NULL) {
        if (NULL == CLI_FindCmd(pstcCmd->pcName)) {
            pstcCmdNode = (stc_cli_cmd_node_t *)malloc(sizeof(stc_cli_cmd_node_t));
            if (pstcCmdNode != NULL) {
                INIT_SLIST_HEAD(&pstcCmdNode->stcNode);
                slist_add_tail(&m_stcCliCmdContainer, &pstcCmdNode->stcNode);
                pstcCmdNode->pstcCmd = pstcCmd;
                i32Ret = OPCODE_OK;
            }
        }
    }

    return i32Ret;
}

/**
 * @brief  Register commands.
 * @param  [in] astcCmdTable        Command array to a @ref stc_cli_cmd_t structure
 * @param  [in] u32CmdNum           Command array number
 * @retval int32_t:
 *           - OPCODE_OK:           Register successfully.
 *           - OPCODE_ERR:          If one of following cases matches:
 *                                  - The pointer astcCmdTable value is NULL.
 *                                  - The u32CmdNum value is 0.
 */
int32_t CLI_RegisterCmdTable(stc_cli_cmd_t astcCmdTable[], uint32_t u32CmdNum)
{
    uint32_t i;
    int32_t i32Ret = OPCODE_ERR;

    if ((astcCmdTable != NULL) && (0UL != u32CmdNum)) {
        for (i = 0UL; i < u32CmdNum; i++) {
            i32Ret = CLI_RegisterCmd(&astcCmdTable[i]);
            if (OPCODE_OK != i32Ret) {
                i32Ret = OPCODE_ERR;
                break;
            }
        }
    }

    return i32Ret;
}

/**
 * @brief  Find command.
 * @param  [in] pcName              Command name
 * @retval Pointer to a @ref stc_cli_cmd_t structure.
 */
stc_cli_cmd_t *CLI_FindCmd(const char *pcName)
{
    stc_slist_t *pstcPos;
    stc_cli_cmd_t *pstcCmdTmp;
    stc_cli_cmd_t *pstcCmd = NULL;

    if (pcName != NULL) {
        slist_for_each(pstcPos, &m_stcCliCmdContainer) {
            pstcCmdTmp = ((stc_cli_cmd_node_t *)pstcPos)->pstcCmd;
            if (0 == strcmp(pstcCmdTmp->pcName, pcName)) {
                pstcCmd = pstcCmdTmp;
                break;
            }
        }
    }

    return pstcCmd;
}

/**
 * @brief  Run command.
 * @param  [in] pcName              Command name
 * @param  [in] argc                Argument number
 * @param  [in] argv                Array of arguments
 * @retval int32_t:
 *           - OPCODE_OK:           Run successfully.
 *           - OPCODE_ERR:          If one of following cases matches:
 *                                  - The pointer name value is NULL.
 *                                  - The argc value is less than 1.
 *                                  - The pointer argv value is NULL.
 */
int32_t CLI_RunCmd(char *pcName, int32_t argc, char *argv[])
{
    stc_cli_cmd_t *pstcCmd;
    int32_t i32Ret = OPCODE_ERR;

    if ((pcName != NULL) && (argc > 0) && (argv != NULL)) {
        pstcCmd = CLI_FindCmd(pcName);
        if (NULL != pstcCmd) {
            i32Ret = pstcCmd->pfnCmd(argc, argv);
            if (i32Ret != OPCODE_OK) {
                (void)printf("Fail to execute command '%s' - check parameter validation \r\n", argv[0]);
                i32Ret = OPCODE_ERR;
            }
        } else {
            (void)printf("Unknown command '%s' - try 'help'\r\n", argv[0]);
        }
    }

    return i32Ret;
}

/**
 * @brief  Visit all command.
 * @param  [in] pfnVisit            The callback function of visiting command
 * @retval None
 */
void CLI_VisitEachCmd(int32_t (*pfnVisit)(stc_cli_cmd_t *pstcCmd))
{
    stc_slist_t *pstcPos;
    stc_cli_cmd_t *pstcCmd;

    if (NULL != pfnVisit) {
        slist_for_each(pstcPos, &m_stcCliCmdContainer) {
            pstcCmd = ((stc_cli_cmd_node_t *)pstcPos)->pstcCmd;
            if (NULL != pstcCmd) {
                (void)pfnVisit(pstcCmd);
            }
        }
    }
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

/*******************************************************************************
 * EOF (not truncated)
 ******************************************************************************/
