#include "common.h"
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/wait.h>

///////////////////////////////////////////////////////////////////////////////
// パスを絶対パスにする
///////////////////////////////////////////////////////////////////////////////
bool correct_path(char* current_path, char* path, char* path2)
{
    char tmp[PATH_MAX];
    char tmp2[PATH_MAX];
    char tmp3[PATH_MAX];
    char tmp4[PATH_MAX];

    /// 先頭にカレントパスを加える ///
    {
        if(path[0] == '/' || path[0] == '~' || path[0] == '$') {
            strcpy(tmp, path);
        }
        else {
            if(current_path == NULL) {
                char* cwd = mygetcwd();
                
                strcpy(tmp, cwd);
                strcat(tmp, "/");
                strcat(tmp, path);
                
                FREE(cwd);
            }
            else {
                strcpy(tmp, current_path);
                strcat(tmp, path);
            }
        }

    }

    /// ~と環境変数を展開する ///
    {
        char* home = getenv("HOME");

        if(home == NULL) {
            fprintf(stderr, "$HOME is not setted");
            exit(1);
        }
        
        if(tmp[0] == '~' && strlen(tmp) == 1) {
            strcpy(tmp2, home);
        }
        else if(tmp[0] == '~' && tmp[1] == '/') {
            strcpy(tmp2, home);
            strcat(tmp2, "/");
            strcat(tmp2, tmp + 2);
        }
        else if(tmp[0] == '$') {
            char* p;
            char* p2;
            char env[256];
            char* env_value;

            p = tmp + 1;
            p2 = env;
            while(*p != '/' && *p) {
                *p2++ = *p++;
            }
            *p2 = 0;

            env_value = getenv(env);
            if(env_value == NULL) {
                strcpy(tmp2, p);
            }
            else {
                if(*p == '/') {
                    strcpy(tmp2, env_value);
                    strcat(tmp2, p);
                }
                else {
                    strcpy(tmp2, env_value);
                }
            }
        }
        else {
            strcpy(tmp2, tmp);
        }

    }

   
    /// .を削除する ///
    {
        char* p;
        char* p2;
        int i;

        p = tmp3;
        p2 = tmp2;
        while(*p2) {
            if(*p2 == '/' && *(p2+1) == '.' && *(p2+2) != '.' && ((*p2+3)=='/' || *(p2+3)==0)) {
                p2+=2;
            }
            else {
                *p++ = *p2++;
            }
        }
        *p = 0;

        if(*tmp3 == 0) {
            *tmp3 = '/';
            *(tmp3+1) = 0;
        }

    }

    /// ..を削除する ///
    {
        char* p;
        char* p2;

        p = tmp4;
        p2 = tmp3;

        while(*p2) {
            if(*p2 == '/' && *(p2+1) == '.' && *(p2+2) == '.' 
                && *(p2+3) == '/')
            {
                p2 += 3;

                do {
                    p--;

                    if(p < tmp4) {
                        strcpy(path2, "/");
                        return false;
                    }
                } while(*p != '/');

                *p = 0;
            }
            else if(*p2 == '/' && *(p2+1) == '.' && *(p2+2) == '.' 
                && *(p2+3) == 0) 
            {
                do {
                    p--;

                    if(p < tmp4) {
                        strcpy(path2, "/");
                        return false;
                    }
                } while(*p != '/');

                *p = 0;
                break;
            }
            else {
                *p++ = *p2++;
            }
        }
        *p = 0;
    }

    if(*tmp4 == 0) {
        strcpy(path2, "/");
    }
    else {
        strcpy(path2, tmp4);
    }

    return true;
}

///////////////////////////////////////////////////////////////////////////////
// rubyのeval実行時エラーがあればエラー表示
///////////////////////////////////////////////////////////////////////////////
void output_eval_error(int error)
{
    if(error) {
        mendwin();

        mclear_immediately();
        mmove_immediately(0, 0);
        fflush(stdout);
        
        switch(error) {
        case TAG_RETURN:
            fprintf(stderr, "unexpected return\n");
            break;
            
        case TAG_NEXT:
            fprintf(stderr, "unexpected next\n");
            break;
            
        case TAG_BREAK:
            fprintf(stderr, "unexpected break\n");
            break;
            
        case TAG_REDO:
            fprintf(stderr, "unexpected redo\n");
            break;
            
        case TAG_RETRY:
            fprintf(stderr, "retry outside of rescue clause\n");
            break;
            
        case TAG_RAISE:
        case TAG_FATAL:
            fprintf(stderr, "%s: %s\n",
                rb_class2name(CLASS_OF(ruby_errinfo)),
                RSTRING(rb_obj_as_string(ruby_errinfo))->ptr);
            break;
        
        default:
            fprintf(stderr, "unknown longjmp status %d\n", error);
            break;
        }
        

        printf("HIT ANY KEY");
        fflush(stdout);

        minitscr();

        int meta;
        mgetch(&meta);

        mclear();
    }
}





void cut(char* src, char* dest, int len)
{
    int n;
    bool tmpend = false;
    bool kanji = false;
    for(n=0; n<len; n++) {
        if(kanji)
            kanji = false;
        else if(!tmpend && is_kanji(src[n])) {
            kanji = true;
        }

        if(!tmpend && src[n] == 0) tmpend = true;
                    
        if(tmpend)
            dest[n] = ' ';
        else
            dest[n] = src[n];
    }
    
    if(kanji) dest[n-1] = ' ';
    dest[n] = 0;
}

void cut2(char* src, char* dest, int len)
{
    int n;
    bool kanji = false;
    for(n=0; n<len; n++) {
        if(!kanji && is_kanji(src[n])) {
            kanji = true;
        }
        else {
            kanji = false;
        }

        dest[n] = src[n];
    }
    
    if(kanji)
        dest[n-1] = 0;
    else
        dest[n] = 0;
}

bool gErrMsgCancel = false;

char gErrMsgBuf[BUFSIZ];

void err_msg_view()
{
    int maxx = mgetmaxx();
    int maxy = mgetmaxy();

    mclear_online(maxy-2);
    mclear_online(maxy-1);
    mmvprintw(maxy-2, 0, "%s", gErrMsgBuf);

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

int err_msg(char* msg, ...)
{
    va_list args;
    va_start(args, msg);
    int ret = vsprintf(gErrMsgBuf, msg, args);
    va_end(args);
    
    gViewSystem = err_msg_view;
    
    while(true) {
        err_msg_view();
        mrefresh();

        int meta;
        int key = 0;
        if(!gErrMsgCancel) {
            key = mgetch(&meta);
        }
        
        if(key == 12) {            // CTRL-L
            mclear_immediately();
        }
        else if(key == 3 || key == 7 || key == 27) {
            gErrMsgCancel = true;
            break;
        }
        else {
            break;
        }
    }
    
    gViewSystem = NULL;
    
    return ret;
}

char gMsgBuf[BUFSIZ];
    
void msg_view()
{
    int maxx = mgetmaxx();
    int maxy = mgetmaxy();

    mclear_online(maxy-2);
    mclear_online(maxy-1);
    mmvprintw(maxy-2, 0, "%s", gMsgBuf);

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

void msg(char* msg, ...)
{
    va_list args;
    va_start(args, msg);
    int ret = vsprintf(gMsgBuf, msg, args);
    va_end(args);
    
    gViewSystem = msg_view;
    
    while(true) {
        msg_view();
        mrefresh();

        int meta;
        int key = mgetch(&meta);
        
        if(key == 12) {            // CTRL-L
            mclear_immediately();
        }
        else {
            break;
        }
    }
    
    gViewSystem = NULL;
}

void msg_nonstop(char* msg, ...)
{
    char buf[BUFSIZ];

    va_list args;
    va_start(args, msg);
    int ret = vsprintf(buf, msg, args);
    va_end(args);
    
    const int maxx = mgetmaxx();
    const int maxy = mgetmaxy();
    
    mclear_online(maxy-2);
    mclear_online(maxy-1);
    mmvprintw(maxy-2, 0, "%s", buf);
    mrefresh();

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

char* gSelectStrMsg;
char** gSelectStrStr;
int gSelectStrCursor;
int gSelectStrLen;

void select_str_view()
{
    int maxx = mgetmaxx();
    int maxy = mgetmaxy();
    
    /// view ///
    mclear_online(maxy-2);
    mclear_online(maxy-1);

    mmove(maxy-2, 0);
    mprintw("%s", gSelectStrMsg);
    
//        mmove(maxy-1, 0);
    mprintw(" ");
    for(int i=0; i< gSelectStrLen; i++) {
        if(gSelectStrCursor == i) {
            mattron(kCAReverse);
            mprintw("%s", gSelectStrStr[i]);
            mattroff();
            mprintw(" ");
        }
        else {
            mprintw("%s ", gSelectStrStr[i]);
        }
    }

    mmove(maxy-1, maxx-2);
}
    
int select_str(char* msg, char* str[], int len, int cancel)
{
    gSelectStrMsg = msg;
    gSelectStrStr = str;
    gSelectStrLen = len;
    
    gSelectStrCursor = 0;
    
    gViewSystem = select_str_view;
    
    while(1) {
        select_str_view();
        mrefresh();

        /// input ///
        int meta;
        int key = mgetch(&meta);
        if(key == 10 || key == 13) {
            break;
        }
        else if(key == 6 || key == KEY_RIGHT) {
            gSelectStrCursor++;

            if(gSelectStrCursor >= len) gSelectStrCursor = len-1;
        }
        else if(key == 2 || key == KEY_LEFT) {
            gSelectStrCursor--;

            if(gSelectStrCursor < 0) gSelectStrCursor= 0;
        }
        else if(key == 12) {            // CTRL-L
            mclear_immediately();
        }
        else if(key == 3 || key == 7 || key == 27) {    // CTRL-C -G Escape
            gSelectStrCursor = cancel;
            break;
        }
        else {
            for(int i=0; i< len; i++) {
                if(toupper(key) == toupper(str[i][0])) {
                    gSelectStrCursor = i;
                    goto finished;
                }
            }
        }
    }
finished:
    
    gViewSystem = NULL;

    return gSelectStrCursor;
}

///////////////////////////////////////////////////////////////////////////////
// インプットボックス
///////////////////////////////////////////////////////////////////////////////
// result 0: ok 1: cancel
static void input_box_cursor_move(string_obj* input, int* cursor, int v)
{
    if(gKanjiCode == kUtf8) {
        char* str = string_c_str(input);
        int utfpos = str_pointer2utfpos(str, str + *cursor);
        utfpos+=v;
        *cursor = str_utfpos2pointer(str, utfpos) - str;
    }
    else {
        *cursor += v;

        if(*cursor < 0) *cursor = 0;
        if(*cursor > string_length(input)) *cursor = string_length(input);
    }
}

char* gInputBoxMsg;
string_obj* gInputBoxInput;
int gInputBoxCursor;
    
void input_box_view()
{
    int maxx = mgetmaxx();
    int maxy = mgetmaxy();

    /// view ///
    mclear_online(maxy-2);
    mclear_online(maxy-1);
    
    mmvprintw(maxy-2, 0, "%s", gInputBoxMsg);
    
    mmove(maxy-1, 0);
    
    const int len = string_length(gInputBoxInput);
    for(int i=0; i< len && i<maxx-1; i++) {
        mprintw("%c", string_c_str(gInputBoxInput)[i]);
    }

    //mmove_immediately(maxy -1, gInputBoxCursor);
    mmove(maxy -1, gInputBoxCursor);
}

int input_box(char* msg, char* result, char* def_input, int def_cursor)
{
    gInputBoxMsg = msg;
    
    int result2 = 0;
    gInputBoxCursor = def_cursor;

    gInputBoxInput = string_new(def_input);
    
    gViewSystem = input_box_view;
    
    while(1) {
        input_box_view();
        mrefresh();

        /// input ///
        int meta;
        int key = mgetch(&meta);
        if(key == 10 || key == 13) {
            result2 = 0;
            break;
        }
        else if(key == 6 || key == KEY_RIGHT) {
            if(gKanjiCode == kUtf8) {
                input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, 1);
            }
            else {
                if(is_kanji(string_c_str(gInputBoxInput)[gInputBoxCursor]))
                    input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, 2);
                else
                    input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, 1);
            }
        }
        else if(key == 2 || key == KEY_LEFT) {
            if(gKanjiCode == kUtf8) {
                input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -1);
            }
            else {
                if(gInputBoxCursor > 0) {
                    if(gInputBoxCursor== 1) {
                        input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -1);
                    }
                    else {
                        if(is_kanji(string_c_str(gInputBoxInput)[gInputBoxCursor-2]))
                            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -2);
                        else 
                            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -1);
                    }
                }
            }
        }
        else if(key == 8 || key == KEY_BACKSPACE) {    // CTRL-H
            if(gInputBoxCursor > 0) {
                char* str2 = string_c_str(gInputBoxInput);

                if(gKanjiCode == kUtf8) {
                    int utfpos = str_pointer2utfpos(str2, str2 + gInputBoxCursor);
                    char* before_point = str_utfpos2pointer(str2, utfpos-1);
                    int new_cursor = before_point-str2;

                    string_erase(gInputBoxInput, before_point - str2, (str2 + gInputBoxCursor) - before_point);
                    gInputBoxCursor = new_cursor;
                }
                else {
                    if(gInputBoxCursor == 1) {
                        string_erase(gInputBoxInput, gInputBoxCursor-1, 1);
                        input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -1);
                    }
                    else {
                        if(is_kanji(string_c_str(gInputBoxInput)[gInputBoxCursor-2])) {
                            string_erase(gInputBoxInput, gInputBoxCursor-2, 2);

                            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -2);
                        } else {
                            string_erase(gInputBoxInput, gInputBoxCursor-1, 1);

                            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -1);
                        }
                    }
                }
            }
        }
        else if(key == 4 || key == KEY_DC) {    // CTRL-D DELETE
            char* str2 = string_c_str(gInputBoxInput);
            if(string_length(gInputBoxInput) > 0) {
                if(gInputBoxCursor < string_length(gInputBoxInput)) {
                    if(gKanjiCode == kUtf8) {
                        int utfpos = str_pointer2utfpos(str2, str2 + gInputBoxCursor);
                        char* next_point = str_utfpos2pointer(str2, utfpos+1);

                        string_erase(gInputBoxInput, gInputBoxCursor, next_point - (str2 + gInputBoxCursor));
                    }
                    else {
                        if(is_kanji(string_c_str(gInputBoxInput)[gInputBoxCursor])) {
                            string_erase(gInputBoxInput, gInputBoxCursor, 2);
                        }
                        else {
                            string_erase(gInputBoxInput, gInputBoxCursor, 1);
                        }
                    }
                }
            }
        }
        else if(key == 1 || key == KEY_HOME) {    // CTRL-A
            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, -999);
        }
        else if(key == 5 || key == KEY_END) {    // CTRL-E
            input_box_cursor_move(gInputBoxInput, &gInputBoxCursor, 999);
        }
        else if(key == 11) {    // CTRL-K
            string_erase(gInputBoxInput, gInputBoxCursor, string_length(gInputBoxInput)-gInputBoxCursor);
        }
        
        else if(key == 21) {    // CTRL-U
            string_put(gInputBoxInput, "");

            gInputBoxCursor = 0;
        }
        else if(key == 23) {     // CTRL-W
            if(gInputBoxCursor > 0) {
                const char* s = string_c_str(gInputBoxInput);
                int pos = gInputBoxCursor-1;
                if(s[pos]==' ' || s[pos]=='/' || s[pos]=='\'' || s[pos]=='"') {
                    while(pos>=0 && (s[pos]==' ' || s[pos]=='/' || s[pos]=='\'' || s[pos]=='"'))
                    {
                        pos--;
                    }
                }
                while(pos>=0 && s[pos]!=' ' && s[pos]!='/' && s[pos]!='\'' && s[pos]!='"')
                {
                    pos--;
                }

                string_erase(gInputBoxInput, pos+1, gInputBoxCursor-pos-1);

                gInputBoxCursor = pos+1;
            }
        }
        else if(meta==1 && key == 'd') {     // Meta-d
            const char* s = string_c_str(gInputBoxInput);

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

                string_erase(gInputBoxInput, gInputBoxCursor, pos-gInputBoxCursor);
            }
        }
        else if(meta==1 && key == 'b') {     // META-b
            if(gInputBoxCursor > 0) {
                const char* s = string_c_str(gInputBoxInput);
                int pos = gInputBoxCursor;
                pos--;
                while(pos>=0 && (s[pos] == ' ' || s[pos] == '/' || s[pos] == '\'' || s[pos] == '"')) {
                    pos--;
                }
                while(pos>=0 && s[pos] != ' ' && s[pos] != '/' && s[pos] != '\'' && s[pos] != '"') {
                    pos--;
                }

                gInputBoxCursor = pos+1;
            }
        }
        else if(meta==1 && key == 'f') {     // META-f
            const char* s = string_c_str(gInputBoxInput);

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

                gInputBoxCursor = pos;
            }
        }
        else if(key == 3 || key == 7 || key == 27) {    // CTRL-C -G Escape
            result2 = 1;
            break;
        }
        else if(key == 12) {            // CTRL-L
            mclear_immediately();
        }
        else {
            if(gKanjiCode == kUtf8) {
                if(meta == 0 && !(key >= 0 && key <= 27)) {
                    char tmp[128];

                    sprintf(tmp, "%c", key);
                    string_insert(gInputBoxInput, gInputBoxCursor, tmp);
                    gInputBoxCursor++;
                }
            }
            else {
                if(is_kanji(key)) {
                    int meta;
                    int key2 = mgetch(&meta);

                    char tmp[128];
                    sprintf(tmp, "%c%c", (char)key, (char)key2);
                    string_insert(gInputBoxInput, gInputBoxCursor, tmp);
                    gInputBoxCursor+=2;
                }
                else if(meta == 0 && ' ' <= key && key <= '~') {
                    char tmp[128];
                    sprintf(tmp, "%c", key);
                    string_insert(gInputBoxInput, gInputBoxCursor, tmp);
                    gInputBoxCursor++;
                }
            }
        }
    }
    
    gViewSystem = NULL;
    
    int maxx = mgetmaxx();
    int maxy = mgetmaxy();
    
    strcpy(result, string_c_str(gInputBoxInput));
    string_delete(gInputBoxInput);

    mmove_immediately(maxy -2, 0);
    
    return result2;
}

void strtolower(char* result)
{
    const int len = strlen(result);

    for(int i=0; i<len; i++) {
        result[i] = tolower(result[i]);
    }
}

///////////////////////////////////////////////////////////////////////////////
// system
///////////////////////////////////////////////////////////////////////////////
int msystem(char* line)
{
#if !HAVE_DECL_ENVIRON
    extern char **environ;
#endif

    int status, save;
    pid_t pid;
    struct sigaction sa, intr, quit;
#ifndef WAITPID_CANNOT_BLOCK_SIGCHLD
    sigset_t block, omask;
#endif

    if (line == NULL) return 0;

    sa.sa_handler = SIG_IGN;
    sa.sa_flags = 0;
    sigemptyset (&sa.sa_mask);

    if (sigaction (SIGINT, &sa, &intr) < 0)
        return -1;
    if (sigaction (SIGQUIT, &sa, &quit) < 0)
    {
        save = errno;
        (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
        errno = save;
        return -1;
    }

#ifndef WAITPID_CANNOT_BLOCK_SIGCHLD

    /* SCO 3.2v4 has a bug where `waitpid' will never return if SIGCHLD is
       blocked.  This makes it impossible for `system' to be implemented in
       compliance with POSIX.2-1992.  They have acknowledged that this is a bug
       but I have not seen nor heard of any forthcoming fix.  */

    sigemptyset (&block);
    sigaddset (&block, SIGCHLD);
    save = errno;
    if (sigprocmask (SIG_BLOCK, &block, &omask) < 0)
    {
        if (errno == ENOSYS)
            errno = save;
        else
        {
            save = errno;
            (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
            (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
            errno = save;
            return -1;
        }
    }
# define UNBLOCK sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL)
#else
# define UNBLOCK 0
#endif

    pid = fork ();
    if (pid == (pid_t) 0)
    {
        /* Child side.  */
        const char *new_argv[4];
        new_argv[0] = gShellName;
        new_argv[1] = gShellOptEval;
        new_argv[2] = line;
        new_argv[3] = NULL;

        /* Restore the signals.  */
        (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL);
        (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
        (void) UNBLOCK;

        /* Exec the shell.  */
        (void) execve (gShellPath, (char *const *) new_argv, environ);
        _exit (127);
    }
    else if (pid < (pid_t) 0)
        /* The fork failed.  */
        status = -1;
    else
        /* Parent side.  */
    {
#ifdef    NO_WAITPID
        pid_t child;
        do
        {
            child = wait (&status);
            if (child <= -1 && errno != EINTR)
            {
                status = -1;
                break;
            }
            /* Note that pid cannot be <= -1 and therefore the loop continues
               when wait returned with EINTR.  */
        }
        while (child != pid);
#else
        int n;

        do
            n = waitpid (pid, &status, 0);
        while (n == -1 && errno == EINTR);

        if (n != pid)
            status = -1;
#endif
    }

    save = errno;
    if ((sigaction (SIGINT, &intr, (struct sigaction *) NULL) |
                sigaction (SIGQUIT, &quit, (struct sigaction *) NULL) |
                UNBLOCK) != 0)
    {
        if (errno == ENOSYS)
            errno = save;
        else
            return -1;
    }

    return status;
}

