/*
 * シェルスクリプトエンジン
 * 湊大典(c) 2009-2010
 */

#ifndef SAPHIRE_H
#define SAPHIRE_H

#include "saphire_vector.h"
#include "saphire_list.h"
#include "saphire_string.h"
#include "saphire_kanji.h"
#include "saphire_curses.h"
#include "saphire_hash.h"
#include "saphire_debug.h"
#include "saphire_extra.h"

/*
    使い方のサンプルはmain.cを参考にしてください
*/

typedef struct {
    int mFd;
    char* mBuffer;
    char* mP;
    int mMallocSize;
} sWFd;

sWFd* sWFd_new(int fd);
void sWFd_delete(sWFd* self);
BOOL sWFd_push_back(sWFd* self, char* str);
BOOL sWFd_flash(sWFd* self);
BOOL sWFd_close(sWFd* self);

typedef struct {
    int mFd;
    string_obj* mBuffer;
} sRFd;

sRFd* sRFd_new(int fd);
sRFd* sRFd_new2(int fd, char* str);
void sRFd_delete(sRFd* self);
BOOL sRFd_close(sRFd* self);
int sRFd_read_all(sRFd* self, string_obj* str);
int sRFd_read_all_preserve(sRFd* self, string_obj* str);
int sRFd_read_oneline(sRFd* self, string_obj* str);
int sRFd_read_oneline_preserve(sRFd* self, string_obj* str);
int sRFd_read_all_to_buffer(sRFd* self);

extern enum eKanjiCode gKanjiCode;
    // saphireが使うデフォルトの漢字コード
    // 初期値はUTF8
    // (端末の漢字コードとは別でかまわない)
enum eLineField { kLF, kCRLF, kCR };
extern enum eLineField gLineField;
    // saphireが文字列処理の内部コマンドで使う改行コード
enum eAppType { kATOptC, kATXApp, kATCursesApp, kATConsoleApp };
    // アプリケーションの種類
    //
    // kATOptC コマンドラインから使う場合
    // kATXApp Xを使っているアプリ
    // kATCursesApp 端末制御ライブラリを使っているコンソールアプリ
    // kATConsoleApp 端末制御ライブラリを使わないコンソールアプリ

enum eRuntimeScript { kRSNoRead, kRSSource, kRSObject };
    // ランタイムスクリプトをどうするのかの設定

    // kRSNoRead 読み込まない
    // kRSSource ソースファイルを読み込む
    // kRSObject オブジェクトファイルを読み込む

void saphire_init(enum eAppType app_type, BOOL job_control, enum eRuntimeScript runtime_script, BOOL run_user_runtimescript);
    // 初期化。saphire_shellなどを実行する前に必ず必要
    // アプリケーションの種別とジョブコントロールの有無とランタイムスクリプトをどうするかを読み込むかを設定してください
    // あと、必要な初期化は端末制御ライブラリsaphire_curses.h
    // のmcurses_init()です。
void saphire_final();
    // 終了化。終了時に必要
void saphire_set_signal();
    // saphireを実行中に使うシグナルの設定を設定する。
    // saphire_shell, saphie_shell3などのsaphireのコードを実行する前
    // に必ず呼んでください。
    // 呼ばれないと正しくコマンドは実行されません。

    // 処理しているのは
    // SIGCHLD, SIGINT, SIGCONT, SIGWINCH, SIGUSR1
    // 無視しているのは
    // SIGTTOU, SIGTTIN, SIGTSTP, SIGQUIT, SIGPIPE
void saphire_set_signal_optc();
    // saphireを実行中に使うシグナルの設定を設定する。
    // saphire_shell, saphie_shell3などのsaphireのコードを実行する前
    // に必ず呼んでください。
    // 呼ばれないと正しくコマンドは実行されません。

    // 処理しているのは
    // SIGCHLD, SIGINT, SIGCONT, SIGWINCH, SIGUSR1
    // 無視しているのは
    // SIGTTOU, SIGTTIN, SIGTSTP, SIGQUIT, SIGPIPE
void saphire_restore_signal_default();
    // シグナルを初期値に戻す
extern void (*saphire_set_signal_other)();
    // もし関数ポインタが代入されていたら
    // saphire_set_signalで実行される
extern string_obj* gErrMsg;
    // エラーが起こった時はこの配列にメッセージ入っているので出力してください
int saphire_shell(char* command, char* title, sWFd* pipeout, sRFd* pipein, int pipeerr);
    // 文字列を読み込んで実行
    // commandは実行するコマンドの文字列。titleはタイトル
    // pipeoutは出力先。pipeinは入力先
    // pipeerrはエラー先（ファイルディスクリプタ)
    // 戻り値はプログラムの終了コードだが、0以下だと
    // saphire内でエラーが起こっているのでgErrMsgをどこかに
    // 出力してください。
    // verboseは実行しているコマンド名を表示するかどうか
int saphire_shell3(string_obj* result, char* command, char* title, sRFd* pipein);
    // 文字列を読み込んで実行。コマンドの出力はresult
    // (saphire_string.hで定義されているstring_obj*文字列型)に入る
    // 戻り値はプログラムの終了コードだが、0以下だとsaphire内でエラーが
    // 起こっている
    // のでgErrMsgをどこかに出力してください。
    // resultはstring_newで領域を確保しておかないといけない
    // verboseは実行しているコマンド名を表示するかどうか
int saphire_load(char* fname, sWFd* pipeout, sRFd* pipein, int pipeerr);
    // スクリプトファイルを読み込んで実行
    // pipeoutは出力先。pipeinは入力先
    // pipeerrはエラー先（ファイルディスクリプタ)
    // 戻り値はプログラムの終了コードだが、0以下だとsaphire内で
    // エラーが起こっている
    // のでgErrMsgをどこかに出力してください。
    // verboseは実行しているコマンド名を表示するかどうか
BOOL saphire_compile(char* fname, char* out_fname);
    // fnameのファイルをコンパイルしてout_fnameに保存
    // スクリプトファイルを読み込んでコンパイルして
    // ファイルとして書き込む関数。
    // エラー時はgErrMsgにエラーメッセージが入っている
int saphire_load_obj(char* fname, sWFd* pipeout, sRFd* pipein, int pipeerr, vector_obj* parent_blocks);
    // コンパイル済みのファイルfnameを実行
    // pipeoutは出力先。pipeinは入力先
    // pipeerrはエラー先（ファイルディスクリプタ)
    // 戻り値はプログラムの終了コードだが、0以下だとsaphire内で
    // エラーが起こっている
    // のでgErrMsgをどこかに出力してください。
    // parent_blocksは親のブロック引数。無いならNULL
void saphire_wait_background_job();
    // バックグランドジョブの状態のチェック
    // ジョブ管理をする場合メインループ中に定期的に実行してください
void saphire_wait_background_job2();
    // バックグランドジョブの状態のチェック
    // ジョブ管理をする場合メインループ中に定期的に実行してください
extern void (*saphire_job_done)(int job_num, char* job_title);
    // ジョブ管理しているときにジョブが終わったら実行する処理
    // job_numに終わったジョブの番号が入いれて呼ばれる。
    // job_titleに終わったジョブのタイトルを入れて呼ばれる
int saphire_job_num();
    // 現在のジョブの数を返す
char* saphire_job_title(int num);
    // num番号のジョブのタイトルを返す。
void saphire_kill_job(int num);
    // num番号のジョブを消す
void saphire_kill_all_jobs();
    // 全てのジョブを強制消去
    // アプリケーションを終了する前に実行してください
    // ただし大抵のジョブは終わっていないと問題が起こると
    // 思われるのでsaphire_job_numで終わっていないジョブがあると
    // アプリケーションを終われないようにしとくほうがいいかもしれません。

extern vector_obj* gSaphireProgCompletions;               // 補完候補

extern hash_obj* gGlobals;
    // グローバル変数 キーは変数名 内容はstring_obj*
extern hash_obj* gArrays;
    // グローバル変数の配列 キーは変数名
    // 内容は配列(vector_obj*) 配列の中にはstring_obj*
    // が入っている
extern hash_obj* gHashs;
    // グローバル変数の配列 キーは変数名
    // 内容はハッシュ(hash_obj*) ハッシュの中にはstring_obj*
    // が入っている

void saphire_rehash();
    // プログラム補完候補の一覧の再読み込み
    // $PATHから読み込む
    // gSaphireProgCompletions(動的配列型vector_obj*)に文字列型(string_obj*)として入る

extern BOOL gKitutukiExit;
        // exit内部コマンドが呼ばれたらTRUEが入る
        // メインループでTRUEが入っていたらプログラムを
        // 終了してください。
extern void saphire_get_quoted_fname(char* fname, string_obj* quoted_fname);
        // saphireシェルで使われる特殊な文字をクォートした文字列を返す
        // &,|,>など

typedef BOOL (*fInnerCommand)(int* rcode, vector_obj* argv, vector_obj* blocks, vector_obj* parent_blocks, sWFd* nextout, sRFd* nextin, int nexterr, char* title, BOOL input);
        // 内部コマンドの実行コード
        // 戻り値は実行に成功したらTRUE, 実行に失敗したらFALSEを返してください。
        // rcodeはリターンコード. 何も入力しなかったら最初は1が入っている
        // コマンドが成功したら*rcode = 0などとしてコマンドの成功を
        // 示してください。
        // argvはstring_obj*（文字列）のvector_obj*(動的配列)。
        // コマンドライン引数が入っている。(0番目はコマンド名)
        // blocksはsStatments*(文)のvector_obj*(動的配列)
        // 引数のブロックが入っている
        // parent_blocksはsStatments*(文)のvector_obj*(動的配列)
        // 親のブロックが入っている
        // 親がなければNULL
        // nextoutは出力先 (パイプやファイルや端末など適切に処理される)
        // nextinは入力元
        // nexterrはエラー出力先
        // titleはコマンドのタイトルが入っている
        // inputはパイプ処理を行う場合はTRUEになっている
        // ls | command などのcommand
        // | command (グローバルパイプ)とかのcommandの場合はTRUE
        // command | lessのcommandだとFALSE

void saphire_add_inner_command(char* name, fInnerCommand fun);
       // 内部コマンドの追加。上のfInnerCommandを実装して第二引数に
       // 関数ポインタを入力。第一引数はコマンド名

extern volatile BOOL gKitutukiSigInt;  // CTRL-Cが押されたらTRUE
extern volatile BOOL gKitutukiSigTstp;  // CTRL-Zが押されたらTRUE
extern volatile BOOL gKitutukiSigCont;  // SITCONT食らったらTRUE

char* saphire_get_local_var(char* name);
    // 現在のスタックフレームでのローカル変数を返す
    // 無ければNULLが返る

void saphire_set_local_var(char* name, char* value);
    // 現在のスタックフレームにローカル変数を追加する
void saphire_delete_local_var(char* name);
    // 現在のスタックフレームのローカル変数を削除する

char* saphire_get_global_var(char* name);
    // グローバル変数を返す
    // 無ければNULLが返る
void saphire_set_global_var(char* name, char* value);
    // グローバル変数を設定する

vector_obj* saphire_get_array(char* name);
    // 配列を返す string_obj*(文字列型)が入ったvector_obj*(動的配列)
    // 無ければNULLが返る
void saphire_set_array(char* name, vector_obj* array);
    // 配列を設定する

hash_obj* saphire_get_hash(char* name);
    // ハッシュを返す string_obj*(文字列型)が入ったhash_obj*(ハッシュ)
    // 無ければNULLが返す

typedef struct 
{
    vector_obj* mStatments;
} sStatments;

sStatments* sStatments_new();
sStatments* sStatments_new2(sStatments* statments);
void sStatments_delete(sStatments* self);
BOOL saphire_compile2(char* cmdline, char* fname, sStatments* statments);
    // cmdlineのソースをコンパイルして結果をstatmentsに格納する。
    // コンパイル結果のstatmentsはsaphire_runによって実行できる。
    // statmentsはsStatments_newによって初期化されないといけない。
    // また必要でなくなったらsStatments_deleteによって開放しないといけない。
    // 終了時にできればsStatments_deleteしたほうがよい。
    // エラー時はgErrMsgにエラー内容が入っている
int saphire_run(sStatments* statments, char* title, sWFd* pipeout, sRFd* pipein, int pipeerr);

    // サンプル
    /*

    sStatments statments = sStatments_new();

    saphire_compile2("ls -al | less; pwd", "title", statments);

    saphire_run(statments, "run", STDOUT_FILENO, STDIN_FILENO, STDERR_FILENO, FALSE);

    sStatments_delete(statments);

    */

// 出力をCTRL-Cで止めたいので、この関数を経由して書き込む
// 戻り値 FALSE --> CTRL-Cで止められた
// 戻り値 TRUE --> 正常終了
BOOL statment_tree_internal_commands_write_nextout(sWFd* nextout, char* str);

// 出力をCTRL-Cで止めたいので、この関数を経由して読み込む
// 戻り値 -1 --> CTRL-Cで止められた
// 戻り値 0 --> 正常終了
// 戻り値 1 --> EOF
int statment_tree_internal_commands_read_nextin(sRFd* nextin, string_obj* str);

// 出力をCTRL-Cで止めたいので、この関数を経由して読み込む
// 戻り値 -1 --> CTRL-Cで止められた
// 戻り値 0 --> 正常終了
// 戻り値 1 --> EOF
// 読み込んでもバッファを読み込んだ分のバッファを消さない
int statment_tree_internal_commands_read_nextin_preserve(sRFd* nextin, string_obj* str);

// 出力をCTRL-Cで止めたいので、この関数を経由して読み込む
// 戻り値 -1 --> CTRL-Cで止められた
// 戻り値 0 --> 正常終了
// 戻り値 1 --> EOF
int statment_tree_internal_commands_read_nextin_oneline(sRFd* nextin, string_obj* str);

// 出力をCTRL-Cで止めたいので、この関数を経由して読み込む
// 戻り値 -1 --> CTRL-Cで止められた
// 戻り値 0 --> 正常終了
// 戻り値 1 --> EOF
// 読み込んでも読み込んだ分のバッファを消さない
int statment_tree_internal_commands_read_nextin_oneline_preserve(sRFd* nextin, string_obj* str);

void saphire_sweep();
    // ごみ掃除
    // sapire_shellやsaphire_loadが呼ばれた後呼び出してください

extern vector_obj* gSaphireVarCompletions;
// 変数補完候補

void saphire_make_var_completion_list();          
// 変数補完候補を更新

typedef struct {
    sStatments* mStatments;
    vector_obj* mArgs;
} sBlock;
// ブロック

void err_msg(char* msg, char* sname, int line);
    // エラーメッセージを設定

#endif

