#include "common.h"

#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.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

BOOL gCmdLineActive = FALSE;
BOOL gCmdLineHitAnyKey = TRUE;
BOOL gCmdLineContinue = FALSE;

string_obj* gCmdLine;
int gCmdLineCursor = 0;   // gCmdLineのバイト単位での位置。文字列の文字数単位でも端末上のカーソル単位でもない

string_obj* gCmdLineSaved;

vector_obj* gHistory;

vector_obj* gHCandidate;

int gHistoryScrollTop = 0;
int gHistoryCursor = -1;

void cmdline_write_history()
{
    /// ヒストリーのサイズ ///
    char* size = getenv("HISTORY_SIZE");
    int histsize;
    if(size) {
        histsize = atoi(size);
    }
    else {
        histsize = 1000;
    }

    char path[PATH_MAX];

    char* histfile = getenv("HISTORY_FILE");
    if(histfile) 
        xstrncpy(path, histfile, PATH_MAX);
    else {
        snprintf(path, PATH_MAX, "%s/history", gHomeDir);
    }

    if(access(path, W_OK) != 0) {
        creat(path, 0600);
    }

    if(vector_size(gHistory) == 0) {
    }
    else if(vector_size(gHistory) < histsize) {
puts("\nwriting history....");
        FILE* f = fopen(path, "w");
        int i;
        for(i=0; i<vector_size(gHistory); i++) {
            char* line = string_c_str(vector_item(gHistory, i));
            fprintf(f, "%s\n", line);
        }
        fclose(f);
    }
    else {
puts("\nwriting history....");
        FILE* f = fopen(path, "w");
        int i;
        for(i=vector_size(gHistory)-histsize; i<vector_size(gHistory); i++)
        {
            char* line = string_c_str(vector_item(gHistory, i));
            fprintf(f, "%s\n", line);
        }
        fclose(f);
    }
}

void cmdline_read_history()
{
    /// ヒストリを初期化 ///
    int i;
    vector_clear(gHistory);

    /// ヒストリを.mhisotryから読み込む ///
    char* env = getenv("HISTORY_FILE");

    if(env == NULL) {
        fprintf(stderr, "$HISTORY_FILE is null");
        exit(1);
    }
    char path[PATH_MAX];
    xstrncpy(path, env, PATH_MAX);

    if(access(path, R_OK) == 0) {
        string_obj* line = STRING_NEW("");

        int fd = open(path, O_RDONLY);
        while(1) {
            char buf[BUFSIZ];
            int size = read(fd, buf, BUFSIZ);

            if(size <= 0) {
                break;
            }
            else if(size > 0) {
                buf[size] = 0;
                string_push_back(line, buf);
            }
        }
        close(fd);

        while(1) {
            char* p = strstr(string_c_str(line), "\n");
            if(p == NULL) {
                break;
            }
            else {
                int size2 = p - string_c_str(line);
                char* line2 = MALLOC(size2+1);
                memcpy(line2, string_c_str(line), size2);
                line2[size2] = 0;

                vector_add(gHistory, STRING_NEW(line2));
                string_erase(line, 0, size2+1);
            }
        }
    }
}

void add_history(char* str)
{
    vector_add(gHistory, STRING_NEW(str));
}

void cmdline_init()
{
    gCmdLine = STRING_NEW("");
    gCmdLineSaved = STRING_NEW("");
    gHistory = VECTOR_NEW(100);
    gHCandidate = VECTOR_NEW(1);
}

void cmdline_final()
{
}

void cmdline_start(char* str, int cursor, BOOL quick, BOOL continue_)
{
    /// 最初の起動ならコマンドラインヒストリを読み込み ///
    if(vector_size(gHistory) == 0) cmdline_read_history();

    /// 
    gCmdLineActive = TRUE;
    gCmdLineHitAnyKey = !quick;
    gCmdLineContinue = continue_;
    string_put(gCmdLine, str);
    if(string_length(gCmdLine) > kCmdLineMax) {
        string_trunc(gCmdLine, kCmdLineMax);
    }
    cmdline_convert();

    /// cursorは文字列単位での位置を指定してくるのでバイト単位に変換する
    if(cursor >= 0 && cursor < str_kanjilen(kUtf8, string_c_str(gCmdLine))) {
        gCmdLineCursor = str_kanjipos2pointer(kUtf8, string_c_str(gCmdLine), cursor) - string_c_str(gCmdLine);
    }
    else if(cursor < 0) {
        gCmdLineCursor = strlen(string_c_str(gCmdLine));
        
        cursor = -cursor -1;
        int i;
        for(i=0; i<cursor; i++) {
            char* str = string_c_str(gCmdLine);
            int utfpos = str_pointer2kanjipos(kUtf8, str, str + gCmdLineCursor);
            utfpos--;
            gCmdLineCursor = str_kanjipos2pointer(kUtf8, str, utfpos) - str;
            if(gCmdLineCursor <= 0) {
                gCmdLineCursor = 0;
                break;
            }
        }
    }
    else {
        gCmdLineCursor = 0;
    }

    int i;
    vector_clear(gHCandidate);

    const int len = vector_size(gHistory);
    for(i=len-1; i>=0; i--) {
        string_obj* cmd = (string_obj*)vector_item(gHistory, i);

        if(strstr(string_c_str(cmd), string_c_str(gCmdLine))) {
            vector_add(gHCandidate, STRING_NEW(string_c_str(cmd)));
        }
    }

    gHistoryScrollTop = 0;
    gHistoryCursor = -1;
}

///////////////////////////////////////////////////
// コマンドライン補完
///////////////////////////////////////////////////

///////////////////////////////////////////////////
// 入力
///////////////////////////////////////////////////

/// カーソルを移動 ///
static void cursor_move(int v)
{
    char* str = string_c_str(gCmdLine);
    int utfpos = str_pointer2kanjipos(kUtf8, str, str + gCmdLineCursor);
    utfpos+=v;
    gCmdLineCursor = str_kanjipos2pointer(kUtf8, str, utfpos) - str;
}

void cmdline_convert()
{
    if(gKanjiCodeFileName != gKanjiCode) {
        char* src = string_c_str(gCmdLine);
        int des_size = strlen(src)*2 + 1;
        char* des = MALLOC(des_size + 1);
#if defined(HAVE_ICONV_H) || defined(HAVE_BICONV_H) 
        if(is_all_ascii(src)) {
            xstrncpy(des, src, des_size);
        }
        else {
            /// コンバート ///
            if(kanji_convert(src, des, des_size, gKanjiCodeFileName, gKanjiCode) < 0) {
                xstrncpy(des, src, des_size);
            }
        }
#else
        xstrncpy(des, src, des_size);
#endif
        string_put(gCmdLine, des);
        if(string_length(gCmdLine) > kCmdLineMax) {
            string_trunc(gCmdLine, kCmdLineMax);
        }
    }
}

void cmdline_input(int meta, int key)
{
    char* p = string_c_str(gCmdLine);
    int len = strlen(p);
    int i;
    char* env = getenv("CMDLINE_ESCAPE_KEY_ENABLE");

    BOOL cmdline_escape_key = env && strcmp(env, "1") == 0;

    if(key == 14 || key == KEY_DOWN) {    // CTRL-N
        if(gHistoryCursor == 0) {
            gCmdLine = STRING_NEW(string_c_str(gCmdLineSaved));
            gCmdLineCursor = strlen(string_c_str(gCmdLine));
            gHistoryCursor = -1;
        }
        else if(gHistoryCursor != -1) {
            gHistoryCursor--;
        }
    }

    else if(key == 16 || key == KEY_UP) {    //CTRL-P
        if(gHistoryCursor != -1) {
            gHistoryCursor++;
        }
        else if(vector_size(gHCandidate) > 0) {
            gHistoryCursor = 0;
            gHistoryScrollTop = 0;

            gCmdLineSaved = STRING_NEW(string_c_str(gCmdLine));
        }
    }
    // CTRL-V
    else if(key==22 || key == KEY_NPAGE)
    {
        if(gHistoryCursor != -1) {
            gHistoryCursor+=5;
        }
        else {
            gHistoryCursor = 0;
            gHistoryScrollTop = 0;
        }
    }
    // CTRL-U Meta-v
    else if(key == 21 || meta==1&&key=='v' || key == KEY_PPAGE) 
    {
        if(gHistoryCursor != -1) {
            gHistoryCursor-=5;
        }
        else {
            gHistoryCursor = 0;
            gHistoryScrollTop = 0;
        }
    }

    /// カーソルを前に移動 ///
    else if(key == 6 || key == KEY_RIGHT) {    // CTRL-F
        cursor_move(1);

        gHistoryCursor = -1;
    }

    /// カーソルを後ろに移動 ///
    else if(key == 2 || key == KEY_LEFT) {    // CTRL-B
        cursor_move(-1);
        
        gHistoryCursor = -1;
    }

    /// 後方に文字列分移動 ///
    else if(meta==1 && key == 'b' || meta==1 && key == KEY_LEFT // Meta-b CTRL-LEFT Meta-Left
        || key==KEY_F(13) || key==KEY_F(17))
    {
        if(gCmdLineCursor > 0) {
            const char* s = string_c_str(gCmdLine);
            int pos = gCmdLineCursor;
            pos--;
            while(pos>=0 && (s[pos] == ' ' || s[pos] == '/' || s[pos] == '\'' || s[pos] == '"' || s[pos] == '.')) {
                pos--;
            }
            while(pos>=0 && s[pos] != ' ' && s[pos] != '/' && s[pos] != '\'' && s[pos] != '"' && s[pos] != '.') {
                pos--;
            gCmdLineCursor = pos+1;
            }
        }

        gHistoryCursor = -1;
    }

    /// 前方に文字列分移動 ///
    else if(meta==1 && key == 'f' || meta==1 && key == KEY_RIGHT // Meta-f CTRL-RIGHT Meta-Right
        || key==KEY_F(14) || key==KEY_F(18))
    {
        const char* s = string_c_str(gCmdLine);

        if(s[gCmdLineCursor] != 0) {
            int pos = gCmdLineCursor;
            pos++;
            while(s[pos]!=0 && (s[pos] == ' ' || s[pos] == '/' || s[pos] == '\'' || s[pos] == '"' || s[pos] == '.')) {
                pos++;
            }
            while(s[pos]!=0 && s[pos] != ' ' && s[pos] != '/' && s[pos] != '\'' && s[pos] != '"' && s[pos] != '.') {
                pos++;
            }

            gCmdLineCursor = pos;
        }

        gHistoryCursor = -1;
    }

    /// カーソルを一番前に移動 ///
    else if(key == 1 || key == KEY_HOME) {    // CTRL-A
        cursor_move(-999);
        gHistoryCursor = -1;
    }

    /// カーソルを末尾に移動 ///
    else if(key == 5 || key == KEY_END) {    // CTRL-E
        cursor_move(999);
        gHistoryCursor = -1;
    }

    /// 後方文字を削除 ///
    else if(key == 4 || key == KEY_DC) {    // CTRL-D DELETE
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 編集テキストのサイズが0ならコマンドライン終了 ///
        if(strlen(cstr) == 0) {
            gCmdLineActive = FALSE;
        }
        /// 編集テキストのサイズが0以上なら文字を削除 ///
        else {
            if(gCmdLineCursor < strlen(cstr)) {
                /// 文字を削除 ///
                int utfpos = str_pointer2kanjipos(kUtf8, cstr, cstr + gCmdLineCursor);
                char* next_point = str_kanjipos2pointer(kUtf8, cstr, utfpos+1);

                string_erase(str, gCmdLineCursor, next_point - (cstr + gCmdLineCursor));
            }
        }
        gHistoryCursor = -1;
    }

    /// 後方文字を削除 ///
    else if(key == 8 || key == KEY_BACKSPACE) {    // CTRL-H
        if(gCmdLineCursor > 0) {
            string_obj* str = gCmdLine;
            char* cstr = string_c_str(str);

            /// 文字を削除 ///
            int utfpos = str_pointer2kanjipos(kUtf8, cstr, cstr + gCmdLineCursor);
            char* before_point = str_kanjipos2pointer(kUtf8, cstr, utfpos-1);
            int new_cursor = before_point-cstr;

            string_erase(str, before_point - cstr, (cstr + gCmdLineCursor) - before_point);
            gCmdLineCursor = new_cursor;
        }
        gHistoryCursor = -1;
    }

    /// 行を削除 ///
    else if(key == 21) {    // CTRL-U
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 行を削除 ///
        string_put(str, "");

        gCmdLineCursor = 0;
        gHistoryCursor = -1;
    }

    /// 前方文字列を削除 ///
    else if(key == 23) {     // CTRL-W
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 文字列を削除 ///
        if(gCmdLineCursor > 0) {
            int pos = gCmdLineCursor-1;
            if(cstr[pos]==' ' || cstr[pos]=='/' || cstr[pos]=='\'' || cstr[pos]=='"' || cstr[pos]=='.') {
                do {
                    pos--;
                }
                while(pos>=0 && (cstr[pos]==' ' || cstr[pos]=='/' || cstr[pos]=='\'' || cstr[pos]=='"' || cstr[pos]=='.') && cstr[pos+1] == cstr[pos]);
            }
            while(pos>=0 && cstr[pos]!=' ' && cstr[pos]!='/' && cstr[pos]!='\'' && cstr[pos]!='"' && cstr[pos]!='.')
            {
                pos--;
            }

            string_erase(str, pos+1, gCmdLineCursor-pos-1);

            gCmdLineCursor = pos+1;
        }
        gHistoryCursor = -1;
    }

    /// 後方文字列を削除 ///
    else if(meta==1 && key == 'd' || meta==0 && key==KEY_F(21)) {     // Meta-d CTRL + DELETE
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 後方文字列を削除 ///
        if(cstr[gCmdLineCursor] != 0) {
            int pos = gCmdLineCursor;
            pos++;
            while(cstr[pos]!=0 && (cstr[pos] == ' ' || cstr[pos] == '/' || cstr[pos] == '\'' || cstr[pos] == '"' || cstr[pos] == '.')) {
                pos++;
            }
            while(cstr[pos]!=0 && cstr[pos] != ' ' && cstr[pos] != '/' && cstr[pos] != '\'' && cstr[pos] != '"' && cstr[pos] != '.') {
                pos++;
            }

            string_erase(str, gCmdLineCursor, pos-gCmdLineCursor);
            //gCmdLineCursor = pos;
        }
        gHistoryCursor = -1;
    }

    /// カーソルから末尾まで削除 ///
    else if(key == 11) {    // CTRL-K
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 末尾まで削除 ///
        string_erase(str, gCmdLineCursor, string_length(str)-gCmdLineCursor);
        gHistoryCursor = -1;
    }
    else if(key == 9) {        // CTRL-I
        if(vector_size(gCCandidate) > 0) {
            gHistoryCursor = 0;
        }
        else {
            cmdline_completion_main();
            gHistoryCursor = -1;
        }
    }
    else if(key == 12) {// CTRL-L
        filer_reread(0);
        filer_reread(1);

        mclear_immediately();
        gHistoryCursor = -1;
    }
    // CTRL-C 
    else if(!cmdline_escape_key && (key == 3)
            || cmdline_escape_key && (key == 3))
    {
        char* p = string_c_str(gCmdLine);
        BOOL all_space = TRUE;
        while(*p) {
            if(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            else {
                p++;
                all_space = FALSE;
                break;
            }
        }
        gCmdLineActive = FALSE;
    }
    // CTRL-G Escape Key
    else if(!cmdline_escape_key && (key == 7)
            || cmdline_escape_key && (key ==7 || key==27))
    {
        char* p = string_c_str(gCmdLine);
        BOOL all_space = TRUE;
        while(*p) {
            if(*p == ' ' || *p == '\t' || *p == '\n') {
                p++;
            }
            else {
                p++;
                all_space = FALSE;
                break;
            }
        }
        gCmdLineActive = FALSE;
        if(!all_space) {
            add_history(string_c_str(gCmdLine));
        }
    }
    else if(key == 10 || key == 13) {       // CTRL-J CTRL-M
        /// 初期化 ///
        if(gCmdLineHitAnyKey) {
            mclear();
            mmove(0,0);
            mrefresh();
            mendwin();
            mreset_tty();
        }

        /// コマンド実行 ///
        saphire_set_signal();
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        int rcode = saphire_shell(string_c_str(gCmdLine), "cmdline", pipeout, pipein, STDERR_FILENO);
        saphire_sweep();
        sWFd_flash(pipeout);
        add_history(string_c_str(gCmdLine));
        set_signal_mfiler();

        if(!gCmdLineHitAnyKey) {
            if(rcode < 0) { merr_msg(string_c_str(gErrMsg)); }
        }
        else {
            if(rcode < 0) { printf("%s", string_c_str(gErrMsg)); }

            /// ディレクトリ再読み込み ///
            if(strcmp(getenv("VIEW_OPTION"), "2pain") == 0) {
                filer_reread(0);
                filer_reread(1);
            }
            else {
                filer_reread(adir());
            }

            /// HIT ANY KEY ///
            putp(tigetstr("rev"));
            printf("HIT ANY KEY");
            putp(tigetstr("sgr0"));
            fflush(stdout);

            minitscr();
            int meta;
            mgetch(&meta);

            //mclear_immediately();
        }

        if(!gCmdLineContinue) {
            gCmdLineActive = FALSE;
        }
        else {
            cmdline_start("", 0, FALSE, TRUE);
        }
    }
    else {
        string_obj* str = gCmdLine;
        char* cstr = string_c_str(str);

        /// 文字列を追加 ///
        if(meta == 0 && !(key >= 0 && key <= 27)) {
            char tmp[128];

            snprintf(tmp, 128, "%c", key);
            string_insert(str, gCmdLineCursor, tmp);
            gCmdLineCursor++;
        }

        gHistoryCursor = -1;
    }
//    cmdline_convert();

    if(gHistoryCursor == -1) {
        vector_clear(gHCandidate);

        len = vector_size(gHistory);
        for(i=len-1; i>=0; i--) {
            string_obj* cmd = (string_obj*)vector_item(gHistory, i);

            if(strstr(string_c_str(cmd), string_c_str(gCmdLine))) {
                vector_add(gHCandidate, STRING_NEW(string_c_str(cmd)));
            }
        }
    }
    else {
        if(gHistoryCursor < 0) {
            gHistoryCursor = 0;
        }
        if(gHistoryCursor >= vector_size(gHCandidate)) {
            gHistoryCursor = vector_size(gHCandidate)-1;
        }

        const int maxy = mgetmaxy();
        if(gHistoryCursor < gHistoryScrollTop) {
            gHistoryScrollTop = (gHistoryCursor/(maxy-2))*(maxy-2);
        }
        if(gHistoryCursor > gHistoryScrollTop + maxy-3) {
            gHistoryScrollTop = (gHistoryCursor/(maxy-2))*(maxy-2);
        }

        string_obj* candidate = (string_obj*)vector_item(
                                             gHCandidate, gHistoryCursor);
        if(candidate) {
            gCmdLine = STRING_NEW(string_c_str(candidate));
            gCmdLineCursor = strlen(string_c_str(gCmdLine));
        }
    }

    if(string_length(gCmdLine) > kCmdLineMax) {
        string_trunc(gCmdLine, kCmdLineMax);
    }
}

void cmdline_hcandidate_refresh()
{
    int i;
    vector_clear(gHCandidate);

    int len = vector_size(gHistory);
    for(i=len-1; i>=0; i--) {
        string_obj* cmd = (string_obj*)vector_item(gHistory, i);

        if(strstr(string_c_str(cmd), string_c_str(gCmdLine))) {
            vector_add(gHCandidate, STRING_NEW(string_c_str(cmd)));
        }
    }
}

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

    /// プロンプトを描写 ///
    mclear();

    mmvprintw(0, 0, "$ ");
    
    /// 各行を描写 ///
    char* cstr = string_c_str(gCmdLine);

    int line = 0;
    char* line_top = cstr;
    int n = 0;
    int n2 = 0;

    int cursor_y = -1;
    int cursor_x = -1;
    while(1) {
        char* line_end = str_kanjipos2pointer(kUtf8, line_top, n);

        int size = line_end-line_top;
        char* buf = MALLOC(size+1);
        memcpy(buf, line_top, size);
        buf[size] = 0;

        /// カーソル描写位置を保存 ///
        if(line_end == cstr+gCmdLineCursor) {
            cursor_y = line;
            cursor_x = str_termlen(kUtf8, buf);
        }
        
        /// 行の限界まできたら ///
        else if(line >= maxy) {
            break;
        }

        /// 行の終わりまで達していたら ///
        if(line == 0 && (str_termlen(kUtf8, buf)+2 > maxx)
                || line > 0 && (str_termlen(kUtf8, buf) > maxx))
        {
            line_end = str_kanjipos2pointer(kUtf8, line_top, n-1);

            int size = line_end-line_top;
            char* buf2 = MALLOC(size+1);
            memcpy(buf2, line_top, line_end-line_top);
            buf2[line_end-line_top] = 0;

            if(line == 0) {
                mprintw("%s", buf2);
            }
            else {
                mmvprintw(line, 0, "%s", buf2);
            }

            line_top = line_end;
            n = 0;
            line++;
        }

        /// 文字列の終端まで達していたら ///
        else if(!*line_end) {
            if(cursor_x == maxx) {
                cursor_x = 0;
                cursor_y++;
            }

            mmvprintw(line, 0, "%s", line_top);

            break;
        }

        /// それ以外 ///
        else {
            n++;
        }
    }

    /// カーソル移動 ///
    if(cursor_y == -1 && cursor_x == -1) {
        mmove(maxy-1, maxx-2);
    }
    else if(cursor_y == 0) {
        mmove(cursor_y, cursor_x+2);
    }
    else {
        mmove(cursor_y, cursor_x);
    }
}

static void cmdline_view_utf8_2line()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    mmvprintw(maxy - 2, 0, "$ ");
    
    char* cstr = string_c_str(gCmdLine);

    BOOL kanji_zure = FALSE;
    int i;
    for(i=0; i<str_kanjilen(kUtf8, cstr); i++) {
        int termpos = str_termlen2(kUtf8, cstr, i);

        if(termpos+2 == maxx-1) {
            char* pointer = str_kanjipos2pointer(kUtf8, cstr, i);
            if(is_kanji(kUtf8, *pointer)) {
                kanji_zure = TRUE;
                break;
            }
        }
    }

    if(kanji_zure) {
        int i;
        for(i=0; i<str_kanjilen(kUtf8, cstr); i++) {
            int termpos = str_termlen2(kUtf8, cstr, i);

            if(termpos+2 == maxx-1) {
                char* pointer = str_kanjipos2pointer(kUtf8, cstr, i);

                if(is_kanji(kUtf8, *pointer)) {
                    int size = pointer-cstr;
                    char* line1 = MALLOC(size+1);

                    memcpy(line1, cstr, size);
                    line1[size+1] = 0;

                    mmvprintw(maxy-2, 2, "%s", line1);
                    mmvprintw(maxy-1, 0, "%s", pointer);
                    break;
                }
            }
        }
    }
    else {
        BOOL cmd = TRUE;
        mmvprintw(maxy-2, 2, "%s", cstr);
    }

    int utfpos = str_pointer2kanjipos(kUtf8, cstr, cstr + gCmdLineCursor);
    int termpos = str_termlen2(kUtf8, cstr, utfpos);

    if(kanji_zure) {
        if(termpos+2 < maxx-1)
            mmove(maxy -2, 2 + termpos);
        else
            mmove(maxy -1, 2 + termpos - maxx+1);
    }
    else {
        if(termpos+2 < maxx) {
            mmove(maxy -2, 2 + termpos);
        }
        else {
            mmove(maxy -1, 2 + termpos - maxx);
        }
    }
}

static void cmdline_view_utf8()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    char* cstr = string_c_str(gCmdLine);

    /// 漢字のずれがあるかどうか？ ///
    BOOL kanji_zure = FALSE;
    int i;
    for(i=0; i<str_kanjilen(kUtf8, cstr); i++) {
        int termpos = str_termlen2(kUtf8, cstr, i);

        if(termpos+2 == maxx-1) {
            char* pointer = str_kanjipos2pointer(kUtf8, cstr, i);
            if(is_kanji(kUtf8, *pointer)) {
                kanji_zure = TRUE;
                break;
            }
        }
    }

    /// 端末上で２行以上の場合 ///
    const int len = str_termlen(kUtf8, cstr);
    if(kanji_zure && (len+3 >= maxx*2)
            || !kanji_zure && (len+2 >= maxx*2)) 
    {
        cmdline_view_utf8_multi_line();
    }
    /// 端末上で２行以下の場合 ///
    else {
        cmdline_view_utf8_2line();
    }
}

static void cmdline_view_filer()
{
    const int maxx = mgetmaxx();        // 画面サイズ
    const int maxy = mgetmaxy();
    
    char* env = getenv("PROMPT");
    if(env) {
        string_obj* str = STRING_NEW("");
        sRFd* rfd = sRFd_new(0);
        saphire_shell3(str, env, "prompt", rfd);
        saphire_sweep();

        mmvprintw(maxy -2, 0, "%s", string_c_str(str));
    }

    /// カーソル下のファイルの情報を描写 ///          
    char buf[1024];
    sFile* file = filer_cursor_file(adir());

    char permission[12];
    xstrncpy(permission, "----------", 12);

    if(S_ISDIR(file->mLStat.st_mode)) permission[0] = 'd';  // ファイルの種別
    if(S_ISCHR(file->mLStat.st_mode)) permission[0] = 'c';
    if(S_ISBLK(file->mLStat.st_mode)) permission[0] = 'b';
    if(S_ISFIFO(file->mLStat.st_mode)) permission[0] = 'p';
    if(S_ISLNK(file->mLStat.st_mode)) permission[0] = 'l';
    if(S_ISSOCK(file->mLStat.st_mode)) permission[0] = 's';

    mode_t smode = file->mLStat.st_mode;        // パーミッション
    if(smode & S_IRUSR) permission[1] = 'r';
    if(smode & S_IWUSR) permission[2] = 'w';
    if(smode & S_IXUSR) permission[3] = 'x';
    if(smode & S_IRGRP) permission[4] = 'r';
    if(smode & S_IWGRP) permission[5] = 'w';
    if(smode & S_IXGRP) permission[6] = 'x';
    if(smode & S_IROTH) permission[7] = 'r';
    if(smode & S_IWOTH) permission[8] = 'w';
    if(smode & S_IXOTH) permission[9] = 'x';
    if(smode & S_ISUID) permission[3] = 's';
    if(smode & S_ISGID) permission[6] = 's';
#if defined(S_ISTXT)
    if(smode & S_ISTXT) permission[9] = 't';
#endif
#if defined(S_ISVTX)
    if(smode & S_ISVTX) permission[9] = 't';
#endif
    
    permission[10] = 0;

    time_t t = file->mLStat.st_mtime;                   // 時間
    struct tm* tm_ = (struct tm*)localtime(&t);
    
    char buf2[1024];

    char owner[256];
    char* tmp = string_c_str(file->mUser);
    if(tmp) {
        str_cut2(kUtf8, tmp, 8, owner, 256);
    }
    else {
        snprintf(owner, 256, "%d", file->mLStat.st_uid);
    }

    char group[256];
    tmp = string_c_str(file->mGroup);
    if(tmp) {
        str_cut2(kUtf8, tmp, 7, group, 256);
    }
    else {
        snprintf(group, 256, "%d", file->mLStat.st_gid);
    }

    char size_buf[256];
    int width = filer_make_size_str(size_buf, file->mLStat.st_size);

    if(S_ISDIR(file->mStat.st_mode)) {
        xstrncpy(size_buf, "<DIR>", 256);
    }

    char format[128];
    xstrncpy(format, "%", 128);
    snprintf(format + 1, 126, "%d", width);
    xstrncat(format, "s", 128);

    char size_buf2[256];
    snprintf(size_buf2, 256, format, size_buf);
    if(!S_ISLNK(file->mLStat.st_mode)) {
        int year = tm_->tm_year-100;
        if(year < 0) year+=100;
        while(year > 100) year-=100;
        
        snprintf(buf2, 1024, 
            "%s %3d %-8s %-7s%s %02d-%02d-%02d %02d:%02d %s"
            //, "%s %3d %-8s %-7s%10lld %02d-%02d-%02d %02d:%02d %s"
            , permission, file->mLStat.st_nlink
            , owner, group
                                
            , size_buf2
            
            , year, tm_->tm_mon+1, tm_->tm_mday
            , tm_->tm_hour, tm_->tm_min
            , string_c_str(file->mNameView));
            
        char buf3[1024];
        str_cut2(kUtf8, buf2, maxx-1, buf3, 1024);
                            
        mmvprintw(maxy-1, 0, "%s", buf3);
    }
    else {
        snprintf(buf, 1024,
           "%s %3d %s%s%s %02d-%02d-%02d %02d:%02d %s -> %s"
           //, "%s %3d %s%s%10lld %02d-%02d-%02d %02d:%02d %s -> %s"
           , permission, file->mLStat.st_nlink
           , owner, group
                                
           , size_buf2

           , tm_->tm_year-100, tm_->tm_mon+1, tm_->tm_mday
           , tm_->tm_hour, tm_->tm_min
           , string_c_str(file->mNameView)
           , string_c_str(file->mLinkTo));
           
        char buf2[1024];
        str_cut2(kUtf8, buf, maxx-1, buf2, 1024);
                   
        mmvprintw(maxy-1, 0, "%s", buf2);
    }

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

void cmdline_view()
{
    if(!gCmdLineActive) {
        cmdline_view_filer();
    }
    else {
        cmdline_view_utf8();
    }
}

void cmdline_history_view()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    int line = maxy-3;
    int i;
    for(i= gHistoryScrollTop; i<vector_size(gHCandidate); i++) {
        string_obj* item = (string_obj*)vector_item(gHCandidate, i);

        char* tmp = (char*)MALLOC(MB_CUR_MAX*maxx+1);
        str_cut2(kUtf8, string_c_str(item), maxx-1, tmp, MB_CUR_MAX*maxx+1);

        if(gHistoryCursor == i) mattron(kCAReverse);        
        mmvprintw(line, 0, "%s", tmp);
        if(gHistoryCursor == i) mattroff();
        line--;
        
        if(line < 0) {
            break;
        }
    }
}
