#include "common.h"

#if defined(HAVE_MIGEMO_H)
migemo* gMigemo;
static regex_t* gReg;
#endif

static string_obj* gInputFileName;      // 入力された文字列

///////////////////////////////////////////////////////////////////////////////
// 外部にさらす定義
///////////////////////////////////////////////////////////////////////////////
bool gISearch = false;                  // 現在インクリメンタルサーチ中かどうか
bool gISearchPartMatch = false;         // 現在インクリメンタルサーチパートマッチ中かどうか
bool gISearchExplore = false;           // エキスプローラー風インクリメンタルサーチ
bool gNoPartMatchClear = false;         // ISearchClarでgISearchPartMatchをクリアするかどうか
bool gISearchOption1 = true;            // 先頭インクリメンタルサーチが失敗したら部分一致インクリメンタルサーチするオプション

// その文字がエクスプローラー風インクリメンタルサーチで使うものかどうか
bool IsISearchExploreChar(int meta, int key)
{
    return meta == 0 && (
                   (key >= '0' && key <= '9') 
                || (key >= 'A' && key <= 'Z') 
                || (key >= 'a' && key <='z')
                || key == '_'
                || key == '-'
                || key == '.'
                );
}

bool IsISearchNULL()
{
    return string_length(gInputFileName) == 0;
}

void ISearchClear()
{
    string_put(gInputFileName, "");
    if(!gNoPartMatchClear) {
        gISearchPartMatch = false;
    }
    else {
        gNoPartMatchClear = false;
    }
}

///////////////////////////////////////////////////////////////////////////////
// 内部定義
///////////////////////////////////////////////////////////////////////////////

/// 入力された文字列がインクリメンタルサーチ向きかどうか ///
static inline bool is_isearch_char(int key)
{
    return key == 9 || (key >= ' ' && key <= '~')
                && key != '\\';
//                && key != '/'
//                && key != '~'
//                && key != ' '
//                && key != '?';
}

/// 後ろにマッチするファイル名にカーソルを移動 ///
static bool match_back(int start)
{
#if !defined(HAVE_MIGEMO_H)
    for(int i=start; i>=0; i--) {
        sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

        char* result = mystrcasestr(file->mNameView, string_c_str(gInputFileName));
        if(!gISearchPartMatch &&  result == file->mNameView
            || gISearchPartMatch && result != NULL)
        {
            ActiveDir()->MoveCursor(i);
            return true;
        }
    }

    return false;

#else

    /// get reg ///
    if(gReg == NULL) {
        const OnigUChar* p = migemo_query(gMigemo, (unsigned char*)string_c_str(gInputFileName));
        if(p == NULL) {
            return false;
        }

        int r;
        OnigErrorInfo err_info;

        if(gKanjiCode == kUtf8) {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT,  &err_info);
        }
        else if(gKanjiCode == kEucjp) {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_EUC_JP, ONIG_SYNTAX_DEFAULT, &err_info);
        }
        else if(gKanjiCode == kSjis) {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_SJIS, ONIG_SYNTAX_DEFAULT, &err_info);
        }

        migemo_release(gMigemo, (unsigned char*) p);

        if(r != ONIG_NORMAL) {
            return false;
        }
    }

    /// start ///
    for(int i=start; i>=0; i--) {
        sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

        bool all_english = true;
        const int len = strlen(file->mNameView);
        char* str = file->mNameView;
        for(int j=0; j<len; j++) {
            if(!(str[j]>=' ' && str[j]<='~')) {
                all_english = false;
            }
        }

        if(all_english) {
            char* result = mystrcasestr(file->mNameView, string_c_str(gInputFileName));
            if(!gISearchPartMatch && result == file->mNameView
                || gISearchPartMatch && result != NULL)
            {
                ActiveDir()->MoveCursor(i);
                //if(gReg) onig_free(gReg);
                //gReg = NULL;
                
                return true;
            }
            
        }
        else {
            OnigRegion* region = onig_region_new();
            
            OnigUChar* str2 = (OnigUChar*)file->mNameView;

            int r2 = onig_search(gReg, str2, str2 + strlen((char*)str2), str2, str2 + strlen((char*)str2), region, ONIG_OPTION_NONE);

            onig_region_free(region, 1);

            if(!gISearchPartMatch && r2 == 0
                || gISearchPartMatch && r2 >= 0)
            {
                ActiveDir()->MoveCursor(i);
                //if(gReg) onig_free(gReg);
                //gReg = NULL;
                return true;
            }
        }
    }

    //if(gReg) onig_free(gReg);
    //gReg = NULL;

    return false;
#endif
}

/// 前にマッチするファイル名にカーソルを移動 ///
static bool match_next(int start)
{
#if !defined(HAVE_MIGEMO_H)
    for(int i=start; i<vector_size(ActiveDir()->Files()); i++) {
        sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

        char* result = mystrcasestr(file->mNameView, string_c_str(gInputFileName));
        if(!gISearchPartMatch &&  result == file->mNameView
            || gISearchPartMatch && result != NULL)
        {
            ActiveDir()->MoveCursor(i);
            return true;
        }
    }

    return false;

#else

    /// get reg ///
    if(gReg == NULL) {
        const OnigUChar* p = migemo_query(gMigemo, (unsigned char*)string_c_str(gInputFileName));
        if(p == NULL) {
            return false;
        }

        int r;
        OnigErrorInfo err_info;

        if(gKanjiCode == kUtf8) {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_UTF8, ONIG_SYNTAX_DEFAULT,  &err_info);
        }
        else if(gKanjiCode == kEucjp) {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_EUC_JP, ONIG_SYNTAX_DEFAULT, &err_info);
        }
        else {
            r = onig_new(&gReg, p, p + strlen((char*)p), ONIG_OPTION_DEFAULT, ONIG_ENCODING_SJIS, ONIG_SYNTAX_DEFAULT, &err_info);
        }
        migemo_release(gMigemo, (unsigned char*) p);

        if(r != ONIG_NORMAL) {
            return false;
        }
    }

    /// start ///
    for(int i=start; i<vector_size(ActiveDir()->Files()); i++) {
        sFile* file = (sFile*)vector_item(ActiveDir()->Files(), i);

        bool all_english = true;
        char* p = file->mNameView;
        while(*p) {
            if(!(*p>=' ' && *p<='~')) {
                all_english = false;
                break;
            }
            p++;
        }

        if(all_english) {
            char* result = mystrcasestr(file->mNameView, string_c_str(gInputFileName));
            if(!gISearchPartMatch && result == file->mNameView
                || gISearchPartMatch && result != NULL)
            {
                ActiveDir()->MoveCursor(i);
                //if(gReg) onig_free(gReg);
                //gReg = NULL;
                
                return true;
            }
            
        }
        else {
            OnigRegion* region = onig_region_new();
            
            OnigUChar* str2 = (OnigUChar*)file->mNameView;

            int r2 = onig_search(gReg, str2, str2 + strlen((char*)str2), str2, str2 + strlen((char*)str2), region, ONIG_OPTION_NONE);

            onig_region_free(region, 1);

            if(!gISearchPartMatch && r2 == 0
                || gISearchPartMatch && r2 >= 0)
            {
                ActiveDir()->MoveCursor(i);
                //if(gReg) onig_free(gReg);
                //gReg = NULL;
                return true;
            }
        }
    }

    //if(gReg) onig_free(gReg);
    //gReg = NULL;

    return false;
#endif
}

///////////////////////////////////////////////////////////////////////////////
// インクリメンタルサーチ初期化
///////////////////////////////////////////////////////////////////////////////
void migemo_init()
{
#if defined(HAVE_MIGEMO_H)
    char buf[PATH_MAX];
    gMigemo = migemo_open(NULL);

#if defined(MYCYGWINCOMPILE)
    if(gKanjiCode == kUtf8) {
        sprintf(buf, "%s/share/mfiler/utf-8/migemo-dict", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/utf-8/roma2hira.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/utf-8/hira2kata.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/utf-8/han2zen.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else if(gKanjiCode == kEucjp) {
        sprintf(buf, "%s/share/mfiler/euc-jp/migemo-dict", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/euc-jp/roma2hira.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/euc-jp/hira2kata.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/euc-jp/han2zen.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else {
        sprintf(buf, "%s/share/mfiler/cp932/migemo-dict", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/cp932/roma2hira.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/cp932/hira2kata.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/share/mfiler/cp932/han2zen.dat", gProgramHome);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    
#else
    if(gKanjiCode == kUtf8) {
        sprintf(buf, "%s/mfiler/utf-8/migemo-dict", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/utf-8/roma2hira.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/utf-8/hira2kata.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/utf-8/han2zen.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else if(gKanjiCode == kEucjp) {
        sprintf(buf, "%s/mfiler/euc-jp/migemo-dict", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/euc-jp/roma2hira.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/euc-jp/hira2kata.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/euc-jp/han2zen.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
    else {
        sprintf(buf, "%s/mfiler/cp932/migemo-dict", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_MIGEMO, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/cp932/roma2hira.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_ROMA2HIRA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/cp932/hira2kata.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HIRA2KATA, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
        sprintf(buf, "%s/mfiler/cp932/han2zen.dat", DATADIR);
        if(migemo_load(gMigemo, MIGEMO_DICTID_HAN2ZEN, buf) == MIGEMO_DICTID_INVALID) {
            fprintf(stderr, "%s is not found\n", buf);
            exit(1);
        }
    }
#endif
    
#endif
}

void isearch_init_after_reading_rc_file()
{
    gInputFileName = string_new("");
#if defined(HAVE_MIGEMO_H)
    gReg = NULL;
#endif

    migemo_init();
}

void isearch_reload_migemo_dict()
{
#if defined(HAVE_MIGEMO_H)
    onig_end();
    migemo_close(gMigemo);
#endif
    
#if defined(HAVE_MIGEMO_H)
    if(gReg) onig_free(gReg);
    gReg = NULL;
#endif

    migemo_init();
}

///////////////////////////////////////////////////////////////////////////////
// インクリメンタルサーチ解放
///////////////////////////////////////////////////////////////////////////////
void isearch_final()
{
#if defined(HAVE_MIGEMO_H)
    onig_end();
    migemo_close(gMigemo);
#endif
    
#if defined(HAVE_MIGEMO_H)
    if(gReg) onig_free(gReg);
    gReg = NULL;
#endif

    string_delete(gInputFileName);
}

///////////////////////////////////////////////////////////////////////////////
// インクリメンタルサーチキー入力
///////////////////////////////////////////////////////////////////////////////
void isearch_input(int meta, int key)
{
    if(key == 8 || key == KEY_BACKSPACE || key == KEY_DC) {
        string_erase(gInputFileName, string_length(gInputFileName)-1, 1);

#if defined(HAVE_MIGEMO_H)
        if(gReg) onig_free(gReg);
        gReg = NULL;
#endif
    }
    else if(key == 14 || key == KEY_DOWN) {
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_next(ActiveDir()->Cursor()+1);
            gISearchPartMatch = false;
        }
        else {
            match_next(ActiveDir()->Cursor()+1);
        }
    }
    else if(key == 16 || key == KEY_UP) {
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_back(ActiveDir()->Cursor()-1);
            gISearchPartMatch = false;
        }
        else {
            match_back(ActiveDir()->Cursor()-1);
        }
    }
    else if(key == ' ') {
        ActiveDir()->Mark2(ActiveDir()->Cursor());
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_next(ActiveDir()->Cursor()+1);
            gISearchPartMatch = false;
        }
        else {
            match_next(ActiveDir()->Cursor()+1);
        }
    }
    else if(!is_isearch_char(key)) {
        gISearch = false;
        gISearchPartMatch = false;
        string_put(gInputFileName, "");

#if defined(HAVE_MIGEMO_H)
        if(gReg) onig_free(gReg);
        gReg = NULL;
#endif
    }
    else {
        if(key == 9)
            string_push_back2(gInputFileName, ' ');
        else
            string_push_back2(gInputFileName, (char)key);

#if defined(HAVE_MIGEMO_H)
                if(gReg) onig_free(gReg);
                gReg = NULL;
#endif

        if(gISearchOption1 && !gISearchPartMatch) {
            if(!match_next(0)) {
                gISearchPartMatch = true;

                if(!match_next(0)) {
                    string_erase(gInputFileName, string_length(gInputFileName)-1, 1);

#if defined(HAVE_MIGEMO_H)
                    if(gReg) onig_free(gReg);
                    gReg = NULL;
#endif
                }

                gISearchPartMatch = false;
            }
        }
        else {
            if(!match_next(0)) {
                string_erase(gInputFileName, string_length(gInputFileName)-1, 1);

    #if defined(HAVE_MIGEMO_H)
                    if(gReg) onig_free(gReg);
                    gReg = NULL;
    #endif
            }
        }
    }
}

void isearch_explore_input(int meta, int key)
{
    if(meta == 1 && key == KEY_UP || key == KEY_F(15) || meta == 1 && key == 16) {
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_back(ActiveDir()->Cursor()-1);
            gISearchPartMatch = false;
        }
        else {
            match_back(ActiveDir()->Cursor()-1);
        }
    }
    else if(meta == 1 && key == KEY_DOWN || key == KEY_F(16) || meta == 1 && key == 14) {
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_next(ActiveDir()->Cursor()+1);
            gISearchPartMatch = false;
        }
        else {
            match_next(ActiveDir()->Cursor()+1);
        }
    }
    else if(key == ' ') {
        ActiveDir()->Mark2(ActiveDir()->Cursor());
        if(gISearchOption1 && !gISearchPartMatch) {
            gISearchPartMatch = true;
            match_next(ActiveDir()->Cursor()+1);
            gISearchPartMatch = false;
        }
        else {
            match_next(ActiveDir()->Cursor()+1);
        }
    }
    else {
        string_push_back2(gInputFileName, (char)key);

    #if defined(HAVE_MIGEMO_H)
        if(gReg) onig_free(gReg);
        gReg = NULL;
    #endif

        if(gISearchOption1 && !gISearchPartMatch) {
            if(!match_next(0)) {
                gISearchPartMatch = true;

                if(!match_next(0)) {
                    string_put(gInputFileName, "");
                    string_push_back2(gInputFileName, (char)key);

#if defined(HAVE_MIGEMO_H)
                    if(gReg) onig_free(gReg);
                    gReg = NULL;
#endif
                    if(!match_next(0)) {
                        string_put(gInputFileName, "");
                    }
                }

                gISearchPartMatch = false;
            }
        }
        else {
            if(!match_next(0)) {
                string_put(gInputFileName, "");
                string_push_back2(gInputFileName, (char)key);

#if defined(HAVE_MIGEMO_H)
                if(gReg) onig_free(gReg);
                gReg = NULL;
#endif
                if(!match_next(0)) {
                    string_put(gInputFileName, "");
                }
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// インクリメンタルサーチ描写
///////////////////////////////////////////////////////////////////////////////
void isearch_view()
{
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();

    if(gISearchPartMatch) 
        mmvprintw(maxy -2, 0, "//");
    else
        mmvprintw(maxy -2, 0, "/");

    char buf[1024];
    char* str = string_c_str(gInputFileName);
    const int len = strlen(str);
    int i;
    for(i=0; i<maxx-35; i++) {
        if(i<len) {
            buf[i] = str[i];
        }
        else {
            buf[i] = ' ';
        }
    }
    buf[i] = 0;
    mprintw(buf);
}

void isearch_explore_view()
{
    const int maxx = mgetmaxx();

    if(gISearchPartMatch) 
        mprintw("//");
    else
        mprintw("/");

    char buf[1024];
    char* str = string_c_str(gInputFileName);
    const int len = strlen(str);
    int i;
    for(i=0; i<maxx-35; i++) {
        if(i<len) {
            buf[i] = str[i];
        }
        else {
            buf[i] = ' ';
        }
    }
    buf[i] = 0;
    mprintw(buf);
}

