/**
 * @file sh_vt100.c
 * @author LokLiang (lokliang@163.com)
 * @brief
 * @version 0.1
 * @date 2023-03-21
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "sh.h"
#include "sh_vt100.h"
#include "sys_log.h"

#ifndef CONFIG_SH_MAX_HISTORY_LEN
#define CONFIG_SH_MAX_HISTORY_LEN 255 /* 用于记录命令的缓存长度 */
#endif

/**
 * @brief 定义热键列表 SH_DEF_KEY 中的成员
 *
 * @param KEY 热键代码（字符串）
 * @param FUNC 热键对应的函数( static void _key_func(void) )
 *
 * @verbatim
 * @c sh_vt100.c
 * @endverbatim
 */
#define SH_SETUP_KEY(KEY, FUNC) \
    {                           \
        .code = KEY,            \
        .key_func = FUNC,       \
    }

#define SH_DEF_KEY(NAME, ...)                       \
    static sys_psnode_t _SH_NAME(key_node_);        \
    static sh_key_t const _SH_NAME(key_data_)[] = { \
        __VA_ARGS__{0}};                            \
    static sh_key_reg_t const NAME = {              \
        .reg_node = &_SH_NAME(key_node_),           \
        .key = _SH_NAME(key_data_),                 \
    };

sh_t g_uart_handle_vt100;

static char last_key = '\0';

/* 基本键键 */
static void _exec_key_TAB(sh_t *sh_hdl);       // 补全命令
static void _exec_key_ENTER(sh_t *sh_hdl);     // 执行命令
static void _exec_key_LF(sh_t *sh_hdl);        // 执行命令
static void _exec_key_BACKSPACE(sh_t *sh_hdl); // 删除光标前一个字符
static void _exec_key_CTR_H(sh_t *sh_hdl);     // 删除光标前一个字符
static void _exec_key_DELETE(sh_t *sh_hdl);    // 删除光标后一个字符
static void _exec_key_UP(sh_t *sh_hdl);        // 查看上一个命令
static void _exec_key_DW(sh_t *sh_hdl);        // 查看下一个命令
static void _exec_key_RIGHT(sh_t *sh_hdl);     // 光标右移
static void _exec_key_LEFT(sh_t *sh_hdl);      // 光标左移
static void _exec_key_HOME(sh_t *sh_hdl);      // 光标移到行首（提示符除外）
static void _exec_key_END(sh_t *sh_hdl);       // 光标移到行尾

/* 其他键键 */
static void _exec_key_CTR_C(sh_t *sh_hdl);     // 结束程序
static void _exec_key_CTR_D(sh_t *sh_hdl);     // 删除光标后一个字符或结束程序
static void _exec_key_CTR_L(sh_t *sh_hdl);     // 清空终端
static void _exec_key_CTR_A(sh_t *sh_hdl);     // 同 HOME
static void _exec_key_CTR_E(sh_t *sh_hdl);     // 同 END
static void _exec_key_CTR_U(sh_t *sh_hdl);     // 擦除光标前到行首的全部内容
static void _exec_key_CTR_K(sh_t *sh_hdl);     // 擦除光标后到行尾的全部内容
static void _exec_key_CTR_W(sh_t *sh_hdl);     // 擦除光标前的单词
static void _exec_key_CTR_Y(sh_t *sh_hdl);     // 还原擦除的内容
static void _exec_key_CTR_RIGHT(sh_t *sh_hdl); // 光标移动到单词首位
static void _exec_key_CTR_LEFT(sh_t *sh_hdl);  // 光标移动到单词结尾

/* 终端操作 */
static void _clear(sh_t *sh_hdl);                  // 清屏
static void _del_line(sh_t *sh_hdl, unsigned len); // 删除当前行
static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos);
static void _insert_str(sh_t *sh_hdl, const char *str);

SH_DEF_KEY(
    _register_vt100_keys,

    /* 基本 */
    SH_SETUP_KEY(KEY_TAB,       _exec_key_TAB),       // 补全命令
    SH_SETUP_KEY(KEY_CTR_M,     _exec_key_ENTER),     // 执行命令
    SH_SETUP_KEY(KEY_CTR_J,     _exec_key_LF),        // 执行命令
    SH_SETUP_KEY(KEY_BACKSPACE, _exec_key_BACKSPACE), // 删除光标前一个字符
    SH_SETUP_KEY(KEY_CTR_H,     _exec_key_CTR_H),     // 删除光标前一个字符
    SH_SETUP_KEY(KEY_DELETE,    _exec_key_DELETE),    // 删除光标后一个字符
    SH_SETUP_KEY(KEY_UP,        _exec_key_UP),        // 查看上一个命令
    SH_SETUP_KEY(KEY_DW,        _exec_key_DW),        // 查看下一个命令
    SH_SETUP_KEY(KEY_RIGHT,     _exec_key_RIGHT),     // 光标右移
    SH_SETUP_KEY(KEY_LEFT,      _exec_key_LEFT),      // 光标左移
    SH_SETUP_KEY(KEY_HOME,      _exec_key_HOME),      // 光标移到行首（提示符除外）
    SH_SETUP_KEY(KEY_END,       _exec_key_END),       // 光标移到行尾

    /* 其他 */
    SH_SETUP_KEY(KEY_CTR_C,     _exec_key_CTR_C),     // 结束程序
    SH_SETUP_KEY(KEY_CTR_D,     _exec_key_CTR_D),     // 删除光标后一个字符或结束程序
    SH_SETUP_KEY(KEY_CTR_L,     _exec_key_CTR_L),     // 清空终端
    SH_SETUP_KEY(KEY_CTR_A,     _exec_key_CTR_A),     // 同 HOME
    SH_SETUP_KEY(KEY_CTR_E,     _exec_key_CTR_E),     // 同 END
    SH_SETUP_KEY(KEY_CTR_U,     _exec_key_CTR_U),     // 擦除光标前到行首的全部内容
    SH_SETUP_KEY(KEY_CTR_K,     _exec_key_CTR_K),     // 擦除光标后到行尾的全部内容
    SH_SETUP_KEY(KEY_CTR_W,     _exec_key_CTR_W),     // 擦除光标前的单词
    SH_SETUP_KEY(KEY_CTR_Y,     _exec_key_CTR_Y),     // 还原擦除的内容
    SH_SETUP_KEY(KEY_CTR_RIGHT, _exec_key_CTR_RIGHT), // 光标移动到单词首位
    SH_SETUP_KEY(KEY_CTR_LEFT,  _exec_key_CTR_LEFT),  // 光标移动到单词结尾
);

static void _exec_key_TAB(sh_t *sh_hdl) // "\x09"
{
    sh_ctrl_tab(sh_hdl); // 自动实例/打印命令
}

static void _exec_key_ENTER(sh_t *sh_hdl) // "\x0D"
{
    sh_echo(sh_hdl, "\r\n");
    sh_ctrl_enter(sh_hdl); // 执行已缓存到的命令
    sh_ctrl_print_cmd_line(sh_hdl);
    last_key = '\r';
}

static void _exec_key_LF(sh_t *sh_hdl) // "\x0A"
{
    if (last_key != '\r')
    {
        _exec_key_ENTER(sh_hdl);
    }
    last_key = '\n';
}

static void _exec_key_DELETE(sh_t *sh_hdl) // "\e[3~"
{
    if (sh_ctrl_delete(sh_hdl, 1))
    {
        /* 删除光标右边一个字符 */
        sh_echo(sh_hdl, DCH(1));
    }
}

static void _exec_key_BACKSPACE(sh_t *sh_hdl) // "\x7F"
{
    if (sh_ctrl_backspace(sh_hdl, 1))
    {
        /* 删除光标左边一个字符，同时使光标自动左移一位 */
        sh_echo(sh_hdl, CUB(1));
        sh_echo(sh_hdl, DCH(1));
    }
}

static void _exec_key_CTR_H(sh_t *sh_hdl) // "\x8"
{
    if (sh_ctrl_backspace(sh_hdl, 1))
    {
        /* 删除光标左边一个字符，同时使光标自动左移一位 */
        uint16_t line_pos = sh_ctrl_get_line_pos(sh_hdl);
        uint16_t line_len = sh_ctrl_get_line_len(sh_hdl);

        sh_echo(sh_hdl, "\b");
        for (int i = 0; i < line_len - line_pos; i++)
        {
            sh_echo(sh_hdl, "%c", sh_ctrl_get_line_char(sh_hdl, line_pos + i));
        }
        sh_echo(sh_hdl, " ");
        for (int i = 0; i < line_len - line_pos + 1; i++)
        {
            sh_echo(sh_hdl, "\b");
        }
    }
}

static void _exec_key_RIGHT(sh_t *sh_hdl) // "\e[C"
{
    sh_ctrl_right(sh_hdl); // 光标右移
}

static void _exec_key_LEFT(sh_t *sh_hdl) // "\e[D"
{
    sh_ctrl_left(sh_hdl); // 光标左移
}

static void _exec_key_HOME(sh_t *sh_hdl) // "\e[H"
{
    sh_ctrl_home(sh_hdl); // 光标移到开头位置
}

static void _exec_key_END(sh_t *sh_hdl) // "\e[F"
{
    sh_ctrl_end(sh_hdl); // 光标移到结尾位置
}

static void _exec_key_UP(sh_t *sh_hdl) // "\e[A"
{
    int len = sh_ctrl_get_line_len(sh_hdl);
    if (sh_ctrl_up(sh_hdl)) // 上一个命令
    {
        _del_line(sh_hdl, len);
        sh_ctrl_print_cmd_line(sh_hdl);
    }
}

static void _exec_key_DW(sh_t *sh_hdl) // "\e[B"
{
    int len = sh_ctrl_get_line_len(sh_hdl);
    if (sh_ctrl_down(sh_hdl)) // 下一个命令
    {
        _del_line(sh_hdl, len);
        sh_ctrl_print_cmd_line(sh_hdl);
    }
}

__weak void drv_hal_sys_exit(void)
{
    SYS_LOG_WRN("never define exit function");
}
static void _exec_key_CTR_C(sh_t *sh_hdl) // "\x03"
{
    sh_echo(sh_hdl, "\r\n");
    drv_hal_sys_exit();
}

static void _exec_key_CTR_D(sh_t *sh_hdl) // "\x04"
{
    if (sh_ctrl_delete(sh_hdl, 1))
    {
        /* 删除光标右边一个字符 */
        sh_echo(sh_hdl, DCH(1));
    }
    else
    {
        uint16_t input_pos = sh_ctrl_get_input_pos(sh_hdl);
        if (input_pos == 0)
        {
            if (sh_ctrl_is_module(sh_hdl))
            {
                sh_putstr(sh_hdl, "exit\r");
            }
            else
            {
                drv_hal_sys_exit();
            }
        }
    }
}

static void _exec_key_CTR_L(sh_t *sh_hdl) // "\x0C"
{
    uint16_t input_pos = sh_ctrl_get_input_pos(sh_hdl);

    _clear(sh_hdl);
    _del_line(sh_hdl, sh_ctrl_get_line_len(sh_hdl));
    sh_ctrl_print_cmd_line(sh_hdl);

    sh_ctrl_set_input_pos(sh_hdl, input_pos);
}

static void _exec_key_CTR_A(sh_t *sh_hdl) // "\x01"
{
    sh_ctrl_home(sh_hdl); // 光标移到开头位置
}

static void _exec_key_CTR_E(sh_t *sh_hdl) // "\x05"
{
    sh_ctrl_end(sh_hdl); // 光标移到结尾位置
}

static void _exec_key_CTR_U(sh_t *sh_hdl) // "\x15"
{
    /* 删除光标左边的所有字符 */
    int n = sh_ctrl_del_left(sh_hdl);
    if (n)
    {
        sh_echo(sh_hdl, CUB(n));
        sh_echo(sh_hdl, DCH(n));
    }
}

static void _exec_key_CTR_K(sh_t *sh_hdl) // "\x0B"
{
    /* 删除光标右边的所有字符 */
    int n = sh_ctrl_del_right(sh_hdl);
    if (n)
    {
        sh_echo(sh_hdl, DCH(n));
    }
}

static void _exec_key_CTR_W(sh_t *sh_hdl) // "\x17"
{
    int n = sh_ctrl_del_word(sh_hdl);
    if (n)
    {
        /* 删除光标左边 n 个字符，同时使光标自动左移 n 位 */
        sh_echo(sh_hdl, CUB(n));
        sh_echo(sh_hdl, DCH(n));
    }
}

static void _exec_key_CTR_Y(sh_t *sh_hdl) // "\x19"
{
    sh_ctrl_undelete(sh_hdl);
}

static void _exec_key_CTR_RIGHT(sh_t *sh_hdl) // "\e[1;5C"
{
    sh_ctrl_word_tail(sh_hdl);
}

static void _exec_key_CTR_LEFT(sh_t *sh_hdl) // "\e[1;5D"
{
    sh_ctrl_word_head(sh_hdl);
}

static int _cmd_clear(sh_t *sh_hdl, int argc, const char *argv[])
{
    _clear(sh_hdl);
    return 0;
}
SH_DEF_CMD(
    _register_cmd_clear,
    SH_SETUP_CMD("clear", "Clear the terminal screen", _cmd_clear, NULL), // 清屏
);

#if defined(__linux__)

static void _clear(sh_t *sh_hdl)
{
    sh_echo(sh_hdl, "\ec");
}

static void _del_line(sh_t *sh_hdl, unsigned len)
{
    sh_echo(sh_hdl, DL(0));
}

static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos)
{
    /* 设置光标水平位置 */
    int hoffset = new_pos - last_pos;
    if (hoffset > 0) // 右移
    {
        sh_echo(sh_hdl, CUF(hoffset));
    }
    else if (hoffset < 0) // 左移
    {
        sh_echo(sh_hdl, CUB(-hoffset));
    }
}

#else

static void _clear(sh_t *sh_hdl)
{
    sh_echo(sh_hdl, "\ec");
}

static void _del_line(sh_t *sh_hdl, unsigned len)
{
    sh_echo(sh_hdl, "\r");
    for (int i = 0; i <= len; i++)
    {
        sh_echo(sh_hdl, " ");
    }
    sh_echo(sh_hdl, "\r");
}

static void _set_cursor_hoffset(sh_t *sh_hdl, int last_pos, int new_pos)
{
    /* 设置光标水平位置 */
    if (new_pos > last_pos) // 右移
    {
        for (; new_pos > last_pos; last_pos++)
        {
            char c = sh_ctrl_get_line_char(sh_hdl, last_pos);
            sh_echo(sh_hdl, "%c", c);
        }
    }
    else if (new_pos < last_pos) // 左移
    {
        for (; new_pos < last_pos; last_pos--)
        {
            sh_echo(sh_hdl, "\b");
        }
    }
}

#endif

static void _insert_str(sh_t *sh_hdl, const char *str)
{
    sh_echo(sh_hdl, ICH(strlen(str)));
    sh_echo(sh_hdl, "%s", str);
}

__used static void _clear_line(sh_t *sh_hdl)
{
    _del_line(sh_hdl, sh_ctrl_get_line_len(sh_hdl));
}

/**
 * @brief 初始化一个新的 VT100 的终端对象。
 *
 * @param sh_hdl 被初始化由 sh_putc() 或 sh_putstr() 的对象
 * @param vprint 注册实现函数 sh_hdl ==> 终端和显示
 * @param disconnect 当执行 exit 命令时被回调的，用于断开连接的函数。如不需要可设为 NULL
 */
int sh_init_vt100(sh_t *sh_hdl, sh_vprint_fn vprint, sh_disconnect_fn disconnect)
{
    int ret = 0;
    for (int i = 0; i < sizeof(*sh_hdl); i++)
    {
        ((uint8_t *)sh_hdl)[i] = 0;
    }

    sh_port_t port = {
        .set_cursor_hoffset = _set_cursor_hoffset,
        .insert_str = _insert_str,
        .vprint = vprint,
        .disconnect = disconnect,
        // .clear_line = _clear_line, // 执行清除一行。值为 NULL 时不启用刷命令行
    };
    sh_register_port(sh_hdl, &port);

    sh_hdl->cmd_buf = sh_hdl->cmd_line;

    sh_hdl->obj_key_data = _register_vt100_keys;
    sh_hdl->obj_key_data.reg_node = &sh_hdl->obj_key_node;
    ret |= sh_register_key(sh_hdl, &sh_hdl->obj_key_data);

    sh_hdl->obj_cmd_data = _register_cmd_clear;
    sh_hdl->obj_cmd_data.reg_node = &sh_hdl->obj_cmd_node;
    ret |= sh_register_key_cmd(sh_hdl, &sh_hdl->obj_cmd_data);

    return -!!ret;
}

static int _install_shell_vt100(void)
{
    if (sh_init_vt100(&g_uart_handle_vt100, SYS_VPRINT, NULL) == 0)
    {
        static uint8_t history_mem[CONFIG_SH_MAX_HISTORY_LEN];
        static uint8_t bank_mem[CONFIG_SH_MAX_LINE_LEN];
        sh_config_history_mem(&g_uart_handle_vt100, history_mem, sizeof(history_mem));
        sh_config_backup_mem(&g_uart_handle_vt100, bank_mem, sizeof(bank_mem));
        return 0;
    }
    else
    {
        return -1;
    }
}
INIT_EXPORT_COMPONENT(_install_shell_vt100);
