/*
 *  Console monitor
 */
#include <t_services.h>
#include <string.h>
#include "lpc23xx.h"
#include "kernel_cfg.h"
#include "kernel_id.h"
#include "tasknames.h"

#define DBG_LINE_SIZE   80

UB *
skip_blanks (UB *line)
{
    while (*line == ' ' || *line == '\t')
        line ++;
    return line;
}

struct cmdtab {
  UB *cmdname;
  void (*cmdfunc)();
};

static void HelpCommand();
static void ps_command();
extern void fat_command();
extern void date_command();
extern void lcd_command();
extern void pwm_command();
extern void scroll_command();
extern void host_command();
extern void time_command();
extern void mplay_command();
extern void aplay_command();
extern void wplay_command();
#ifdef TEST_CODE
extern void uplay_command();
#endif
extern void codec_command();
extern void volume_command();
extern void player_command();
extern void ar1000_command();
extern void tune_command();
extern void seek_command();
extern void fm_command();
extern void path_command();
extern void ftest_command();
extern void nand_command();
extern void gamma_command();
extern void copy_command();
extern void verify_command();
extern void touch_command();
extern void irstat_command();
extern void expand_command();
extern void dma_command();
extern void mci_command();
extern void blight_command();

const struct cmdtab CmdTable[] = {
  { "help",    HelpCommand },
  { "?",       HelpCommand },
  { "ar1000",  ar1000_command },
  { "blight",  blight_command },
  { "copy",    copy_command },
  { "verify",  verify_command },
  { "dma",     dma_command },
  { "expand",  expand_command },
  { "fat",     fat_command },
  { "fm",      fm_command },
  { "ftest",   ftest_command },
  { "mci",     mci_command },
#define	USE_LCD
  { "codec",   codec_command },
  { "date",    date_command },
  { "gamma",   gamma_command },
  { "lcd",     lcd_command },
  { "host",    host_command },
  { "irstat",  irstat_command },
  { "nand",    nand_command },
  { "ps",      ps_command },
#ifdef USE_LCD
  { "pwm",     pwm_command },
  { "scroll",  scroll_command },
#endif
#ifdef USE_TIME
  { "time",    time_command },
#endif
  { "path",    path_command },
  { "seek",    seek_command },
  { "touch",   touch_command },
  { "tune",    tune_command },
  { "volume",  volume_command },
  { "mplay",   mplay_command },
  { "aplay",   aplay_command },
  { "wplay",   wplay_command },
#ifdef TEST_CODE
  { "uplay",   uplay_command },
#endif
  { "player",  player_command },
  { NULL, NULL },
};

int cons_read(char *cp, int nb)
{
    return (serial_rea_dat(1, cp, sizeof(UB)));
}

void cons_write(char *cp, int nb)
{
    serial_wri_dat(1, cp, nb);
}

static void HelpCommand(int argc, char **argv)
{
    struct cmdtab *cp;

    for (cp = (struct cmdtab *)CmdTable; cp->cmdname; cp++)
        cons_printf(" %s\n", cp->cmdname);
}

static const char task_stat_str[][sizeof("SUSPENDED")] = {
    "DORMANT",
    "RUNNABLE",
    "WAITING",
    "SUSPENDED",
    "SLEEP",
    "WOBJ",
    "WOBJCB",
    };

static void
ps_command(int argc, char **argv)
{
    TCB *tcb;
    int ix, st, sx, nb;
    UB *pstk;
    const TINIB *pktab = _kernel_tinib_table;

    cons_printf("TASK Status\n"
                "Name            ID PI     Stack  State\n");
    for (ix = TMIN_TSKID; ix <= tmax_tskid; ix ++) {
        nb = 0;
        for (pstk = (UB *)pktab->stk; *pstk == 0; pstk++) {
            nb++;
            if (nb == pktab->stksz)
                break;
        }
        tcb = get_tcb(ix);
        cons_printf("%15s %2d %2d %4d/%4d ", task_names[ix-1], ix, tcb->priority, nb, pktab->stksz);
        st = tcb->tstat;
        for (sx = 0; st; sx ++) {
            if (st & 0x01) {
                cons_printf("%s", task_stat_str[sx]);
                if (st & 0xfe)
                    cons_printf("|");
            }
            st >>= 1;
        }
        cons_printf("\n");
        pktab++;
    }
}

void
ParseCommand (UB *line)
{
    UB *p;
    struct cmdtab *cp;
    UB *argv[20], *wp;
    int n;


    line = skip_blanks(line);
    if (*line == '\0')
        return;

    n = 0; wp = line;
    argv[n++] = line;
    for (p = line; *p; p++) {
        if (*p == ' ' || *p == '\t') {
            *p++ = '\0';
            p = skip_blanks(p);
            if (p && *p)
                argv[n++] = p;
        }
    }
    argv[n] = NULL;

    for (cp = (struct cmdtab *)CmdTable; cp->cmdname; cp++) {
        if (memcmp(cp->cmdname, line, strlen(line)) == 0) {
            cp->cmdfunc(n, argv);
            break;
        }
    }
    if (cp->cmdname == NULL)
        cons_printf("no such command: %s\n", line);
}

int
cons_getline (UB *line, int size)
{
    UB      ch;
    int     len;

    len = size;
    while (size > 0) {
        if (cons_read(&ch, sizeof(UB)) > 0) {
            if (ch == '\r') {
                cons_putchar('\n');
                break;
            } else if (ch == '\b') {
                if (size < len) {
                    line --;
                    size ++;
                    cons_putstring("\b \b");
                }
            } else {
                *line ++ = ch;
                size --;
                cons_putchar(ch);
            }
        }
    }
    *line = '\0';
    return len - size;
}

void console_task(VP_INT extinf)
{
    static UB line[DBG_LINE_SIZE];
    int len;

    dly_tsk(500);
    serial_ctl_por(1, IOCTL_CRLF);
    cons_printf("]]] %s Console Monitor [[[\n", TARGET_NAME);
    while (1) {
        cons_putstring("> ");
        len = cons_getline(line, DBG_LINE_SIZE);
        ParseCommand(line);
    }
}
