#include "nasal.h"
#include "nasal_err.h"
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_opt.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"

#include <unordered_map>

using ch_clk=std::chrono::high_resolution_clock;

const u32 VM_AST   =0x01;
const u32 VM_CODE  =0x02;
const u32 VM_TIME  =0x04;
const u32 VM_EXEC  =0x08;
const u32 VM_DETAIL=0x10;
const u32 VM_DEBUG =0x20;
const u32 VM_OPT   =0x40;

std::ostream& help(std::ostream& out)
{
    out
    <<"     ,--#-,\n"
    <<"<3  / \\____\\  <3\n"
    <<"    |_|__A_|\n"
#ifdef _WIN32
    <<"use command <chcp 65001> to use unicode.\n"
#endif
    <<"\nnasal <option>\n"
    <<"option:\n"
    <<"   -h, --help     | get help.\n"
    <<"\nnasal [option] <file> [argv]\n"
    <<"option:\n"
    <<"   -a, --ast      | view abstract syntax tree.\n"
    <<"   -c, --code     | view bytecode.\n"
    <<"   -e, --exec     | execute.\n"
    <<"   -t, --time     | show execute time.\n"
    <<"   -d, --detail   | get detail crash info.\n"
    <<"                  | get detail path-not-found info.\n"
    <<"                  | get detail gc info.\n"
    <<"   -o, --optimize | use optimizer (beta).\n"
    <<"                  | use <-o -e> to run optimized code.\n"
    <<"   -dbg, --debug  | debug mode (ignore -t -d).\n"
    <<"file:\n"
    <<"   <filename>     | execute file.\n"
    <<"argv:\n"
    <<"   <args>         | cmd arguments used in program.\n";
    return out;
}

std::ostream& logo(std::ostream& out)
{
    out
    <<"       __                _\n"
    <<"    /\\ \\ \\__ _ ___  __ _| |\n"
    <<"   /  \\/ / _` / __|/ _` | |\n"
    <<"  / /\\  / (_| \\__ \\ (_| | |\n"
    <<"  \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
    <<"version   : "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n"
    <<"c++ std   : "<<__cplusplus<<"\n"
    <<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
    <<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
    <<"lang wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
    <<"input <nasal -h> to get help .\n";
    return out;
}

[[noreturn]]
void err()
{
    std::cerr
    <<"invalid argument(s).\n"
    <<"use <nasal -h> to get help.\n";
    std::exit(1);
}

void execute(const string& file,const std::vector<string>& argv,const u32 cmd)
{
    error   err;
    lexer   lex(err);
    parse   parse(err);
    linker  ld(err);
    codegen gen(err);
    vm      ctx;

    // lexer scans file to get tokens
    lex.scan(file);
    
    // parser gets lexer's token list to compile
    parse.compile(lex);
    // linker gets parser's ast and load import files to this ast
    ld.link(parse,file,cmd&VM_DETAIL);
    // optimizer does simple optimization on ast
    if(cmd&VM_OPT)
        optimize(parse.tree());
    if(cmd&VM_AST)
        parse.print();
    
    // code generator gets parser's ast and linker's import file list to generate code
    gen.compile(parse,ld);
    if(cmd&VM_CODE)
        gen.print();
    
    // run
    if(cmd&VM_DEBUG)
        debugger(err).run(gen,ld,argv);
    else if(cmd&VM_TIME)
    {
        auto start=ch_clk::now();
        ctx.run(gen,ld,argv,cmd&VM_DETAIL);
        auto end=ch_clk::now();
        std::clog<<"process exited after "<<(end-start).count()*1.0/ch_clk::duration::period::den<<"s.\n";
    }
    else if(cmd&VM_EXEC)
        ctx.run(gen,ld,argv,cmd&VM_DETAIL);
}

i32 main(i32 argc,const char* argv[])
{
    if(argc<=1)
    {
        std::clog<<logo;
        return 0;
    }
    if(argc==2)
    {
        string s(argv[1]);
        if(s=="-h" || s=="--help")
            std::clog<<help;
        else if(s[0]!='-')
            execute(s,{},VM_EXEC);
        else
            err();
        return 0;
    }
    std::unordered_map<string,u32> cmdlst={
        {"--ast",VM_AST},{"-a",VM_AST},
        {"--code",VM_CODE},{"-c",VM_CODE},
        {"--exec",VM_EXEC},{"-e",VM_EXEC},
        {"--time",VM_TIME|VM_EXEC},{"-t",VM_TIME|VM_EXEC},
        {"--detail",VM_DETAIL|VM_EXEC},{"-d",VM_DETAIL|VM_EXEC},
        {"--optimize",VM_OPT},{"-o",VM_OPT},
        {"--debug",VM_DEBUG},{"-dbg",VM_DEBUG}
    };
    u32 cmd=0;
    string filename="";
    std::vector<string> vm_argv;
    for(i32 i=1;i<argc;++i)
    {
        if(cmdlst.count(argv[i]))
            cmd|=cmdlst[argv[i]];
        else if(!filename.length())
            filename=argv[i];
        else
            vm_argv.push_back(argv[i]);
    }
    if(!filename.length())
        err();
    execute(filename,vm_argv,cmd?cmd:VM_EXEC);
    return 0;
}