#include "common.h"
#include <unistd.h>
#include <string.h>

#include "config.h"

#if defined(HAVE_CURSES_H)
#include <curses.h>
#elif defined(HAVE_NCURSES_H)
#include <ncurses.h>
#elif defined(HAVE_NCURSES_NCURSES_H)
#include <ncurses/ncurses.h>
#endif

static sMenuItem* sMenuItem_new(char* name, int key, char* cmd) {
    sMenuItem* self = MALLOC(sizeof(sMenuItem));

    self->mName = STRDUP(name);
    self->mKey = key;
    self->mCmd = STRDUP(cmd);

    return self;
}

static void sMenuItem_delete(sMenuItem* self)
{
    FREE(self->mName);
    FREE(self->mCmd);

    FREE(self);
}

sMenu* sMenu_new(char* title)
{
    sMenu* self = MALLOC(sizeof(sMenu));

    strcpy(self->mTitle, title);
    self->mScrollTop = 0;
    self->mCursor = 0;

    self->mMenuItems = VECTOR_NEW(5);

    hash_put(gMenu, title, self);

    return self;
}

void sMenu_delete(sMenu* self)
{
    int i;
    for(i=0; i<vector_size(self->mMenuItems); i++) {
        sMenuItem_delete(vector_item(self->mMenuItems, i));
    }
    vector_delete(self->mMenuItems);

    FREE(self);
}

sMenu* gActiveMenu = NULL;
hash_obj* gMenu;

///////////////////////////////////////////////////
// 初期化
///////////////////////////////////////////////////
void menu_init()
{
    gMenu = HASH_NEW(10);
}

void menu_final()
{
    hash_it* i = hash_loop_begin(gMenu);
    while(i != NULL) {
        sMenu_delete(hash_loop_item(i));
        i = hash_loop_next(i);
    }
    hash_delete(gMenu);
}

///////////////////////////////////////////////////
// メニューに追加
///////////////////////////////////////////////////
void menu_append(sMenu* self, char* name, int key, char* cmd)
{
    char buf[256];
    snprintf(buf, 256, " %-40s", name);
    vector_add(self->mMenuItems, sMenuItem_new(buf, key, cmd));
}

void menu_start(char* menu_name)
{
    gActiveMenu = hash_item(gMenu, menu_name);
    if(gActiveMenu) {
        gActiveMenu->mScrollTop = 0;
        gActiveMenu->mCursor = 0;
    }
}

///////////////////////////////////////////////////
// キー入力
///////////////////////////////////////////////////
void menu_input(sMenu* self, int meta, int key)
{
    if(key == 1 || key == KEY_HOME) {        // CTRL-A
        self->mCursor = 0;
    }
    else if(key == 5 || key == KEY_END) {    // CTRL-E
        self->mCursor = vector_size(self->mMenuItems)-1;
    }
    else if(key == 14 || key == 6|| key == KEY_DOWN) {    // CTRL-N CTRL-F
        self->mCursor++;
    }
    else if(key == 16 || key == 2 || key == KEY_UP) {    // CTRL-P CTRL-B
        self->mCursor--;
    }
    else if(key == 4 || key == KEY_NPAGE) { // CTRL-D PAGE DOWN
        self->mCursor+=7;
    }
    else if(key == 21 || key == KEY_PPAGE) { // CTRL-U   PAGE UP
        self->mCursor-=7;
    }
    else if(key == 3 || key == 7 || key == 27) {    // CTRL-C CTRL-G Escape
        gActiveMenu = NULL;
        mclear();
    }
    else if(key == 10 || key == 13) {    // CTRL-M CTRL-J
        gActiveMenu = NULL;
        
        sMenuItem* item = vector_item(self->mMenuItems, self->mCursor);
        
        mclear();
        view(TRUE);
        mrefresh();
        
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        int rcode = saphire_shell(item->mCmd, NULL, pipeout, pipein, STDERR_FILENO);
        saphire_sweep();
        sWFd_flash(pipeout);
        sWFd_delete(pipeout);
        sRFd_delete(pipein);

        mclear();
    }
    else if(key == 12) {// CTRL-L
        mclear_immediately();
    }
    else {
        gActiveMenu = NULL;
        
        int i;
        for(i=0; i<vector_size(self->mMenuItems); i++) {
            sMenuItem* item = (sMenuItem*)vector_item(self->mMenuItems, i);
            
            if(item->mKey == key) {
                mclear();
                view(TRUE);
                mrefresh();
                
                sRFd* pipein = sRFd_new(STDIN_FILENO);
                sWFd* pipeout = sWFd_new(STDOUT_FILENO);
                int rcode = saphire_shell(item->mCmd, NULL, pipeout, pipein, STDERR_FILENO);
                saphire_sweep();
                sWFd_flash(pipeout);
                sWFd_delete(pipeout);
                sRFd_delete(pipein);
            }
        }
        
        mclear();
    }

    while(self->mCursor < 0) {
        self->mCursor += vector_size(self->mMenuItems);
    }
    while(vector_size(self->mMenuItems) != 0 
        && self->mCursor >= vector_size(self->mMenuItems)) 
    {
       self->mCursor -= vector_size(self->mMenuItems);
    }

    const int maxy = mgetmaxy() -2;
    
    if(self->mCursor >= (self->mScrollTop+maxy)) {
        self->mScrollTop += self->mCursor - (self->mScrollTop+maxy) + 1;
    }
    if(self->mCursor < self->mScrollTop) {
        self->mScrollTop = self->mCursor;
    }
}

///////////////////////////////////////////////////
// 描写
///////////////////////////////////////////////////
void menu_view(sMenu* self)
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    int item_maxx = 0;
    int i;
    for(i=0; i<vector_size(self->mMenuItems); i++) {
        sMenuItem* item = (sMenuItem*)vector_item(self->mMenuItems, i);
        
        int len = str_termlen(kUtf8, item->mName);

        if(item_maxx < len) {
            item_maxx = len;
        }
    }
    item_maxx+=2;
    
    if(item_maxx < 43) {
        item_maxx = 43;
    }
    else if(item_maxx > maxx-2) {
        item_maxx = maxx-2;
    }
    
    const int size = vector_size(self->mMenuItems) + 2;
    mbox(0, 2, item_maxx, size < maxy ? size : maxy);

    mattron(kCABold);
    mmvprintw(0, 4, self->mTitle);
    mattroff();

    float f;
    if(vector_size(self->mMenuItems) == 0) {
        f = 0.0;
    }
    else {
        f = ((float)(self->mCursor+1)
                /(float)vector_size(self->mMenuItems))*100;
    }

    mmvprintw((size < maxy ? size: maxy) - 1, 2+item_maxx-8, "(%3d%%)", (int)f);
    for(i=self->mScrollTop;
        (i<vector_size(self->mMenuItems) && i-self->mScrollTop<maxy-2); i++) {
        sMenuItem* item = (sMenuItem*)vector_item(self->mMenuItems, i);
        
        char buf[1024];
        if(strlen(item->mName) > maxx-4) {
            str_cut2(kUtf8, item->mName, maxx-4, buf, 1024);
        }
        else {
            strncpy(buf, item->mName, 1024);
        }
        
        if(i == self->mCursor) {
            mattron(kCAReverse);
            mmvprintw(i-self->mScrollTop+1, 3, buf);
            mattroff();
        }
        else  {
            mmvprintw(i-self->mScrollTop+1, 3, buf);
        }
    }

    mmove(maxy-1, maxx-2);
}
