#include "common.h"
#include <dirent.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

vector_obj* gCCandidate;
static vector_obj* gCExplanation;

static BOOL gCompletionAddSpace;
static BOOL gCompletionSqort;
static BOOL gCompletionDqort;
static BOOL gCompletionLastSqort;
static BOOL gCompletionLastDqort;
static int gCompletionCandidateLen;
static int gEditingLen;
static char gEditing[2048];
static int gCmdLineCompletionMaxRow;

static int gCompletionScrollTop = 0;
static int gCompletionCursor = -1;

vector_obj* gEnvirons;             // 環境変数補完候補

static BOOL name_sort(void* left, void* right)
{
    string_obj* l = left;
    string_obj* r = right;

    return strcmp(string_c_str(l), string_c_str(r)) < 0;
}


///////////////////////////////////////////////////
// 初期化
///////////////////////////////////////////////////
void cmdline_completion_init()
{
#if !HAVE_DECL_ENVIRON
    extern char **environ;
#endif
    gCCandidate = VECTOR_NEW(5);
    gCExplanation = VECTOR_NEW(5);
    
    gEnvirons = VECTOR_NEW(5);
    char** p;
    for(p = environ; *p; p++) {
        char env_name[256];

        char* p2 = env_name;
        char* p3 = *p;

        *p2++ = '$';
        while(*p3 != '=') {
            *p2++ = *p3++;
        }

        char* env = getenv(*p);
        struct stat estat;
        if(stat(env, &estat) >= 0) {
            if(S_ISDIR(estat.st_mode)) {
                *p2++ = '/';
            }
        }
        *p2 = 0;

        vector_add(gEnvirons, STRING_NEW(env_name));
    }

    vector_sort(gEnvirons, name_sort);
}

void cmdline_completion_final()
{
    int i;
    for(i=0; i<vector_size(gCCandidate); i++) {
        string_delete(vector_item(gCCandidate, i));
    }
    vector_delete(gCCandidate);
    for(i=0; i<vector_size(gCExplanation); i++) {
        string_delete(vector_item(gCExplanation, i));
    }
    vector_delete(gCExplanation);
    
    for(i=0; i<vector_size(gEnvirons); i++) {
        string_delete(vector_item(gEnvirons, i));
    }
    vector_delete(gEnvirons);
}

///////////////////////////////////////////////////
// コマンドライン補完候補クリア
///////////////////////////////////////////////////
void cmdline_completion_clear()
{
    int i;
    for(i=0; i<vector_size(gCCandidate); i++) {
        string_delete(vector_item(gCCandidate, i));
    }
    vector_clear(gCCandidate);
    for(i=0; i<vector_size(gCCandidate); i++) {
        string_delete(vector_item(gCExplanation, i));
    }
    vector_clear(gCExplanation);
}

///////////////////////////////////////////////////
// コマンドライン補完1
///////////////////////////////////////////////////
void cmdline_completion(vector_obj* all_candidate, char* editing, BOOL add_space)
{
    string_obj* str = gCmdLine;
    char* cstr = string_c_str(str);

    /// go ///
    gCompletionAddSpace = add_space;

    gEditingLen = strlen(editing);
    strcpy(gEditing, editing);

    cmdline_completion_clear();

    if(strcmp(editing, "") == 0) {
        int i;
        for(i=0; i<vector_size(all_candidate); i++) {
            string_obj* str = vector_item(all_candidate, i);
            vector_add(gCCandidate , STRING_NEW(string_c_str(str)));
        }
    }
    else {
        int i;
        for(i=0; i<vector_size(all_candidate); i++) {
            char* item = string_c_str(vector_item(all_candidate, i));

            if(strstr(item, editing) == item) {
                vector_add(gCCandidate, STRING_NEW(item));
            }
        }
    }

    if(vector_size(gCCandidate) > 0) {
        int l = strlen(editing);

        const int len = strlen(string_c_str(vector_item(gCCandidate, 0)));
        char c = 0;
        BOOL kanji = FALSE;
        while(l < len) {
            BOOL same = TRUE;

            int i;
            for(i= 0; i<vector_size(gCCandidate); i++) {
                char* item = string_c_str(vector_item(gCCandidate, i));

                if(item[l] != (string_c_str(vector_item(gCCandidate, 0)))[l]) {
                    same = FALSE;
                }
            }

            if(same) {
                char tmp[256];
                c = (string_c_str(vector_item(gCCandidate, 0)))[l];
                
                BOOL kanji_before = kanji;
                if(gKanjiCode != kUtf8) {
                    if(kanji) {
                        kanji = FALSE;
                    }
                    else {
                        if(is_kanji(kUtf8, c)) {
                            kanji = TRUE;
                        }
                    }
                }

                if(!gCompletionSqort && !gCompletionDqort && (gKanjiCode==kUtf8 || !kanji_before) && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
                {
                    sprintf(tmp, "\\%c", c);
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;

                    gEditingLen++;
                    sprintf(gEditing + strlen(gEditing), "%c", c);
                }
                else {
                    sprintf(tmp, "%c", c);
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor++;

                    gEditingLen++;
                    strcat(gEditing, tmp);
                }
            }
            else {
                break;
            }

            l++;
        }

        if(kanji) {
            gCmdLineCursor--;
            string_erase(str, gCmdLineCursor, 1);
            gEditingLen--;
            gEditing[strlen(gEditing)-1] = 0;
        }

        if(l == len && vector_size(gCCandidate) == 1) {
            if(c != '/') {
                if(gCompletionSqort && !gCompletionLastSqort) {
                    char tmp[128];
                    sprintf(tmp, "' ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor++;
                }
            }

            cmdline_completion_clear();
        }
    }
}

///////////////////////////////////////////////////
// コマンドライン補完2
///////////////////////////////////////////////////
void cmdline_completion2(vector_obj* all_candidate, char* editing, BOOL add_space, int candidate_len)
{
    string_obj* str = gCmdLine;
    char* cstr = string_c_str(str);

    /// go ///
    gCompletionAddSpace = add_space;
    gCompletionCandidateLen = candidate_len;
    gEditingLen = strlen(editing);
    strcpy(gEditing, editing);

    cmdline_completion_clear();

    if(strcmp(editing, "") == 0) {
        int i;
        for(i=0; i<vector_size(all_candidate); i++) {
            vector_obj* array = vector_item(all_candidate, i);

            vector_add(gCCandidate, vector_item(array, 0));
            vector_add(gCExplanation, vector_item(array, 1));
        }
    }
    else {
        int i;
        for(i=0; i<vector_size(all_candidate); i++) {
            vector_obj* array = vector_item(all_candidate, i);

            if(strstr(string_c_str(vector_item(array, 0)), editing) == string_c_str(vector_item(array, 0)))
            {
                vector_add(gCCandidate, vector_item(array, 0));
                vector_add(gCExplanation, vector_item(array, 1));
            }
        }
    }

    if(vector_size(gCCandidate) > 0) {
        int l = strlen(editing);

        const int len = strlen((char*)vector_item(gCCandidate, 0));
        char c = 0;
        BOOL kanji = FALSE;
        while(l < len) {
            BOOL same = TRUE;

            int i;
            for(i= 0; i<vector_size(gCCandidate); i++) {
                char* item = (char*)vector_item(gCCandidate, i);

                if(item[l] != ((char*)vector_item(gCCandidate, 0))[l]) {
                    same = FALSE;
                }
            }

            if(same) {
                char tmp[256];
                c = ((char*)vector_item(gCCandidate, 0))[l];
                
                BOOL kanji_before = kanji;
                if(gKanjiCode != kUtf8) {
                    if(kanji) {
                        kanji = FALSE;
                    }
                    else {
                        if(is_kanji(kUtf8, c)) {
                            kanji = TRUE;
                        }
                    }
                }

                if(!gCompletionSqort && !gCompletionDqort && (gKanjiCode==kUtf8 || !kanji_before) && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
                {
                    sprintf(tmp, "\\%c", c);
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;

                    gEditingLen++;
                    sprintf(gEditing + strlen(gEditing), "%c", c);
                }
                else {
                    sprintf(tmp, "%c", c);
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor++;

                    gEditingLen++;
                    strcat(gEditing, tmp);
                }
            }
            else {
                break;
            }

            l++;
        }

        if(kanji) {
            gCmdLineCursor--;
            string_erase(str, gCmdLineCursor, 1);
            gEditingLen--;
            gEditing[strlen(gEditing)-1] = 0;
        }

        if(l == len && vector_size(gCCandidate) == 1 && c != '/') {
            if(c != '/') {
                if(gCompletionSqort && !gCompletionLastSqort) {
                    char tmp[128];
                    sprintf(tmp, "' ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert(str, gCmdLineCursor, tmp);
                    gCmdLineCursor++;
                }
            }

            cmdline_completion_clear();
        }
    }
}

///////////////////////////////////////////////////
// ファイル名の補完
///////////////////////////////////////////////////
void cmdline_completion_file(char* editing_dir, char* editing_file, char* editing, int editing_position)
{
    char editing_dir2[kCmdLineMax];
    strcpy(editing_dir2, editing_dir);

    /// editing_dirが存在するなら ///
    if(strcmp(editing_dir2, "") != 0) {
        /// 環境変数から始まっている場合 ///
        if(editing_dir2[0] == '$') {
            char new_editing_dir[kCmdLineMax];
            char env_name[256];

            char *p = editing_dir2;
            p++;
            
            
            char *p2 = env_name;
            
            
            while(*p != '/') {
                *p2++ = *p++;                
            }
            *p2 = 0;
            
            char* env = getenv(env_name);
            if(env) {
                strcpy(new_editing_dir, env);
                strcat(new_editing_dir, p);

                strcpy(editing_dir2, new_editing_dir);
            }
        }

        /// チルダから始まっている場合 ///
        else if(editing_dir2[0] == '~') {
            char new_editing_dir[kCmdLineMax];

            char* p = editing_dir2;
            p++;

            strcpy(new_editing_dir, getenv("HOME"));
            strcat(new_editing_dir, p);

            strcpy(editing_dir2, new_editing_dir);
        }

        DIR* dir = opendir(editing_dir2);
        vector_obj* files = VECTOR_NEW(10);

        if(dir) {
            struct dirent* entry;
            while(entry = readdir(dir)) {
                if(strcmp(entry->d_name, ".") != 0
                        && strcmp(entry->d_name, "..") != 0)
                {
                    char buf[PATH_MAX];

                    struct stat _stat;
                    strcpy(buf, editing_dir2);
                    strcat(buf, entry->d_name);
                    stat(buf, &_stat);

                    strcpy(buf, entry->d_name);
                    if(S_ISDIR(_stat.st_mode)) {
                        strcat(buf, "/");
                    }

                    vector_add(files, STRING_NEW(buf));
                }
            }

            closedir(dir);

            vector_sort(files,name_sort);

            cmdline_completion(files, editing_file, TRUE);
        }

        int i;
        for(i=0; i<vector_size(files); i++) {
            string_delete(vector_item(files, i));
        }
        vector_delete(files);
    }
    /// not exist editing_dir ///
    else {
        /// editing enverion ///
        if(editing[0] == '$') {
            cmdline_completion(gEnvirons, editing, TRUE);
        }
        else if(editing[0] == '~') {
            vector_obj* tilda = VECTOR_NEW(10);
            vector_add(tilda, STRING_NEW("~/"));

            cmdline_completion(tilda, editing, TRUE);
        }
        else {
            DIR* dir = opendir(".");
            vector_obj* files = VECTOR_NEW(100);

            if(dir) {
                struct dirent* entry;
                while(entry = readdir(dir)) {
                    if(strcmp(entry->d_name, ".") != 0
                            && strcmp(entry->d_name, "..") != 0)
                    {
                        char buf[PATH_MAX];

                        struct stat _stat;
                        strcpy(buf, "./");
                        strcat(buf, entry->d_name);
                        stat(buf, &_stat);

                        strcpy(buf, entry->d_name);
                        if(S_ISDIR(_stat.st_mode)) {
                            strcat(buf, "/");
                        }
                        vector_add(files, STRING_NEW(buf));
                    }
                }

                closedir(dir);

                vector_sort(files, name_sort);
                cmdline_completion(files, editing_file, TRUE);
            }

            int i;
            for(i=0; i<vector_size(files); i++) {
                string_delete(vector_item(files, i));
            }
            vector_delete(files);
        }
    }
}

///////////////////////////////////////////////////
// プログラム名の補完
///////////////////////////////////////////////////
void cmdline_completion_program(char* editing_dir, char* editing_file, char* editing, int editing_position)
{
    if(strcmp(editing_dir, "") != 0) {
        cmdline_completion_file(editing_dir, editing_file, editing, editing_position);
    }
    else {
        cmdline_completion(gPrograms, editing, TRUE);
    }
}

///////////////////////////////////////////////////
// コマンドラインをパース
///////////////////////////////////////////////////
void cmdline_parse(char* cmdname, char* editing, char* editing_before, char* editing_dir, char* editing_file, int* editing_position, vector_obj** earray, BOOL* squote, BOOL* dquote, BOOL* last_squote, BOOL* last_dquote)
{
    char* cmdline = string_c_str(gCmdLine);

    BOOL qort = FALSE;
    char* p = cmdline;
    char* e = editing;

    *e = 0;
    *editing_position = 0;
    strcpy(cmdname, "");
    strcpy(editing_before, "");
    *squote = FALSE;
    *dquote = FALSE;
    *last_squote = FALSE;
    *last_dquote = FALSE;
   
    *earray = VECTOR_NEW(10);
   
    while(*p && *p == ' ') p++;

    while(*p && (p-cmdline)<gCmdLineCursor) {
        if(*dquote && (*p == '`' || *(p-1)=='$' && *p=='(' )) {
            *dquote = FALSE;
        }
        
        if(!*squote && !*dquote && *p=='\\') {
            p++;

            qort = TRUE;
        }
        else if(!*dquote && !qort && *p=='\'') {
            p++;

            *squote = !*squote;
        }
        else if(!*squote && !qort && *p=='"') {
            p++;

            *dquote = !*dquote;
        }
        else if(!*squote && !*dquote && !qort && *p == ' ') {
            p++;

            *e = 0;
            vector_add(*earray, STRING_NEW(editing));
            strcpy(editing_before, editing);

            if(editing[0] != '%' && editing[0] != '-') {
                if(*editing_position == 0) strcpy(cmdname, editing);
                (*editing_position)++;
            }

            e = editing;

            while(*p && *p == ' ') p++;
        }
        else if(!qort && !*squote && !*dquote && (*p==',' || *p=='>'|| *p=='<'))
        {
            p++;

            *e = 0;
            vector_add(*earray, STRING_NEW(editing));
            strcpy(editing_before, editing);

            if(editing[0] != '%' && editing[0] != '-') {
                if(*editing_position == 0) strcpy(cmdname, editing);
                (*editing_position)++;
            }

            e = editing;

            while(*p && *p == ' ') p++;
        }
        else if(!qort && !*squote && !*dquote && (*p == '|' || *p == ';' || *p == '&' || *p=='(' || *p=='`'))
        {
            p++;

            *e = 0;
            vector_add(*earray, STRING_NEW(editing));
            strcpy(editing_before, editing);

            e = editing;

            strcpy(cmdname, "");                
            *editing_position = 0;
            int i;
            for(i=0; i<vector_size(*earray); i++) {
                string_delete(vector_item(*earray, i));
            }
            vector_clear(*earray);
 
            while(*p && *p == ' ') p++;
        }
        else {
            *e++ = *p++;

            qort = FALSE;
        }
    }
    *e = 0;
    vector_add(*earray, STRING_NEW(editing));
    //strcpy(editing_before, editing);
    if(editing[0] != '%' && editing[0] != '-')
    {
        if(*editing_position == 0) strcpy(cmdname, editing);
    }

    if(strlen(cmdline) >= gCmdLineCursor && cmdline[gCmdLineCursor] == '\'') {
        *last_squote = TRUE;
    }

    if(strlen(cmdline) >= gCmdLineCursor && cmdline[gCmdLineCursor] == '"') {
        *last_dquote = TRUE;
    }

    /// divide editing into dir part and file part ///
    strcpy(editing_dir, "");
    strcpy(editing_file, "");

    e = editing + strlen(editing);
    while(e >= editing) {
        if(*e == '/') {
            memcpy(editing_dir, editing, e - editing + 1);
            editing_dir[e-editing + 1] = 0;

            memcpy(editing_file, e + 1, editing + strlen(editing) - e);
            editing_file[editing + strlen(editing) -e] = 0;
            break;
        }

        e--;
    }

    if(strcmp(editing_dir, "") == 0) {
        strcpy(editing_file, editing);
    }

    int i;
    for(i=0; i<vector_size(*earray); i++) {
        string_delete(vector_item(*earray, i));
    }
    vector_delete(*earray);

}

///////////////////////////////////////////////////
// コマンドラインでタブが押された時の処理
///////////////////////////////////////////////////
void cmdline_completion_main()
{
    char* cmdline = string_c_str(gCmdLine);

    if(vector_size(gCCandidate) > 0)
    {
        gCompletionScrollTop = 0;
        gCompletionCursor = 0;
    }
    else {
        /// コマンドラインパース ///
        int editing_position;
        char cmdname[kCmdLineMax];
        char editing[kCmdLineMax];
        char editing_before[kCmdLineMax];
        vector_obj* earray;
        BOOL squote;
        BOOL dquote;
        BOOL last_squote;
        BOOL last_dquote;
        char editing_dir[kCmdLineMax];
        char editing_file[kCmdLineMax];

        cmdline_parse(cmdname, editing, editing_before, editing_dir, editing_file, &editing_position, &earray, &squote, &dquote, &last_squote, &last_dquote);

        gCompletionScrollTop = 0;

        gCompletionSqort = squote;
        gCompletionDqort = dquote;
        gCompletionLastSqort = last_squote;
        gCompletionLastDqort = last_dquote;

        if(editing_position == 0) {
            cmdline_completion_program(editing_dir, editing_file, editing
                                , editing_position);
        }
        else {
            cmdline_completion_file(editing_dir, editing_file, editing
                                    , editing_position);
        }

        /// gCmdLineCompletionMaxRowを得る ///
        const int maxx = mgetmaxx();
        const int maxy = mgetmaxy();

        gCmdLineCompletionMaxRow = 10;
        int i;
        for(i=0; i<vector_size(gCCandidate); i++) {
            char* str = string_c_str(vector_item(gCCandidate, i));
            if(str_termlen(kUtf8, str) > maxx/gCmdLineCompletionMaxRow) {
                int j;
                for(j=gCmdLineCompletionMaxRow; j>1; j--) {
                    if(str_termlen(kUtf8, str) < maxx/j) {
                        break;
                    }
                }
                gCmdLineCompletionMaxRow = j;
            }
        }
    }
}

///////////////////////////////////////////////////
// 描写
///////////////////////////////////////////////////
void cmdline_completion_view()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    if(vector_size(gCExplanation) == 0) {   
        int row = 0;
        int line = 0;
        int i;
        for(i= gCompletionScrollTop; i<vector_size(gCCandidate); i++) {
            char* item = (char*)string_c_str(vector_item(gCCandidate, i));

            if(row == gCmdLineCompletionMaxRow) {
                row = 0;
                line++;
            }

            if(line >= maxy-2) break;

            int editing_len;
            if(gKanjiCode == kUtf8) {
                editing_len = str_termlen(kUtf8, gEditing);
            }
            else {
                editing_len = strlen(gEditing);
            }
            int editing_len2 = strlen(gEditing);

            if(editing_len < maxx/gCmdLineCompletionMaxRow-1) {
                char tmp[4096];
                str_cut2(kUtf8, item, editing_len, tmp, 4096);

                char tmp2[4096];
                str_cut2(kUtf8, item + editing_len2, maxx/gCmdLineCompletionMaxRow-1-editing_len, tmp2, 4096);

                if(gCompletionCursor == i) {
                    mattron(kCAReverse);
                    mmvprintw(line, row * (maxx/gCmdLineCompletionMaxRow), "%s", tmp);
                    mprintw("%s", tmp2);
                    mattroff();
                }
                else {
                    char* env = getenv("CMDLINE_COLOR");
                    if(env && strcmp(env, "1") == 0) 
                        mattron(kCACyan); 
                    else 
                        mattron(kCABold);

                    mmvprintw(line, row * (maxx/gCmdLineCompletionMaxRow)
                                        , "%s", tmp);
                    mattroff();
                    mprintw("%s", tmp2);
                }

                row++;
            }
            else {
                char tmp[4096];
                str_cut2(kUtf8, item, maxx/gCmdLineCompletionMaxRow-1, tmp, 4096);

                if(gCompletionCursor == i) {
                    mattron(kCAReverse);
                    mmvprintw(line, row * (maxx/gCmdLineCompletionMaxRow), "%s", tmp);
                    mattroff();
                }
                else {
                    char* env = getenv("CMDLINE_COLOR");
                    if(env && strcmp(env, "1") == 0) 
                        mattron(kCACyan); 
                    else 
                        mattron(kCABold);
                    mmvprintw(line, row * (maxx/gCmdLineCompletionMaxRow), "%s", tmp);
                    mattroff();
                }

                row++;
            }
        }
    }
    else {
        int line = 0;
        int i;
        for(i= gCompletionScrollTop; i<vector_size(gCCandidate); i++) {
            char* item = (char*)string_c_str(vector_item(gCCandidate, i));
            char* explanation = (char*)string_c_str(
                            vector_item(gCExplanation, i));

            char tmp[256];
            sprintf(tmp, "%%-%ds %%s", gCompletionCandidateLen);

            char buf[4096];
            sprintf(buf, tmp, item, explanation);

            char tmp2[4096];
            str_cut2(kUtf8, buf, maxx-1, tmp2, 256);

            if(gCompletionCursor == i) mattron(kCAReverse);        
            mmvprintw(line, 0, "%s", tmp2);
            if(gCompletionCursor == i) mattroff();
            line++;

            if(line >= maxy-2) break;
        }
    }
}

///////////////////////////////////////////////////
// 入力処理
///////////////////////////////////////////////////
void cmdline_completion_input(int meta, int key)
{
    if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        if(vector_size(gCExplanation) == 0) 
            gCompletionCursor+=gCmdLineCompletionMaxRow;
        else
            gCompletionCursor++;
    }
    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        if(vector_size(gCExplanation) == 0)
            gCompletionCursor-=gCmdLineCompletionMaxRow;
        else
            gCompletionCursor--;
    }
    else if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        gCompletionCursor++;
    }
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        gCompletionCursor--;
    }
    else if(key == 9) {    // CTRL-I
        gCompletionCursor++;
    }
    // CTRL-J CTRL-M
    else if((key == 10 || key == 13) && gCompletionCursor != -1) {
        char* candidate = (char*)string_c_str(
                            vector_item(gCCandidate, gCompletionCursor));

        BOOL kanji[2048];
        if(gKanjiCode != kUtf8) {
            BOOL kanji_ = FALSE;
            int i;
            for(i=0; i<strlen(candidate); i++) {
                if(kanji_) {
                    kanji_ = FALSE;
                }
                else {
                    if(is_kanji(kUtf8, candidate[i])) {
                        kanji_ = TRUE;
                    }
                }

                kanji[i] = kanji_;
            }
        }

        char tmp[2048];
        char* p = tmp;
        int i;
        for(i=gEditingLen; i<strlen(candidate); i++) {
            char c = candidate[i];
            
            if(!gCompletionSqort && !gCompletionDqort && (gKanjiCode==kUtf8 || !kanji[i-1]) && (c==' ' || c=='!' || c=='*' || c=='>' || c=='&' || c=='~' || c=='#' || c =='$' || c=='(' || c==')' || c=='\\' || c=='|' || c=='[' || c==']' || c=='{' || c=='}' || c==';' || c=='\'' || c=='"' || c=='<' || c=='>' || c=='?'))
            {
                *p++ = '\\';
                *p++ = c;
            }
            else {
                *p++ = c;
            }
        }
        if(*(p-1) != '/') {
            if(gCompletionSqort && !gCompletionLastSqort) {
                *p++ = '\'';
                *p++ = ' ';
            }
            else if(gCompletionDqort && !gCompletionLastDqort) {
                *p++ = '"';
                *p++ = ' ';
            }
            else if(gCompletionAddSpace && !gCompletionLastSqort
                    && !gCompletionLastDqort)
            {
                *p++ = ' ';
            }
        }
        *p = 0;

        
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 補完文字列を挿入 ///
        string_insert(str, gCmdLineCursor, tmp);
        gCmdLineCursor+=strlen(tmp);

        cmdline_completion_clear();
        gCompletionCursor = -1;
        cmdline_hcandidate_refresh();
        return;
    }
    else if(key == 4 || key == 22 || key == KEY_NPAGE) {    // CTRL-D CTRL-V
        if(vector_size(gCExplanation) == 0) 
            gCompletionCursor+=20;
        else
            gCompletionCursor+=5;
    }
    else if(key == 21 || meta==1&&key=='v' || key == KEY_PPAGE) {    // CTRL-U Meta-v
        if(vector_size(gCExplanation) == 0)
            gCompletionCursor-=20;
        else
            gCompletionCursor-=5;
    }
    else if(key == 3 || key == 7 || key == 27 || key == 8) { // CTRL-C CTRL-G Escape
        gCompletionCursor = -1;
        gCompletionScrollTop = 0;

        cmdline_completion_clear();
        return;
    }
    else {
        gCompletionCursor = -1;
        gCompletionScrollTop = 0;
        
        cmdline_completion_clear();

        cmdline_input(meta, key);
    }

    if(gCompletionCursor < 0) {
        gCompletionCursor = vector_size(gCCandidate)-1;
    }
    if(gCompletionCursor >= vector_size(gCCandidate)) {
        gCompletionCursor = 0;
        //        gCompletionCursor = vector_size(gCCandidate)-1;
    }

    const int maxy = mgetmaxy();
    if(vector_size(gCExplanation) == 0) {
        if(gCompletionCursor < gCompletionScrollTop) {
            gCompletionScrollTop = (gCompletionCursor/((maxy-2)*gCmdLineCompletionMaxRow))*((maxy-2)*gCmdLineCompletionMaxRow);
        }
        if(gCompletionCursor > gCompletionScrollTop + (maxy-3)*gCmdLineCompletionMaxRow) {
            gCompletionScrollTop = (gCompletionCursor/((maxy-2)*gCmdLineCompletionMaxRow))*((maxy-2)*gCmdLineCompletionMaxRow);
        }
    }
    else {
        if(gCompletionCursor < gCompletionScrollTop) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
        if(gCompletionCursor > gCompletionScrollTop + maxy-3) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
    }
}
