#ifndef AST2IRP_HPP
#define AST2IRP_HPP

#include "../utils/dsa/ast.hpp"
#include "../utils/dsa/env.hpp"
#include "../utils/dsa/irp.hpp"
#include "../utils/dsa/table.hpp"
#include "../utils/dsa/temp.hpp"
#include <stack>

namespace irpgen {
class TrExp;
class TrExpList;
class ExpTy;
class LoopInfo;
class ConstInitValInfo;
class InitValInfo;

class LocalVarInfo {
public:
    temp::Temp *temp;
};

class FuncInfo {
public:
    irp::FuncDef *funcDef;
    temp::Label *label;
};

class AST2IRP {
public:
    AST2IRP(ast::Prog *root) : root(root) {}
    irp::Prog *result;
    void translate();

private:
    ast::Prog *root;

    table::SymbolTable<env::GlobalVar *> globalTable;
    table::SymbolTable<env::LocalVar *> localTable;
    table::SymbolTable<env::Func *> funcTable;

    std::stack<LoopInfo *> loopStack;

    std::string *curFuncName;
    int curFuncStackOffset;
    TrExp *curFuncAllocas;
    std::vector<irp::FormatDef *> *curFuncFormatsPtr;
    std::unordered_map<std::string, irp::FormatDef *> formatTable;

    TrExp *beforeMainDefs;
    TrExp *mainAllocas;

    irp::FuncDefList *transCompUnitList(ast::CompUnitList *compUnitList);
    irp::FuncDef *transFuncDef(ast::FuncDef *funcDef);
    std::pair<std::vector<env::TyKind> *, temp::TempList *> transFuncFParamList(ast::FuncFParamList *paramList);
    std::pair<env::TyKind, temp::Temp *> transFuncFParam(ast::FuncFParam *param);
    std::vector<int> *getArrayDimList(irp::ExpList *dimList);

    void transGlobalDecl(ast::Decl *decl);
    void transGlobalConstDecl(ast::ConstDecl *constDecl);
    void transGlobalConstDef(ast::ConstDef *constDef, ast::BaseType baseType);
    TrExp *transGlobalVarDecl(ast::VarDecl *varDecl);
    TrExp *transGlobalVarDef(ast::VarDef *varDef, ast::BaseType baseType);

    irp::GlobalDef *transGlobalArrConstInitVal(ast::InitVal *initVal, ast::BaseType baseType, temp::Label *label,
                                               std::vector<int> *dims);
    ConstInitValInfo *transConstInitValRecur(ast::InitVal *initVal, ast::BaseType baseType, std::vector<int> *dims,
                                             int curDim, int step, int align, int &offset);
    std::pair<irp::GlobalDef *, TrExp *> transGlobalVarInitVal(ast::InitVal *initVal, ast::BaseType baseType,
                                                               temp::Label *label);
    std::pair<irp::GlobalDef *, TrExp *> transGlobalArrInitVal(ast::InitVal *initVal, ast::BaseType baseType,
                                                               temp::Label *label, std::vector<int> *dims);
    InitValInfo *transGlobalInitValRecur(ast::InitVal *initVal, ast::BaseType baseType, temp::Label *label,
                                         std::vector<int> *dims, int curDim, int step, int align, int &offset);

    TrExp *transBlock(ast::Block *block);
    TrExp *transBlockItemList(ast::BlockItemList *blockItemList);
    TrExp *transBlockItem(ast::BlockItem *blockItem);
    TrExp *transDecl(ast::Decl *decl);
    void transConstDecl(ast::ConstDecl *constDecl);
    void transConstDef(ast::ConstDef *constDef, ast::BaseType baseType);
    irp::LocalConstArrayDef *transLocalArrConstInitVal(ast::InitVal *initVal, ast::BaseType baseType,
                                                       temp::Label *label, std::vector<int> *dims);
    TrExp *transVarDecl(ast::VarDecl *varDecl);
    TrExp *transVarDef(ast::VarDef *varDef, ast::BaseType baseType);
    TrExp *transInitVal(ast::InitVal *initVal, ast::BaseType baseType, temp::Temp *temp, std::vector<int> *dims);
    TrExp *transInitValRecur(ast::InitVal *initVal, ast::BaseType baseType, temp::Temp *temp, std::vector<int> *dims,
                             int curDim, int step, int align, int &offset);
    TrExp *transStmt(ast::Stmt *stmt);
    TrExp *transAssignStmt(ast::Stmt *stmt);
    TrExp *transExpStmt(ast::Stmt *stmt);
    TrExp *transBlockStmt(ast::Stmt *stmt);
    TrExp *transIfStmt(ast::Stmt *stmt);
    TrExp *transWhileStmt(ast::Stmt *stmt);
    TrExp *transBreakStmt(ast::Stmt *stmt);
    TrExp *transContinueStmt(ast::Stmt *stmt);
    TrExp *transReturnStmt(ast::Stmt *stmt);
    TrExp *transPutintStmt(ast::Stmt *stmt);
    TrExp *transPutchStmt(ast::Stmt *stmt);
    TrExp *transPutfloatStmt(ast::Stmt *stmt);
    TrExp *transPutarrayStmt(ast::Stmt *stmt);
    TrExp *transPutfarrayStmt(ast::Stmt *stmt);
    TrExp *transPutfStmt(ast::Stmt *stmt);
    TrExp *transStarttimeStmt();
    TrExp *transStoptimeStmt();

    ExpTy *transExp(ast::Exp *exp);
    ExpTy *transBinOpExp(ast::Exp *exp);
    ExpTy *transCondExp(ast::Exp *exp);
    ExpTy *transIdExp(ast::Exp *exp);
    ExpTy *transIntConstExp(ast::Exp *exp);
    ExpTy *transFloatConstExp(ast::Exp *exp);
    ExpTy *transNotExp(ast::Exp *exp);
    ExpTy *transMinusExp(ast::Exp *exp);
    ExpTy *transCallExp(ast::Exp *exp);
    ExpTy *transArrayExp(ast::Exp *exp);
    ExpTy *transGetintExp();
    ExpTy *transGetchExp();
    ExpTy *transGetfloatExp();
    ExpTy *transGetarrayExp(ast::Exp *exp);
    ExpTy *transGetfarrayExp(ast::Exp *exp);

    TrExpList *transExpList(ast::ExpList *expList);
    TrExpList *transArgs(ast::ExpList *args, std::vector<env::TyKind> *paramTypes);
};
} // namespace irpgen

#endif // AST2IRP_HPP