/*
 * Copyright 2022 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "embedded_cli.h"
#include "board_init.h"
#include "ff.h"

#include "stdlib.h"

#define CLI_BUF_SIZE        1024
#define CLI_RX_BUF_SIZE     128
#define CLI_CMD_BUF_SIZE    128
#define CLI_HISTORY_SIZE    128
#define CLI_BINDING_CNT     8

static CLI_UINT cli_buf[BYTES_TO_CLI_UINTS(CLI_BUF_SIZE)];
static EmbeddedCli * cli_obj;

extern const CliCommandBinding cli_mem_cmd_bingding;

/* change directory. */
static void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
/* list directory contents. */
static void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
/* print working directory */
static void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);

/* write char. */
static void cli_write_char(EmbeddedCli *cli, char c)
{
    (void)cli;
    putchar(c);
}

void cli_cmd_init(void)
{
    EmbeddedCliConfig *cli_conf = embeddedCliDefaultConfig();
    cli_conf->cliBuffer         = cli_buf;
    cli_conf->cliBufferSize     = CLI_BUF_SIZE;
    cli_conf->rxBufferSize      = CLI_RX_BUF_SIZE;
    cli_conf->cmdBufferSize     = CLI_CMD_BUF_SIZE;
    cli_conf->historyBufferSize = CLI_HISTORY_SIZE;
    cli_conf->maxBindingCount   = CLI_BINDING_CNT;

    cli_obj = embeddedCliNew(cli_conf);
    cli_obj->writeChar = cli_write_char;

    embeddedCliAddBinding(cli_obj, (CliCommandBinding) {
        "cd",
        "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
        true,
        NULL,
        cli_cmd_cd
    });

    embeddedCliAddBinding(cli_obj, (CliCommandBinding) {
        "ls",
        "Usage: ls [DIR]...\r\n\tList information about the FILEs (the current directory by default).",
        true,
        NULL,
        cli_cmd_ls
    });

    embeddedCliAddBinding(cli_obj, (CliCommandBinding) {
        "pwd",
        "Usage: pwd\r\n\tPrint the name of the current working directory.",
        true,
        NULL,
        cli_cmd_pwd
    });

    embeddedCliAddBinding(cli_obj, cli_mem_cmd_bingding);
}

void cli_task(void)
{
    char ch = board_getchar();
    if ( ch > 0 )
    {
        while( ch > 0 )
        {
            embeddedCliReceiveChar(cli_obj, ch);
            ch = board_getchar();
        }
        embeddedCliProcess(cli_obj);
    }
}

/* change directory. */
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context)
{
    (void) cli;
    (void) context;

    uint16_t argc = embeddedCliGetTokenCount(args);

    /* only support 1 argument. */
    if ( argc != 1 )
    {
        embeddedCliPrint(cli, "invalid arguments\r\n");
        return;
    }

    /* default is current directory. */
    const char* dpath = args;

    if ( FR_OK != f_chdir(dpath) )
    {
        embeddedCliPrint(cli, "No such file or directory\r\n");
        return;
    }
}

/* list directory contents. */
static void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
{
    (void) cli;
    (void) context;

    uint16_t argc = embeddedCliGetTokenCount(args);

    /* only support 1 argument. */
    if ( argc > 1 )
    {
        embeddedCliPrint(cli, "invalid arguments\r\n");
        return;
    }

    /* default is current directory. */
    const char* dpath = ".";
    if (argc) dpath = args;

    DIR dir;
    if ( FR_OK != f_opendir(&dir, dpath) )
    {
        embeddedCliPrint(cli, "No such file or directory\r\n");
        return;
    }

    FILINFO fno;
    while( (f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0) )
    {
        if ( fno.fname[0] != '.' ) /* ignore . and .. entry. */
        {
            if ( fno.fattrib & AM_DIR )
            {
                /* directory. */
                printf("/%s\r\n", fno.fname);
            }
            else
            {
                printf("%-40s", fno.fname);
                if (fno.fsize < 1024)
                {
                    printf("%lu B\r\n", (unsigned long)fno.fsize);
                }
                else
                {
                    printf("%lu KB\r\n", (unsigned long)(fno.fsize / 1024));
                }
            }
        }
    }

    f_closedir(&dir);
}

/* print working directory */
static void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context)
{
    (void) cli;
    (void) context;
    uint16_t argc = embeddedCliGetTokenCount(args);

    if (argc != 0)
    {
        embeddedCliPrint(cli, "invalid arguments\r\n");
        return;
    }

    char path[256];
    if (FR_OK != f_getcwd(path, sizeof(path)))
    {
        embeddedCliPrint(cli, "cannot get current working directory\r\n");
    }

    embeddedCliPrint(cli, path);
}

/* EOF. */
