/**
 *******************************************************************************
 * @file  cli_inbuilt_cmd.c
 * @brief This file provides firmware functions to manage the CLI in-built command.
 @verbatim
   Change Logs:
   Date             Author          Notes
   2022-06-30       CDT             First version
   2023-01-17       CDT             Typo: Acess -> Access, strint -> string
 @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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "cli_cmd.h"

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

/**
 * @defgroup CLI_Inbuilt_Command CLI Inbuilt Command
 * @{
 */

/*******************************************************************************
 * Local type definitions ('typedef')
 ******************************************************************************/

/*******************************************************************************
 * Local pre-processor symbols/macros ('#define')
 ******************************************************************************/
/**
 * @defgroup CLI_Inbuilt_Command_Local_Macros CLI Inbuilt Command Local Macros
 * @{
 */

#define CLI_ICMD_PRINT_FORMAT           "%-20.15s %-35.30s %s\r\n"

/**
 * @defgroup CLI_Access_Memory_Width CLI Access Memory Width
 * @{
 */
#define CLI_MEM_WIDTH_8BIT              (0)
#define CLI_MEM_WIDTH_16BIT             (1)
#define CLI_MEM_WIDTH_32BIT             (2)
/**
 * @}
 */

/**
 * @}
 */

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

/*******************************************************************************
 * Local function prototypes ('static')
 ******************************************************************************/
/**
 * @addtogroup CLI_Inbuilt_Command_Local_Functions
 * @{
 */

static int32_t CLI_ICMD_Help(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Read8(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Write8(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Read16(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Write16(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Read32(int32_t argc, char *argv[]);
static int32_t CLI_ICMD_Write32(int32_t argc, char *argv[]);

/**
 * @}
 */

/*******************************************************************************
 * Local variable definitions ('static')
 ******************************************************************************/

/**
 * @defgroup CLI_Inbuilt_Command_Global_Variables CLI Inbuilt Command Global Variables
 * @{
 */

static stc_cli_cmd_t m_astcInbuiltCmd[] = {
    CLI_CMD(help, CLI_ICMD_Help,    "help",           "Show all commands"),

    CLI_CMD(r8,   CLI_ICMD_Read8,   "r8  addr [len]", "Read memory 8-bit"),
    CLI_CMD(r16,  CLI_ICMD_Read16,  "r16 addr [len]", "Read memory 16-bit"),
    CLI_CMD(r32,  CLI_ICMD_Read32,  "r32 addr [len]", "Read memory 32-bit"),

    CLI_CMD(w8,   CLI_ICMD_Write8,  "w8  addr value", "Write memory 8-bit"),
    CLI_CMD(w16,  CLI_ICMD_Write16, "w16 addr value", "Write memory 16-bit"),
    CLI_CMD(w32,  CLI_ICMD_Write32, "w32 addr value", "Write memory 32-bit"),
};

/**
 * @}
 */

/*******************************************************************************
 * Function implementation - global ('extern') and local ('static')
 ******************************************************************************/
/**
 * @defgroup CLI_Inbuilt_Command_Local_Functions CLI Inbuilt Command Local Functions
 * @{
 */

/**
 * @brief  Convert hex string to data(uint32_t).
 * @param  [in]  pcStr              The pointer to hex string
 * @param  [out] pu32Data           Data
 * @retval int32_t:
 *           - OPCODE_OK:           Run command successfully
 *           - OPCODE_ERR:          If one of following cases matches:
 *                                  - The pointer pcStr or pu32Data value is NULL
 *                                  - String content is invalid
 */
static int32_t HexStrToUL(char *pcStr, uint32_t *pu32Data)
{
    char *pcEndPtr;
    int32_t i32Ret = OPCODE_ERR;

    if ((pcStr != NULL) && (pu32Data != NULL)) {
        *pu32Data = strtoul(pcStr, &pcEndPtr, 16);
        if ('\0' == *pcEndPtr) {
            i32Ret = OPCODE_OK;
        }
    }

    return i32Ret;
}

/**
 * @brief  Read memory.
 * @param  [in] i32Width            Access width
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Read successfully
 *           - OPCODE_ERR:          Read unsuccessfully
 */
static int32_t ReadMem(int32_t i32Width, int32_t argc, char *argv[])
{
    uint32_t i;
    uint8_t u8Data;
    uint16_t u16Data;
    uint32_t u32Data;
    uint32_t u32Addr;
    uint32_t u32Len = 1UL;
    int32_t i32Ret = OPCODE_ERR;

    if ((argc == 2) || (argc == 3)) {
        i32Ret = HexStrToUL(argv[1], &u32Addr);
        if (OPCODE_OK == i32Ret) {
            if (argc == 3) {
                i32Ret = HexStrToUL(argv[2], &u32Len);
            }

            if (OPCODE_OK == i32Ret) {
                for (i = 0UL; i < u32Len; i++) {
                    switch (i32Width) {
                        case CLI_MEM_WIDTH_8BIT:
                            u8Data = *(volatile uint8_t *)(u32Addr + i);
                            (void)printf("0x%02X ", u8Data);
                            break;
                        case CLI_MEM_WIDTH_16BIT:
                            u16Data = *(volatile uint16_t *)(u32Addr + i * 2UL);
                            (void)printf("0x%04X ", u16Data);
                            break;
                        default:
                            u32Data = *(volatile uint32_t *)(u32Addr + i * 4UL);
                            (void)printf("0x%08X ", (unsigned int)u32Data);
                            break;
                    }
                }

                (void)printf("\r\n");
            }
        }
    }

    return i32Ret;
}

/**
 * @brief  Write memory.
 * @param  [in] i32Width            Access width
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Write successfully
 *           - OPCODE_ERR:          Write unsuccessfully
 */
static int32_t WriteMem(int32_t i32Width, int32_t argc, char *argv[])
{
    uint32_t u32Data;
    uint32_t u32Addr;
    int32_t i32Ret = OPCODE_ERR;

    if ((CLI_MEM_WIDTH_8BIT == i32Width)  || \
        (CLI_MEM_WIDTH_16BIT == i32Width) || \
        (CLI_MEM_WIDTH_32BIT == i32Width)) {
        if (3 == argc) {
            if (HexStrToUL(argv[1], &u32Addr) == OPCODE_OK) {
                if (HexStrToUL(argv[2], &u32Data) == OPCODE_OK) {
                    switch (i32Width) {
                        case CLI_MEM_WIDTH_8BIT:
                            *(uint8_t *)u32Addr = (uint8_t)u32Data;
                            break;
                        case CLI_MEM_WIDTH_16BIT:
                            *(uint16_t *)u32Addr = (uint16_t)u32Data;
                            break;
                        default:
                            *(uint32_t *)u32Addr = u32Data;
                            break;
                    }

                    i32Ret = OPCODE_OK;
                }
            }
        }
    }

    return i32Ret;
}

/**
 * @brief  Print command name, usage and description.
 * @param  [in] pstcCmd             The pointer to a @ref stc_cli_cmd_t structure
 * @retval int32_t:
 *           - OPCODE_OK:           Print successfully
 *           - OPCODE_ERR:          The pointer pstcCmd value is NULL
 */
static int32_t ShowCmd(stc_cli_cmd_t *pstcCmd)
{
    int32_t i32Ret = OPCODE_ERR;

    if (NULL != pstcCmd) {
        (void)printf(CLI_ICMD_PRINT_FORMAT, pstcCmd->pcName, pstcCmd->pcUsage, pstcCmd->pcDesc);
        i32Ret = OPCODE_OK;
    }

    return i32Ret;
}

/**
 * @brief  Show all command.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Show all command successfully
 */
static int32_t CLI_ICMD_Help(int32_t argc, char *argv[])
{
    (void)printf("\r\n");
    (void)printf(CLI_ICMD_PRINT_FORMAT, "Command", "Usage", "Description");
    (void)printf(CLI_ICMD_PRINT_FORMAT, "-------", "-----", "-----------");

    /* Print command name, usage, and description. */
    (void)CLI_VisitEachCmd(&ShowCmd);

    (void)printf("\r\n");

    return OPCODE_OK;
}

/**
 * @brief  Read memory 8bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Read successfully
 *           - OPCODE_ERR:          Read unsuccessfully
 */
static int32_t CLI_ICMD_Read8(int32_t argc, char *argv[])
{
    return ReadMem(CLI_MEM_WIDTH_8BIT, argc, argv);
}

/**
 * @brief  Write memory 8bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Write successfully
 *           - OPCODE_ERR:          Write unsuccessfully
 */
static int32_t CLI_ICMD_Write8(int32_t argc, char *argv[])
{
    return WriteMem(CLI_MEM_WIDTH_8BIT, argc, argv);
}

/**
 * @brief  Read memory 16bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Read successfully
 *           - OPCODE_ERR:          Read unsuccessfully
 */
static int32_t CLI_ICMD_Read16(int32_t argc, char *argv[])
{
    return ReadMem(CLI_MEM_WIDTH_16BIT, argc, argv);
}

/**
 * @brief  Write memory 16bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Write successfully
 *           - OPCODE_ERR:          Write unsuccessfully
 */
static int32_t CLI_ICMD_Write16(int32_t argc, char *argv[])
{
    return WriteMem(CLI_MEM_WIDTH_16BIT, argc, argv);
}

/**
 * @brief  Read memory 32bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Read successfully
 *           - OPCODE_ERR:          Read unsuccessfully
 */
static int32_t CLI_ICMD_Read32(int32_t argc, char *argv[])
{
    return ReadMem(CLI_MEM_WIDTH_32BIT, argc, argv);
}

/**
 * @brief  Write memory 32bit.
 * @param  [in] argc                The number of argument
 * @param  [in] argv                The pointer array of argument
 * @retval int32_t:
 *           - OPCODE_OK:           Write successfully
 *           - OPCODE_ERR:          Write unsuccessfully
 */
static int32_t CLI_ICMD_Write32(int32_t argc, char *argv[])
{
    return WriteMem(CLI_MEM_WIDTH_32BIT, argc, argv);
}

/**
 * @}
 */

/**
 * @defgroup CLI_Inbuilt_Command_Global_Functions CLI Inbuilt Command Global Functions
 * @{
 */

/**
 * @brief  Register in-built command.
 * @param  None
 * @retval int32_t:
 *           - OPCODE_OK:           Register successfully.
 *           - OPCODE_ERR:          Register unsuccessfully.
 */
int32_t CLI_ICMD_Register(void)
{
    return CLI_RegisterCmdTable(m_astcInbuiltCmd, ARRAY_SZ(m_astcInbuiltCmd));
}

/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

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