/*********************************************************************************************************                                               
*                                          ACH Test Suite
*                             COPYRIGHT(c) 2019 Amped RF Technology (ART)
*                                        All Rights Reserved
*
* File    : menu.c
* Version : V1.0.0
* Brief   : Menu driver
**********************************************************************************************************/

#include "ach118x.h"
#include <stdio.h>
#include <stdlib.h>
#include "GKI_uart.h"
#include "menu.h"

#define CR 0x0D
#define LF 0x0A

u32 MenuTimerMS=0;


//------------------------------------------
// _itoa() - Convert int to string
//------------------------------------------
void reverse_str(char *s, int len) {
    int i, j;
    char c;
    for (i = 0, j = len-1; i < j; ++i, --j) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}
char* _itoa(int n, char *s) {
    int i = 0, sign = n;
    do {
        s[i++] = abs(n % 10) + '0';
    } while ((n /= 10) != 0);
    if (sign < 0)
        s[i++] = '-';
    s[i] = '\0';
    reverse_str(s, i);
    return s;
}


//-----------------------------------------------
// _atoi() - Convert string to unsigned int
//-----------------------------------------------
u32 _atoi(char *s) {
    u32 n=0;
    while (*s>='0' && *s<='9') {
        n *= 10;
        n += *s++ - '0';
    }
    return n;
}


//------------------------------------------------------------------------
// Menu functions
//------------------------------------------------------------------------
char *ItemNum="123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
//------------------------------------
void ShowMenuItemNum(int i) {
    putchar(' ');
    putchar(ItemNum[i]);
    putchar('.');
    putchar(' ');
}
//------------------------------------
int GetMenuItemNum(int ch) {
    char *p=&ItemNum[0];
    for (int i=0; *p; i++, p++) {
        if (*p==ch) {
            return i;
        }
    }
    return -1;
}
//------------------------------------
void crlf(void) {
    putstring("\r\n");
}
//------------------------------------
void putstring_crlf(char *s) {
    putstring(s);
    crlf();
}
//------------------------------------
void NotImplemented(void) {
   putstring_crlf("Not Implemented");
}
//------------------------------------
void InvalidSelection(void) {
   putstring_crlf("Invalid Selection");
}
//------------------------------------
u8 CharIsReady(void) {
    return GKI_UartRxDataReady(UART0);
}
//------------------------------------------
// Ask are you sure? (Y)
//------------------------------------------
int AreYouSure(void)
{
    int ch;
    putstring("Are you sure? (Y) ");
    do {
        ch = CharIsReady() ? getchar() : 0;
    } while (ch==0);
    crlf();
    return ch=='Y';
}



//------------------------------------------------------------------------
// menu definitions
//------------------------------------------------------------------------
#if 0
extern MenuTypedef MainMenu;
extern MenuTypedef AdvancedMenu;
extern MenuTypedef UploadFileMenu;
extern MenuTypedef DebugMenu;
//------------------------------------------------------------------------
const MenuItemTypedef MainMenuItems[]={
    {ITEM_TYPE_MENU,   1, "Upload File",            (void *)&NotImplemented},
    {ITEM_TYPE_NORMAL, 1, "Delete File",            (void *)&NotImplemented},
    {ITEM_TYPE_NORMAL, 1, "Run Application",        (void *)&NotImplemented},
    {ITEM_TYPE_NORMAL, 0, "Default Application",    (void *)&NotImplemented},
    {ITEM_TYPE_NORMAL, 0, "Undefault Application",  (void *)&NotImplemented},
    {ITEM_TYPE_NORMAL, 0, "List Files",             (void *)&NotImplemented},
    {0,0,0}
};
MenuTypedef MainMenu={"Main Menu", (MenuItemTypedef *)&MainMenuItems};
//------------------------------------------------------------------------
const MenuItemTypedef AdvancedMenuItems[]={
    {ITEM_TYPE_YESNO,  0, "Erase Variables",        (void *)menu_erase_vars},
    {ITEM_TYPE_YESNO,  0, "Init Directory",         (void *)InitDirectory},
    {ITEM_TYPE_YESNO,  0, "Update Flashloader",     (void *)menu_update_flashloader},
//  {ITEM_TYPE_NORMAL, 0, "Timer Test",             (void *)menu_timer_test},
    {0,0,0}
};
MenuTypedef AdvancedMenu={"Advanced Menu", (MenuItemTypedef *)&AdvancedMenuItems};
//------------------------------------------------------------------------
const MenuItemTypedef UploadFileItems[]={
    {ITEM_TYPE_NORMAL, 1, "Application/Executable", (void *)menu_upload_app},
    {ITEM_TYPE_NORMAL, 1, "Generic",                (void *)menu_upload_file},
#ifdef __DEBUG
    {ITEM_TYPE_NORMAL, 0, "YModem Debug",           (void *)menu_ymodem_debug},
#endif
    {0,0,0}
};
MenuTypedef UploadFileMenu={"Select type of file to upload", (MenuItemTypedef *)&UploadFileItems};
//------------------------------------------------------------------------
const MenuItemTypedef DebugItems[]={
    {ITEM_TYPE_NORMAL, 0, "Show OTP",               (void *)menu_show_otp},
    {ITEM_TYPE_NORMAL, 0, "Erase OTP",              (void *)menu_erase_otp},
    {ITEM_TYPE_NORMAL, 0, "Run Flashloader",        (void *)menu_run_flashloader},
    {ITEM_TYPE_NORMAL, 0, "Analyze Directory",      (void *)menu_analyze},
    {ITEM_TYPE_NORMAL, 0, "Insert Test",            (void *)menu_insert_test},
    {0,0,0}
};
MenuTypedef DebugMenu={"Select type of file to upload", (MenuItemTypedef *)&DebugItems};
//------------------------------------------------------------------------
#endif


//------------------------------------------------------------------------
// Run Menu (recursive code)
//------------------------------------------------------------------------
void RunMenu(MenuTypedef *pMenu)
{
    int i, ch, num, nApp;
    MenuItemTypedef *pItem = pMenu->Items;
    MenuItemTypedef *p;
    pMenuFunc_t pFn;
    int repaintmenu=true;

    while (1) {
        if (repaintmenu) {
            putstring_crlf(pMenu->Title);
            p = pMenu->Items;
            for (num=0; p->pFunc; num++, p++) {
                ShowMenuItemNum(num);
                putstring_crlf(p->Text);
            }
            repaintmenu = false;
        }

        nApp = ReadDirectory();
        putchar('#');
        do {
            ch = CharIsReady() ? getchar() : 0;
            if (ch=='+' || ch=='-') {
                void setvolume(char);
                setvolume(ch);
                ch = 0;
            }
            if (ch==LF)
                ch = 0;
#ifndef NEVER_TIMEOUT
            if (ch==0 && nApp>0) {
                if (MenuTimerMS < KEY_TIMEOUT_MS) {
                    util_waitMS(1);
                    if (++MenuTimerMS==KEY_TIMEOUT_MS) {
                        // Do something on timeout
                    }
                }
            }
            else {
                // keypress stops timeout
                MenuTimerMS = KEY_TIMEOUT_MS;
            }
#endif
        } while (ch==0);
        crlf();

        // Return from menu on CR
        if (ch==CR) {
            return;                     // go back one menu
        }
        // Show menu on space
        else if (ch==' ') {
            repaintmenu = true;            // repaint menu
            continue;
        }

        // Get item index
        i = GetMenuItemNum(ch);

        // Exit on Invalid keypress
        if (i<0 || i>=num) {
            InvalidSelection();
            repaintmenu = true;            // repaint menu
            continue;
        }

        // Handle menu item
        p = &pMenu->Items[i];
        switch (p->type) {
        case ITEM_TYPE_YESNO:
            if (!AreYouSure()) {        // ask yes/no and fall through to next case
                break;
            }
        case ITEM_TYPE_NORMAL:
            putstring_crlf(p->Text);
            pFn = (pMenuFunc_t)(p->pFunc); // function call
            pFn();
            if (p->refresh)             // repaint menu after function call
                repaintmenu = true;
            break;
        case ITEM_TYPE_MENU:
            RunMenu(p->pFunc);          // menu - recursive call
            repaintmenu = true;
            break;
        }
    }
}


//------------------------------------------------------------------------
//------------------------------------------------------------------------
void menu_start(MenuTypedef *m)
{
    putstring_crlf("\r\nPress space to repaint menu.\r\nPress CR to go back one menu.");
    while (1) {
        RunMenu(m);
    }
}

/************************ (C) COPYRIGHT ART *****END OF FILE****/
