#include "config.h"
#include "saphire.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <locale.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <libgen.h>
#include <dirent.h>
#include <oniguruma.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <limits.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.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

#include "saphire_extra.h"

/// from readline.c ///
void readline_init();
void readline_final();

//////////////////////////////////////////////////////////////////////
// exit時に実行される関数
//////////////////////////////////////////////////////////////////////
static void atexit_fun()
{
    if(mis_raw_mode()) {
        mendwin();
    }
}


//////////////////////////////////////////////////////////////////////
// メイン関数
//////////////////////////////////////////////////////////////////////
static void usage()
{
    printf("usage saphiresh or saphire [-c command] [ -s -w -e ] [ -Lu -Lm -Lw ] [--version ] [ script file]\n\n");
    printf("-c : eval a command on saphire\n");
    printf("-rs : use run time a source run time script\n");
    printf("-ro : use run time a compiled run time script(default)\n");
    printf("-rn : no use run time script\n");
    printf("-ts : set termianl kanjicode sjis\n");
    printf("-tw : set termianl kanjicode utf8\n");
    printf("-te : set termianl kanjicode eucjp\n");
    printf("-s : set saphire kanjicode sjis\n");
    printf("-w : set saphire kanjicode utf8\n");
    printf("-e : set saphire kanjicode eucjp\n");
    printf("-Lu : set line field LF\n");
    printf("-Lm : set line field CR\n");
    printf("-Lw : set line field CRLF\n");
    printf("--version : display saphire version\n");

    exit(0);
}

static void version()
{
    printf("saphire version %s.", getenv("SAPHIRE_VERSION"));
    printf("author Daisuke Minato.\n");
    
    exit(0);
}

void main_saphire_job_done(int job_num, char* job_title)
{
    printf("%d: %s done.\n", job_num+1, job_title);
}

static int mreadline_signal()
{
    struct sigaction sa2;
    memset(&sa2, 0, sizeof(sa2));
    sa2.sa_handler = SIG_DFL;
//    sa2.sa_flags |= SA_RESTART;
    if(sigaction(SIGINT, &sa2, NULL) < 0) {
        perror("sigaction2");
        exit(1);
    }
}

#if defined(ENABLE_READLINE)
void sig_int_readline(int signo)
{
    rl_kill_full_line(0,0);
    rl_reset_line_state();
    rl_crlf();
    rl_redisplay();
/*
  rl_completion_found_quote = 0;
  rl_completion_quote_character = 0;
    rl_free_line_state ();
      //rl_echo_signal_char (signo);
      rl_cleanup_after_signal ();
      rl_reset_after_signal ();
  //rl_point = cxt->save_point;
  //rl_mark = cxt->save_mark;
  rl_maybe_unsave_line ();
  rl_clear_message ();

  rl_restore_prompt ();
  rl_ding ();
  rl_clear_message ();
//  _rl_reset_argument ();
  rl_clear_pending_input ();
  RL_UNSETSTATE (RL_STATE_MACRODEF);
  while (rl_executing_macro)
    _rl_pop_executing_macro ();

  rl_last_func = (rl_command_func_t *)NULL;
  longjmp (_rl_top_level, 1);
  */
}

static int readline_signal()
{
    struct sigaction sa2;
    memset(&sa2, 0, sizeof(sa2));
//    sa2.sa_handler = SIG_DFL;
    sa2.sa_handler = sig_int_readline;
    sa2.sa_flags |= SA_RESTART;
    if(sigaction(SIGINT, &sa2, NULL) < 0) {
        perror("sigaction2");
        exit(1);
    }
}
#endif

int main(int argc, char* argv[])
{
    /// メモリリークの監視を開始 ///
    CHECKML_BEGIN();

    /// 乱数初期化 ///
    srandom(1000);

    /// ロケール初期化 ///
    setlocale(LC_CTYPE, "ja_JP.UTF-8");

    /// オプション処理 ///
    char send_cmd[BUFSIZ];

    setenv("LINEFIELD","LF", 1);
    enum eTerminalKanjiCode code = kTKUtf8;

    char cwd[PATH_MAX];
    mygetcwd(cwd, PATH_MAX);
    setenv("PWD", cwd, 1);

    vector_obj* argv2 = VECTOR_NEW(10);

    int i;
    enum eRuntimeScript runtime_script = kRSObject;
    for(i=1 ; i<argc; i++){
        // ファイル名
        if(argv[i][0] != '-') {
            vector_add(argv2, STRING_NEW(argv[i]));
        }
        // 引数
        else {
            if(strcmp(argv[i], "--version") == 0) {
                saphire_set_signal();
                saphire_init(kATConsoleApp, FALSE, runtime_script, FALSE);
                version();
                break;
            }
            else if(strcmp(argv[i], "-ts") == 0) {
                code = kTKSjis;
            }
            else if(strcmp(argv[i], "-tw") == 0) {
                code = kTKUtf8;
            }
            else if(strcmp(argv[i], "-te") == 0) {
                code = kTKEucjp;
            }
            else if(strcmp(argv[i], "-rs") == 0) {
                runtime_script = kRSSource;
            }
            else if(strcmp(argv[i], "-ro") == 0) {
                runtime_script = kRSObject;
            }
            else if(strcmp(argv[i], "-rn") == 0) {
                runtime_script = kRSNoRead;
            }
            else {
                switch (argv[i][1]){
                    case 'c':
                        if(i+1 < argc) {
                            snprintf(send_cmd, BUFSIZ, "c%s", argv[i+1]);
                            i++;
                        }
                        else {
                            usage();
                        }
                        break;

                    case 's':
                        gKanjiCode = kSjis;
                        break;

                    case 'e':
                        gKanjiCode = kEucjp;
                        break;

                    case 'w':
                        gKanjiCode = kUtf8;
                        break;

                    case 'L':
                        if(strcmp(argv[i], "-Lu") == 0) {
                            gLineField = kLF;
                        }
                        else if(strcmp(argv[i], "-Lw") == 0) {
                            gLineField = kCRLF;
                        }
                        else if(strcmp(argv[i], "-Lm") == 0) {
                            gLineField = kCR;
                        }
                        else {
                            fprintf(stderr, "invalid option %s", argv[i]);
                            exit(1);
                        }
                        break;

                    default:
                        usage();
                }
            }
        }
    }
/*
    /// saphireは前面にあるか？ ///
    BOOL saphire_fg;
    pid_t pgrp =  tcgetpgrp(0);
    if(pgrp < 0) {
        perror("tcgetpgrp");
        exit(1);
    }
    pid_t pgrp2 = getpgrp();
    if(pgrp == pgrp2) {
        saphire_fg = TRUE;
    }
    else {
        saphire_fg = FALSE;
    }
*/
    /// 引数のファイル名がディレクトリじゃなければスクリプトと判断する ///
    if(vector_size(argv2) > 0) {
        char* file_name = string_c_str(vector_item(argv2, 0));

        struct stat stat_;
        if(stat(file_name, &stat_) < 0) {
            printf("argument file name is invalid\n");
            exit(1);
        }

        if(!S_ISDIR(stat_.st_mode)) {
            mcurses_init(code);
            saphire_set_signal_optc();
            saphire_init(kATOptC, FALSE, runtime_script, FALSE);

            saphire_set_global_var("SCRIPT_FILE_NAME", file_name);

            if(vector_size(argv2) >= 2) {
                vector_obj* argv3 = VECTOR_NEW(50);
                int j;
                for(j=1; j<vector_size(argv2); j++) {
                    vector_add(argv3, STRING_NEW(string_c_str(vector_item(argv2, j))));
                }
                saphire_set_array("ARGV", argv3);
            }
            else {
                saphire_set_array("ARGV", VECTOR_NEW(10));
            }

            sWFd* pipeout = sWFd_new(STDOUT_FILENO);
            sRFd* pipein = sRFd_new(STDIN_FILENO);
            int rcode = saphire_load(file_name, pipeout, pipein, STDERR_FILENO);
            saphire_sweep();
            sWFd_flash(pipeout);
            sWFd_delete(pipeout);
            sRFd_delete(pipein);
            if(rcode == -1) {
                fprintf(stderr, "run time err\n");
                fprintf(stderr, "%s", string_c_str(gErrMsg));
            }
            saphire_final();
            mcurses_final();

            for(i=0; i<vector_size(argv2); i++) {
                string_delete(vector_item(argv2, i));
            }
            vector_delete(argv2);
            CHECKML_END();
            return rcode;
        }
    }

    /// オプションで指定されているならシェルで実行する ///
    if(send_cmd[0] == 'c') {
        mcurses_init(code);
        saphire_set_signal_optc();
        saphire_init(kATOptC, FALSE, runtime_script, FALSE);
        sWFd* pipeout = sWFd_new(STDOUT_FILENO);
        sRFd* pipein = sRFd_new(STDIN_FILENO);
        int rcode = saphire_shell(send_cmd + 1, "saphire -c", pipeout, pipein, STDERR_FILENO);
        saphire_sweep();

        sWFd_flash(pipeout);
        sWFd_delete(pipeout);
        sRFd_delete(pipein);
        if(rcode < 0) { 
            fprintf(stderr, "run time err\n");
            fprintf(stderr, "%s", string_c_str(gErrMsg));
            exit(1);
        }
        saphire_final();
        mcurses_final();

        for(i=0; i<vector_size(argv2); i++) {
            string_delete(vector_item(argv2, i));
        }
        vector_delete(argv2);
        CHECKML_END();
        return rcode;
    }

    setenv("SAPHIRE_INTERACTIVE", "1", 1);

#if !defined(ENABLE_READLINE)
    fprintf(stderr, "if you use saphire as interactive shell, use saphiresh command instead\n");
    for(i=0; i<vector_size(argv2); i++) {
        string_delete(vector_item(argv2, i));
    }
    vector_delete(argv2);
    return 0;
#else
    /// インタラクティブシェル開始 ///
    mreset_tty();   // 一応端末リセット

    mcurses_init(code);

    saphire_set_signal();
    saphire_init(kATConsoleApp, TRUE, runtime_script, TRUE);
    saphire_rehash();
    saphire_job_done = main_saphire_job_done;
    readline_init();

    /// ヒストリーを読み込む ///
    char* histfile = getenv("SAPHIRE_HISTFILE");
    if(histfile) {
        read_history(histfile);
    }
    int histsize = atoi(getenv("SAPHIRE_HISTSIZE"));
    if(histsize < 0) histsize = 1000;

    //saphire_set_signal();

    char* buf;
    int history_num = 0;
    while(1) {
        /// 一行読み込み ///
        readline_signal();

        /// 終了コードの保存 ///
        char end_code[128];
        char* lvar =  saphire_get_local_var("RCODE");
        if(lvar) 
            snprintf(end_code, 128, "%s", lvar);
        else 
            snprintf(end_code, 128, "0");

        /// プロンプト ///
        char* prompt = getenv("SAPHIRE_PROMPT");
        if(prompt) {
            string_obj* ret = STRING_NEW("");
            sRFd* rfd = sRFd_new(0);
            if(saphire_shell3(ret, prompt, "SAPHIRE_PROMPT", rfd) == -1) {
                buf = readline("> ");
            }
            else {
                buf = readline(string_c_str(ret));
            }
            sRFd_delete(rfd);

            string_delete(ret);
        }
        else {
            buf = readline("> ");
        }

        /// 終了コードの復帰 ///
        saphire_set_local_var("RCODE", end_code);

        /// 実行 ///
        if(buf == NULL) {
            if(saphire_job_num() > 0) {
                fprintf(stderr,"\njobs exist\n");
            }
            else {
                break;
            }
        }
        else if(*buf) {
            saphire_set_signal();
            sWFd* pipeout = sWFd_new(STDOUT_FILENO);
            sRFd* pipein = sRFd_new(STDIN_FILENO);
            int rcode = saphire_shell(buf, "saphire", pipeout, pipein, STDERR_FILENO);
            saphire_sweep();
            sWFd_flash(pipeout);
            sWFd_delete(pipeout);
            sRFd_delete(pipein);
            if(rcode == -1) {
                /// ヒストリに追加 ///
                add_history(buf);
                history_num++;
                if(history_num > histsize) {
                    HIST_ENTRY* history = remove_history(0);
                    free(history);
                }

                fprintf(stderr, "run time err\n");
                fprintf(stderr, "%s", string_c_str(gErrMsg));
            }
            else {
                /// ヒストリに追加 ///
                add_history(buf);
                history_num++;
                if(history_num > histsize) {
                    HIST_ENTRY* history = remove_history(0);
                    free(history);
                }
            }
        }

        /// バックグランド待ち ///
        saphire_wait_background_job();

        free(buf);

        if(gKitutukiExit >= 0) {
            break;        // exitコマンドが実行された
        }
    }

    readline_final();
    saphire_kill_all_jobs();
    saphire_final();
    mcurses_final();

    mreset_tty();   // 一応端末リセット

    /// ヒストリーを書き込む ///
    if(histfile) {
        write_history(histfile);
    }

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

    /// メモリリーク検出 ///
    CHECKML_END();
#endif
    return gKitutukiExit;
}

