#include "common.h"
#include <dirent.h>

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];

bool gCompletionMenu = false;
int gCompletionScrollTop = 0;
int gCompletionCursor = -1;

VALUE gPrograms;             // ?v???O???????⊮????
VALUE gEnvirons;             // ???ϐ??⊮????

///////////////////////////////////////////////////////////////////////////////
// ??????
///////////////////////////////////////////////////////////////////////////////
void cmdline_init_after_reading_rc_file()
{
#if !HAVE_DECL_ENVIRON
    extern char **environ;
#endif
    
    gEnvirons = rb_ary_new();
    for(char** 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;

        rb_ary_push(gEnvirons, rb_str_new2(env_name));
    }

    gEnvirons = rb_ary_sort(gEnvirons);
}

///////////////////////////////////////////////////////////////////////////////
// ?v???O???????⊮???????ēǂݍ???
///////////////////////////////////////////////////////////////////////////////
void cmdline_rehash()
{
    hash_obj* check = hash_new(10);

    gPrograms = rb_ary_new();
    rb_ary_push(gPrograms, rb_str_new2("read"));
    rb_ary_push(gPrograms, rb_str_new2("export"));

    /// $PATH?ɂ????t?@?C?????S??gPrograms?ɓ????Ă??? ///
    char* path = getenv("PATH");
    if(path == NULL) {
        fprintf(stderr, "$PATH is NULL\n");
        exit(1);
    }

    char* p = path;
    char buf[512];
    char* p2 = buf;

    while(*p) {
        if(*p != ':') {
            *p2++ = *p;
        }
        else {
            *p2 = 0;

            DIR* dir = opendir(buf);
            if(dir) {
                struct dirent* entry;
                while(entry = readdir(dir)) {
                    char path2[512];
                    sprintf(path2, "%s/%s", buf, entry->d_name);

                    struct stat stat_;
                    memset(&stat_, 0, sizeof(struct stat));
                    stat(path2, &stat_);

#if defined(__CYGWIN__)
                    if(strcmp(entry->d_name, ".") != 0
                            && strcmp(entry->d_name, "..") != 0)
                    {
                        if(strstr(entry->d_name, ".exe")) {
                            char tmp[PATH_MAX];
                            memcpy(tmp
                                , entry->d_name
                                , strstr(entry->d_name, ".exe") - entry->d_name
                                );
                            tmp[strstr(entry->d_name, ".exe") - entry->d_name]
                             = 0;

                            if(hash_item(check, tmp) == NULL) {
                                hash_put(check, tmp, (void*)1);
                                rb_ary_push(gPrograms, rb_str_new2(tmp));
                            }
                        }
                        else if(strstr(entry->d_name, ".EXE")) {
                            char tmp[PATH_MAX];
                            memcpy(tmp
                                , entry->d_name
                                , strstr(entry->d_name, ".EXE") - entry->d_name
                                );
                            tmp[strstr(entry->d_name, ".EXE") - entry->d_name]
                             = 0;


                            if(hash_item(check, tmp) == NULL) {
                                hash_put(check, tmp, (void*)1);
                                rb_ary_push(gPrograms, rb_str_new2(tmp));
                            }
                        }
#else
                    if(strcmp(entry->d_name, ".") != 0
                            && strcmp(entry->d_name, "..") != 0
                            &&
                            (stat_.st_mode & S_IXUSR
                             ||stat_.st_mode & S_IXGRP
                             ||stat_.st_mode & S_IXOTH))
                    {
                        if(hash_item(check, entry->d_name) == NULL) {
                            hash_put(check, entry->d_name, (void*)1);
                            rb_ary_push(gPrograms, rb_str_new2(entry->d_name));
                        }
#endif
                    }
                }

                closedir(dir);
            }

            p2 = buf;
        }

        p++;
    }

    *p2 = 0;

    DIR* dir = opendir(buf);
    if(dir) {
        struct dirent* entry;
        while(entry = readdir(dir)) {
            if(strcmp(entry->d_name, ".") != 0
                    && strcmp(entry->d_name, "..") != 0)
            {
                rb_ary_push(gPrograms, rb_str_new2(entry->d_name));
            }
        }

        closedir(dir);
    }

    /// gPrograms?ɃG?C???A?X???????? ///    
    const int len = vector_size(gAlias);
    for(int i=0; i<len; i++) {
        sAlias* alias = (sAlias*)vector_item(gAlias, i);

        rb_ary_push(gPrograms, rb_str_new2(alias->mAlias));
    }

    gPrograms = rb_ary_sort(gPrograms);
    hash_delete(check);
}

///////////////////////////////////////////////////////////////////////////////
// ?R?}???h???C???⊮?????N???A
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion_clear()
{
    for(int i=0; i<vector_size(gCCandidate); i++) {
        FREE(vector_item(gCCandidate, i));
    }
    vector_clear(gCCandidate);
    for(int i=0; i<vector_size(gCExplanation); i++) {
        FREE(vector_item(gCExplanation, i));
    }
    vector_clear(gCExplanation);
}

///////////////////////////////////////////////////////////////////////////////
// ?R?}???h???C???⊮1
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion(VALUE all_candidate, char* editing, bool add_space)
{
    string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
    char* cstr = string_c_str(str);

    /// ?q?X?g???[???ҏW?????Ȃ??R?s?[?𖖔??ɍ쐬 ///
    if(gCLCursor != vector_size(gCmdLine)-1) {
        cmdline_delete_last_str_if_length_0();
        
        vector_add(gCmdLine, string_new(cstr));
        gCLCursor = vector_size(gCmdLine) -1;

        str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        cstr = string_c_str(str);
    }

    /// go ///
    gCompletionAddSpace = add_space;

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

    cmdline_completion_clear();

    if(strcmp(editing, "") == 0) {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            vector_add(gCCandidate
                    , STRDUP(RSTRING(rb_ary_entry(all_candidate, i))->ptr));
        }
    }
    else {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            Check_Type(rb_ary_entry(all_candidate, i), T_STRING);
            char* item = RSTRING(rb_ary_entry(all_candidate, i))->ptr;


            //            err_msg("%s %s", item, editing);


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

    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;

            for(int 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(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, gCursor, tmp);
                    gCursor+=2;

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

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

            l++;
        }

        if(kanji) {
            gCursor--;
            string_erase(str, gCursor, 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, gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert(str, gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert(str, gCursor, tmp);
                    gCursor++;
                }
            }

            cmdline_completion_clear();
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?R?}???h???C???⊮2
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion2(VALUE all_candidate, char* editing, bool add_space, int candidate_len)
{
    string_obj* str = (string_obj*) vector_item(gCmdLine, gCLCursor);
    char* cstr = string_c_str(str);

    /// ?q?X?g???[???ҏW?????Ȃ??R?s?[?𖖔??ɍ쐬 ///
    if(gCLCursor != vector_size(gCmdLine)-1) {
        cmdline_delete_last_str_if_length_0();
        
        vector_add(gCmdLine, string_new(cstr));
        gCLCursor = vector_size(gCmdLine) -1;

        str = (string_obj*)vector_item(gCmdLine, gCLCursor);
        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) {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            VALUE array = rb_ary_entry(all_candidate, i);

            Check_Type(array, T_ARRAY);

            if(RARRAY(array)->len != 2) {
                gErrMsgCancel = false; 
                err_msg("evalid candidate type in completion2");
                return;
            }

            VALUE candidate = rb_ary_entry(array, 0);
            VALUE explanation = rb_ary_entry(array, 1);

            Check_Type(candidate, T_STRING);
            Check_Type(explanation, T_STRING);

            vector_add(gCCandidate, STRDUP(RSTRING(candidate)->ptr));
            vector_add(gCExplanation, STRDUP(RSTRING(explanation)->ptr));
        }
    }
    else {
        for(int i=0; i<RARRAY(all_candidate)->len; i++) {
            VALUE array = rb_ary_entry(all_candidate, i);

            Check_Type(array, T_ARRAY);

            if(RARRAY(array)->len != 2) {
                gErrMsgCancel = false; 
                err_msg("evalid candidate type in completion2");
                return;
            }

            VALUE candidate = rb_ary_entry(array, 0);
            VALUE explanation = rb_ary_entry(array, 1);

            Check_Type(candidate, T_STRING);
            Check_Type(explanation, T_STRING);

            if(strstr(RSTRING(candidate)->ptr, editing) == RSTRING(candidate)->ptr)
            {
                vector_add(gCCandidate, STRDUP(RSTRING(candidate)->ptr));
                vector_add(gCExplanation, STRDUP(RSTRING(explanation)->ptr));
            }
        }
    }

    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;

            for(int 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(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, gCursor, tmp);
                    gCursor+=2;

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

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

            l++;
        }

        if(kanji) {
            gCursor--;
            string_erase(str, gCursor, 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, gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionDqort && !gCompletionLastDqort) {
                    char tmp[128];
                    sprintf(tmp, "\" ");
                    string_insert(str, gCursor, tmp);
                    gCursor+=2;
                }
                else if(gCompletionAddSpace && !gCompletionLastSqort
                        && !gCompletionLastDqort)
                {
                    char tmp[128];
                    sprintf(tmp, " ");
                    string_insert(str, gCursor, tmp);
                    gCursor++;
                }
            }

            cmdline_completion_clear();
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?v???O???????̕⊮
///////////////////////////////////////////////////////////////////////////////
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);
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?t?@?C?????̕⊮
///////////////////////////////////////////////////////////////////////////////
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) {
        /// ???ϐ??????n?܂��Ă????ꍇ ///
        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);
            }
        }

        /// ?`???_?????n?܂��Ă????ꍇ ///
        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);
        VALUE files = rb_ary_new();

        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, "/");
                    }

#if (defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)) && !defined(__CYGWIN7__)
                    char buf2[PATH_MAX];
                    enum eKanjiCode code = kanji_encode_type(buf);
                    if(gKanjiCodeFileName == kUnknown || code == kAscii || gKanjiCodeFileName == code) {
                        if(kanji_convert(buf, buf2, PATH_MAX, code, gKanjiCode) == -1) {
                            strcpy(buf2, buf);
                        }
                        gCmdLineEncode = code;
                        rb_ary_push(files, rb_str_new2(buf2));
                    }

#else
                    rb_ary_push(files, rb_str_new2(buf));
#endif
                }
            }

            closedir(dir);

            files = rb_ary_sort(files);

            cmdline_completion(files, editing_file, true);
        }
    }
    /// not exist editing_dir ///
    else {
        /// editing enverion ///
        if(editing[0] == '$') {
            cmdline_completion(gEnvirons, editing, true);
        }
        else if(editing[0] == '~') {
            VALUE tilda = rb_ary_new();
            rb_ary_push(tilda, rb_str_new2("~/"));

            cmdline_completion(tilda, editing, true);
        }
        else {
            DIR* dir = opendir(".");
            VALUE files = rb_ary_new();

            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, "/");
                        }

#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H)
                        char buf2[PATH_MAX];
                        enum eKanjiCode code = kanji_encode_type(buf);
                        
                        if(gKanjiCodeFileName == kUnknown || code == kAscii || gKanjiCodeFileName == code) {
                            if(kanji_convert(buf, buf2, PATH_MAX, code, gKanjiCode) == -1) {
                                strcpy(buf2, buf);
                            }
                            gCmdLineEncode = code;
                            rb_ary_push(files, rb_str_new2(buf2));
                        }
#else
                        rb_ary_push(files, rb_str_new2(buf));
#endif
                    }
                }

                closedir(dir);

                files = rb_ary_sort(files);
                cmdline_completion(files, editing_file, true);
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?R?}???h???C?????p?[?X
///////////////////////////////////////////////////////////////////////////////
void cmdline_parse(char* cmdname, char* editing, char* editing_before, char* editing_dir, char* editing_file, int& editing_position, VALUE& earray, bool& squote, bool& dquote, bool& last_squote, bool& last_dquote)
{
    char* cmdline = string_c_str((string_obj*)vector_item(gCmdLine, gCLCursor));

    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 = rb_ary_new();

    while(*p && *p == ' ') p++;

    while(*p && (p-cmdline)<gCursor) {
        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;
            rb_ary_push(earray, rb_str_new2(editing));
            strcpy(editing_before, editing);

            if(strcmp(editing, "do") == 0 || strcmp(editing, "then") == 0
                    || strcmp(editing, "else") == 0)
            {
                editing_position = 0;
                earray = rb_ary_new();
            }
            else {
                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;
            rb_ary_push(earray, rb_str_new2(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;
            rb_ary_push(earray, rb_str_new2(editing));
            strcpy(editing_before, editing);

            e = editing;

            strcpy(cmdname, "");                
            editing_position = 0;
            earray = rb_ary_new();
 
            while(*p && *p == ' ') p++;
        }
        else {
            *e++ = *p++;

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

    if(strlen(cmdline) >= gCursor && cmdline[gCursor] == '\'') {
        last_squote = true;
    }

    if(strlen(cmdline) >= gCursor && cmdline[gCursor] == '"') {
        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);
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?R?}???h???C???Ń^?u???????ꂽ???̏???
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion_main()
{
    char* cmdline = string_c_str((string_obj*)vector_item(gCmdLine, gCLCursor));

    if(vector_size(gCCandidate) > 0 && gCursorBefore == gCursor)
    {
        gCompletionMenu = true;
        gCompletionScrollTop = 0;
        gCompletionCursor = 0;
    }
    else {
        /// ?R?}???h???C???p?[?X ///
        int editing_position;
        char cmdname[kAliasMax];
        char editing[kAliasMax];
        char editing_before[kAliasMax];
        VALUE earray;
        bool squote;
        bool dquote;
        bool last_squote;
        bool last_dquote;
        char editing_dir[kAliasMax];
        char editing_file[kAliasMax];

        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;
    
        /// Ruby???x???ɍs?? ///
        rb_funcall(rb_cObject, rb_intern("completion_hook"),7, rb_str_new2(editing), rb_str_new2(editing_dir), rb_str_new2(editing_file), rb_str_new2(editing_before), earray, rb_str_new2(cmdname), INT2NUM(editing_position));


        gCursorBefore = gCursor;
    }
}

///////////////////////////////////////////////////////////////////////////////
// ?`??
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion_view()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();
    if(vector_size(gCExplanation) == 0) {   
        int row = 0;
        int line = 0;
        for(int i= gCompletionScrollTop; i<vector_size(gCCandidate); i++) {
            char* item = (char*)vector_item(gCCandidate, i);

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

            if(line >= maxy-2) break;

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

            if(editing_len < maxx/4-1) {
                char tmp[4096];
                if(gKanjiCode == kUtf8)
                    str_cut2(item, editing_len, tmp, 4096);
                else
                    cut(item, tmp, editing_len);

                char tmp2[4096];
                if(gKanjiCode == kUtf8)
                    str_cut2(item + editing_len2, maxx/4-1-editing_len, tmp2, 4096);
                else
                    cut(item + editing_len2, tmp2, maxx/4-1-editing_len);

                if(gCompletionCursor == i) {
                    mattron(kCAReverse);
                    mmvprintw(line, row * (maxx/4), "%s", tmp);
                    mprintw("%s", tmp2);
                    mattroff();
                }
                else {
                    if(gColor) mattron(kCACyan); else mattron(kCABold);
                    mmvprintw(line, row * (maxx/4), "%s", tmp);
                    mattroff();
                    mprintw("%s", tmp2);
                }

                row++;
            }
            else {
                char tmp[4096];
                if(gKanjiCode == kUtf8)
                    str_cut2(item, maxx/4-1, tmp, 4096);
                else
                    cut(item, tmp, maxx/4-1);

                if(gCompletionCursor == i) {
                    mattron(kCAReverse);
                    mmvprintw(line, row * (maxx/4), "%s", tmp);
                    mattroff();
                }
                else {
                    if(gColor) mattron(kCACyan); else mattron(kCABold);
                    mmvprintw(line, row * (maxx/4), "%s", tmp);
                    mattroff();
                }

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

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

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

            char tmp2[4096];
            if(gKanjiCode == kUtf8)
                str_cut2(buf, maxx-1, tmp2, 256);
            else
                cut(buf, tmp2, maxx-1);

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

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

///////////////////////////////////////////////////////////////////////////////
// ???͏???
///////////////////////////////////////////////////////////////////////////////
void cmdline_completion_menu_input(int meta, int key)
{
    if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        if(vector_size(gCExplanation) == 0) 
            gCompletionCursor+=4;
        else
            gCompletionCursor++;
    }
    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        if(vector_size(gCExplanation) == 0)
            gCompletionCursor-=4;
        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++;
    }
    else if(key == 10 || key == 13) {		// CTRL-J CTRL-M
        char* candidate = (char*)vector_item(gCCandidate, gCompletionCursor);

        bool kanji[2048];
        if(gKanjiCode != kUtf8) {
            bool kanji_ = false;
            for(int i=0; i<strlen(candidate); i++) {
                if(kanji_) {
                    kanji_ = false;
                }
                else {
                    if(is_kanji(candidate[i])) {
                        kanji_ = true;
                    }
                }

                kanji[i] = kanji_;
            }
        }

        char tmp[2048];
        char* p = tmp;
        for(int 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 = (string_obj*) vector_item(gCmdLine, gCLCursor);
        char* cstr = string_c_str(str);

        /// ?q?X?g???[???ҏW?????Ȃ??R?s?[?𖖔??ɍ쐬 ///
        if(gCLCursor != vector_size(gCmdLine)-1) {
            cmdline_delete_last_str_if_length_0();
            
            vector_add(gCmdLine, string_new(cstr));
            gCLCursor = vector_size(gCmdLine) -1;

            str = (string_obj*)vector_item(gCmdLine, gCLCursor);
            cstr = string_c_str(str);
        }

        /// ?⊮?????????}?? ///
        string_insert(str, gCursor, tmp);
        gCursor+=strlen(tmp);

        gCompletionMenu = false;
        cmdline_completion_clear();
        gCompletionCursor = -1;
        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 || key == KEY_BACKSPACE || key == KEY_DC) { // CTRL-C CTRL-G Escape Bs
        gCompletionCursor = -1;
        gCompletionScrollTop = 0;

        gCompletionMenu = false;
        cmdline_completion_clear();
        return;
    }

    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)*4))*((maxy-2)*4);
        }
        if(gCompletionCursor > gCompletionScrollTop + (maxy-3)*4) {
            gCompletionScrollTop = (gCompletionCursor/((maxy-2)*4))*((maxy-2)*4);
        }
    }
    else {
        if(gCompletionCursor < gCompletionScrollTop) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
        if(gCompletionCursor > gCompletionScrollTop + maxy-3) {
            gCompletionScrollTop = (gCompletionCursor/(maxy-2))*(maxy-2);
        }
    }
}
