
#include <iostream>
#include <fstream>

#include "TestParser.h"
#include "TestLexer.h"

#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include "ast/translationcontext.h"


int main(int argc, char** argv)
{
    std::ifstream example( argv[1] );
    TestLexer lexer(&example, &std::cout);

    Statement* ast = nullptr;
    yy::TestParser parser(&lexer, &ast);
    if(parser.parse() == 0)
    {
        #define vt100YELLOW "\x1b[33m"
        std::cout << std::endl << vt100YELLOW;
        ast -> dump(std::cout, 0);
        #define vt100DEFAULT "\x1b[37m"
        std::cout << std::endl << vt100DEFAULT;


        llvm::LLVMContext ctx;
        llvm::Module mod(argv[1], ctx);
        llvm::IRBuilder builder(ctx);
        llvm::IRBuilder entryblockBuilder(ctx);

        // int main()
        llvm::FunctionType* mainfuncType = llvm::FunctionType::get(
            builder.getInt32Ty(), false);
        llvm::Function* mainfunc = llvm::Function::Create(
            mainfuncType, llvm::Function::ExternalLinkage, "main", &mod);

        // body
        llvm::BasicBlock* entryblock = llvm::BasicBlock::Create(ctx, "entry", mainfunc);
        entryblockBuilder.SetInsertPoint(entryblock);
        llvm::BasicBlock* mainfuncBody = llvm::BasicBlock::Create(ctx, "body", mainfunc);
        builder.SetInsertPoint(mainfuncBody);

        // Translate
        TranslationContext trctx;
        trctx.llvmcontext = &ctx;
        trctx.llvmmodule = &mod;
        trctx.builder = &builder;
        trctx.entryblockBuilder = &entryblockBuilder;
        trctx.func = mainfunc;
        SymbolTable symboltable;
        trctx.symboltable = &symboltable;
        ast->translate(&trctx);

        // finalize the BasicBlock "entry"
        entryblockBuilder.CreateBr(mainfuncBody);

        // return 0
        llvm::Value* v = builder.getInt32(0);
        builder.CreateRet(v);

        verifyFunction(*mainfunc);
        // write LLVM code to file
        std::error_code errorCode;
        llvm::raw_fd_ostream llfile(argv[2], errorCode);
        mod.print(llfile, nullptr);
    }

    std::cout << std::endl;
    //delete mainfuncType;
    //delete mainfunc;
    //delete mainfuncBody;
    //delete v;
    delete ast;
    return 0;
}
