/**
 * @file ntshell_usrcmd.c
 * @author Shinichiro Nakamura
 * @brief ナチュラルタイニーシェルタスクコマンドの実装。
 * @details
 */

/*
 * ===============================================================
 *  Natural Tiny Shell - User Command Module
 * ===============================================================
 * Copyright (c) 2010-2011 Shinichiro Nakamura
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * ===============================================================
 */

#include <kernel.h>
#include <target_syssvc.h>
#include <syssvc/serial.h>
#include "kernel_cfg.h"

#include "ntopt.h"
#include "ntlibc.h"
#include "ntshell_config.h"

#include "task_led.h"
#include "task_display.h"
#include "task_gps.h"
#include "task_camera.h"

static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj);
static int usrcmd_help(int argc, char **argv);
static int usrcmd_info(int argc, char **argv);
static int usrcmd_led(int argc, char **argv);
static int usrcmd_display(int argc, char **argv);
static int usrcmd_gps(int argc, char **argv);
static int usrcmd_camera(int argc, char **argv);

/**
 * @brief コマンドテーブル。
 */
typedef struct {
    char *cmd;  /**< コマンド名。 */
    char *desc; /**< コマンド説明。 */
    int (*func)(int argc, char **argv); /**< コールバック関数。 */
} cmd_table_t;

/**
 * @brief コマンドの定義。
 * @details システムで必要なコマンドの実装を追加すると良い。
 */
static const cmd_table_t cmdlist[] = {
    { "help", "Help.", usrcmd_help },
    { "info", "Information.", usrcmd_info },
    { "led", "Command for LED task.", usrcmd_led },
    { "display", "Command for display task.", usrcmd_display },
    { "gps", "Command for GPS task.", usrcmd_gps },
    { "camera", "Command for camera task.", usrcmd_camera },
    { NULL, NULL, NULL }
};

/**
 * @brief NT-Shellコマンドを実行する。
 * @details
 *
 * @param text テキスト。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
int ntshell_usrcmd_execute(const char *text)
{
    /*
     * ntoptライブラリを使って引数を解析し実行する。
     */
    return ntopt_parse(text, usrcmd_ntopt_callback, NULL);
}

/**
 * @brief ユーザコマンドを実行する。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_ntopt_callback(int argc, char **argv, void *extobj)
{
    /*
     * 引数の数が0ならば何もしない。
     * 単にenterが押されただけと解釈する。
     */
    if (argc == 0) {
        return 0;
    }

    /*
     * コマンドテーブルを探索して、
     * コマンド名が一致したらコールバック関数を呼び出す。
     */
    const cmd_table_t *p = &cmdlist[0];
    while (p->cmd != NULL) {
        if (ntlibc_strcmp((const char *)argv[0], p->cmd) == 0) {
            return p->func(argc, argv);
        }
        p++;
    }

    /*
     * ここに到達するということは、未知のコマンドである。
     */
    {
        static const char *msg = "Unknown command. (help: display help.)\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg));
    }
    return 0;
}

/**
 * @brief helpコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_help(int argc, char **argv)
{
    const cmd_table_t *p = &cmdlist[0];
    char buf[128];

    /*
     * コマンド名とコマンド説明を列挙する。
     */
    while (p->cmd != NULL) {
        ntlibc_strcpy(buf, p->cmd);
        ntlibc_strcat(buf, "\t:");
        ntlibc_strcat(buf, p->desc);
        ntlibc_strcat(buf, "\n");
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)buf, ntlibc_strlen(buf));
        tslp_tsk(5);
        p++;
    }
    return 0;
}

/**
 * @brief infoコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_info(int argc, char **argv)
{
    if (argc != 2) {
        static const char *msg = "info sys\ninfo ver\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "sys") == 0) {
        static const char *msg = "LPCXpressoClock\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "ver") == 0) {
        static const char *msg = "0.0.1\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    {
        static const char *msg = "Unknown sub command.\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
    }
    return -1;
}

/**
 * @brief ledコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_led(int argc, char **argv) {
    if (argc != 2) {
        static const char *msg = "led [low | mid | high]\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "low") == 0) {
        TSKAPI_LED_INTERVAL(1000);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "mid") == 0) {
        TSKAPI_LED_INTERVAL(500);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "high") == 0) {
        TSKAPI_LED_INTERVAL(250);
        return 0;
    }
    {
        static const char *msg = "Unknown sub command.\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
    }
    return -1;
}

/**
 * @brief displayコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_display(int argc, char **argv)
{
    if (argc != 3) {
        static const char *msg = "display text [TTTTTT]\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "text") == 0) {
        int i;
        int len = ntlibc_strlen(argv[2]);
        uint8_t segsel[6];
        for (i = 0; i < sizeof(segsel); i++) {
            segsel[i] = 0;
        }
        for (i = 0; i < len; i++) {
            switch (i) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                    /*
                     * ７セグ。
                     */
                    segsel[i] = argv[2][i];
                    break;
                default:
                    /*
                     * 無視する。
                     */
                    break;
            }
        }
        TSKAPI_DISPLAY_UPDATE(
                segsel[0],
                segsel[1],
                segsel[2],
                segsel[3],
                segsel[4],
                segsel[5],
                0);
        return 0;
    }
    {
        static const char *msg = "Unknown sub command.\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
    }

    return 0;
}

/**
 * @brief gpsコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_gps(int argc, char **argv)
{
    if (argc != 3) {
        static const char *msg = "gps actmode [N]\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "actmode") == 0) {
        int n = argv[2][0] - '0';
        TSKAPI_GPS_ACTMODE(n);
        return 0;
    }
    {
        static const char *msg = "Unknown sub command.\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
    }

    return 0;
}

/**
 * @brief cameraコマンド。
 * @details
 *
 * @param argc 引数の数。
 * @param argv 引数。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
static int usrcmd_camera(int argc, char **argv)
{
    if (argc != 3) {
        static const char *msg = "camera capture [N]\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
        return 0;
    }
    if (ntlibc_strcmp(argv[1], "capture") == 0) {
        int n = argv[2][0] - '0';
        TSKAPI_CAMERA_CAPTURE(n);
        return 0;
    }
    {
        static const char *msg = "Unknown sub command.\n";
        serial_wri_dat(SHELL_PORTID,
                (const char_t *)msg, ntlibc_strlen(msg) + 1);
    }
    return 0;
}

