#include "CrossLang.hpp"
#include <iostream>
#include <array>
#include <map>
#include <cstring>
namespace Tesses::CrossLang
{
    void Write(Tesses::Framework::Streams::Stream* strm, uint8_t* buffer, size_t len)
    {
        strm->WriteBlock(buffer,len);
    }
    void WriteInt(Tesses::Framework::Streams::Stream* strm,uint32_t v)
    {
        uint8_t buffer[4];
        BitConverter::FromUint32BE(buffer[0],v);
        Write(strm,buffer,4);
    }
    void WriteString(Tesses::Framework::Streams::Stream* strm,std::string v)
    {
        WriteInt(strm,(uint32_t)v.size());
        Write(strm,(uint8_t*)v.data(),v.size());
    }
    
    
    CodeGen::~CodeGen()
    {
        for(auto& item : this->chunks) 
        {
            for(auto instr : item.second)
                delete instr;
        }
    }

    void CodeGen::Save(Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Streams::Stream* stream)
    {
        TVMVersion runtime_version(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE);
        uint8_t buffer[18];
        memcpy(buffer,"TCROSSVM",8);
        runtime_version.ToArray(buffer+8);
        version.ToArray(buffer+13);
        Write(stream,buffer,18);
        uint32_t sections=5;
        uint32_t name = GetString(this->name);
        uint32_t info = GetString(this->info);
        for(auto& dep : this->dependencies)
        {
            GetString(dep.first);
            sections++;
        }
        for(auto& res : this->res)
            sections++;
        
       
        WriteInt(stream,sections);
        uint32_t strSz=4;
        for(auto& s : this->strs)
        {
            strSz += (uint32_t)s.size() + 4;
        }
        memcpy(buffer,"STRS",4);
        Write(stream,buffer,4);
        WriteInt(stream,strSz); //even though its ignored
        WriteInt(stream,this->strs.size());
        for(auto& str : this->strs)
            WriteString(stream,str);
        memcpy(buffer,"NAME",4);
        Write(stream,buffer,4);
        WriteInt(stream,4);
        WriteInt(stream,name);
        memcpy(buffer,"INFO",4);
        Write(stream,buffer,4);
        WriteInt(stream,4);
        WriteInt(stream,info);

        for(auto& dep : this->dependencies)
        {
            memcpy(buffer,"DEPS",4);
            Write(stream,buffer,4);
            WriteInt(stream,9); //even though its ignored
            WriteInt(stream,GetString(dep.first));
            dep.second.ToArray(buffer);
            Write(stream,buffer,5);
        }

        uint32_t fnLen=4;

        for(auto& fn : this->funcs)        
        {
            fnLen += (fn.first.size() + 2) * 4;
        }

        memcpy(buffer,"FUNS",4);
        Write(stream,buffer,4);
        WriteInt(stream,fnLen);
        WriteInt(stream,(uint32_t)this->funcs.size());
        for(auto& fn : this->funcs)        
        {
            WriteInt(stream,(uint32_t)fn.first.size());
            for(auto namePart : fn.first)
            {
                WriteInt(stream,namePart);
            }
            WriteInt(stream,fn.second);
        }

        uint32_t clength = 4;
        for(auto& chunk : this->chunks)
        {
            clength += (2 + chunk.first.size())*4;
            size_t offset=0;
            std::map<std::string, int64_t> items;
       
            for(auto instr : chunk.second)
            {
                
                auto lbl = dynamic_cast<LabelInstruction*>(instr);
                if(lbl != nullptr)
                {
                    items[lbl->label] = offset;
                }

                offset += instr->Size();

            }
            
            clength += (uint32_t)offset;
            
            for(auto instr : chunk.second)
            {
                auto jmp = dynamic_cast<LabelableInstruction*>(instr);
                if(jmp != nullptr)
                {
                    jmp->n = items[jmp->label];
                }
            }
            
        }

        memcpy(buffer,"CHKS",4);
        Write(stream,buffer,4);
        WriteInt(stream,clength);
        WriteInt(stream,(uint32_t)this->chunks.size());
        for(auto& chunk : this->chunks)
        {
            std::vector<uint8_t> buffer;
            WriteInt(stream,(uint32_t)chunk.first.size());
            for(auto arg : chunk.first)
            {
                WriteInt(stream,arg);
            }
            for(auto instr : chunk.second)
            {
                instr->Write(buffer);
            }
            WriteInt(stream,(uint32_t)buffer.size());
            Write(stream,buffer.data(),buffer.size());
        }
    
        for(auto& reso : res)
        {
            memcpy(buffer,"RESO",4);
            Write(stream,buffer,4);
           
            if(vfs == nullptr)
            {
                WriteInt(stream,0);
                continue;
            }
            auto f = vfs->OpenFile(reso,"rb");
            if(f != NULL)
            {
                
                uint32_t len = (uint32_t)f->GetLength();
                
                WriteInt(stream,len);
                uint8_t buff[1024];
                size_t read;
                do {
                    read = f->Read(buff,1024);
                    Write(stream,buff,read);
                } while(read > 0);
                delete f;
            }
            else
            {
                WriteInt(stream,0);
            }
        }
    }


    size_t SimpleInstruction::Size()
    {
        return 1;
    }
    SimpleInstruction::SimpleInstruction(Instruction instr)
    {
        this->instruction = instr;
    }
    void SimpleInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(instruction);
    }


    ScopeEndTimesInstruction::ScopeEndTimesInstruction(uint32_t s)
    {
        this->n = s;
    }
    size_t ScopeEndTimesInstruction::Size()
    {
        return 5;
    }
    void ScopeEndTimesInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(SCOPEENDTIMES);
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }

    LabelInstruction::LabelInstruction(std::string lbl)
    {
        this->label = lbl;
    }

    size_t LabelInstruction::Size()
    {
        return 0;
    }

    void LabelInstruction::Write(std::vector<uint8_t>& instr)
    {
        //label instructions do not write a thing
    }

    JumpStyleInstruction::JumpStyleInstruction(Instruction instr, std::string lbl)
    {
        this->type = instr;
        this->label = lbl;
        this->n = 0;
    }
    
    size_t JumpStyleInstruction::Size()
    {
        return 5;
    }
    void JumpStyleInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back((uint8_t)type);
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }

    size_t LabelableInstruction::Size()
    {
        return 4;
    }
    void LabelableInstruction::Write(std::vector<uint8_t>& instr)
    {
        
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }
    StringInstruction::StringInstruction(uint32_t s)
    {
        this->n = s;
    }
    size_t StringInstruction::Size()
    {
        return 5;
    }
    void StringInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(PUSHSTRING);
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }
    EmbedInstruction::EmbedInstruction(uint32_t s)
    {
        this->n = s;
    }
    size_t EmbedInstruction::Size()
    {
        return 5;
    }
    void EmbedInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(PUSHRESOURCE);
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }
    ClosureInstruction::ClosureInstruction(uint32_t s,bool hasScope)
    {
        this->n = s;
        this->hasScope = hasScope;
    }
    size_t ClosureInstruction::Size()
    {
        return 5;
    }
    void ClosureInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(this->hasScope ? PUSHCLOSURE : PUSHSCOPELESSCLOSURE);
        std::array<uint8_t,4> buff;
        BitConverter::FromUint32BE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }
    
    DoubleInstruction::DoubleInstruction(double s)
    {
        this->n = s;
    }
    size_t DoubleInstruction::Size()
    {
        return 9;
    }
    void DoubleInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(PUSHDOUBLE);
        std::array<uint8_t,8> buff;
        BitConverter::FromDoubleBE(buff[0],this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }

     CharInstruction::CharInstruction(char s)
    {
        this->n = s;
    }
    size_t CharInstruction::Size()
    {
        return 2;
    }
    void CharInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(PUSHCHAR);
        uint8_t buff[8];
        instr.push_back((uint8_t)this->n);
    }
     LongInstruction::LongInstruction(int64_t s)
    {
        this->n = s;
    }
    size_t LongInstruction::Size()
    {
        return 9;
    }
    void LongInstruction::Write(std::vector<uint8_t>& instr)
    {
        instr.push_back(PUSHLONG);
        std::array<uint8_t,8> buff;
        BitConverter::FromUint64BE(buff[0],(uint64_t)this->n);
        instr.insert(instr.end(),buff.begin(),buff.end());
    }
    uint32_t CodeGen::GetString(std::string str)
    {
        for(uint32_t i = 0; i < (uint32_t)this->strs.size();i++)
        {
            if(this->strs[i] == str) return i;
        }
        uint32_t strI = (uint32_t)this->strs.size();
        this->strs.push_back(str);
        return strI;
    }
    uint32_t CodeGen::GetResource(std::string res)
    {
        for(uint32_t i = 0; i < (uint32_t)this->res.size();i++)
        {
            if(this->res[i] == res) return i;
        }
        uint32_t resI = (uint32_t)this->res.size();
        this->res.push_back(res);
        return resI;
    }
    #define ONE_EXPR(EXPRESSION, INSTRUCTION) if(adv.nodeName == EXPRESSION && adv.nodes.size() == 1) {GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);instructions.push_back(new SimpleInstruction(INSTRUCTION));}
    #define TWO_EXPR(EXPRESSION, INSTRUCTION) if(adv.nodeName == EXPRESSION && adv.nodes.size() == 2) {GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);instructions.push_back(new SimpleInstruction(INSTRUCTION));}
    void CodeGen::GenNode(std::vector<ByteCodeInstruction*>& instructions, SyntaxNode n,int32_t scope,int32_t contscope, int32_t brkscope, int32_t contI, int32_t brkI)
    {
        if(std::holds_alternative<std::nullptr_t>(n))
        {
            instructions.push_back(new SimpleInstruction(PUSHNULL));
        }
        else if(std::holds_alternative<Undefined>(n))
        {
            instructions.push_back(new SimpleInstruction(PUSHUNDEFINED));
        }
        else if(std::holds_alternative<bool>(n))
        {
            instructions.push_back(new SimpleInstruction(std::get<bool>(n) ? PUSHTRUE : PUSHFALSE));
        }
        else if(std::holds_alternative<std::string>(n))
        {
            instructions.push_back(new StringInstruction(GetString(std::get<std::string>(n))));
        }
        else if(std::holds_alternative<char>(n))
        {
            instructions.push_back(new CharInstruction(std::get<char>(n)));
        }
        else if(std::holds_alternative<int64_t>(n))
        {
            instructions.push_back(new LongInstruction(std::get<int64_t>(n)));
        }
        else if(std::holds_alternative<double>(n))
        {
            instructions.push_back(new DoubleInstruction(std::get<double>(n)));
        }
        else if(std::holds_alternative<AdvancedSyntaxNode>(n))
        {
            auto adv = std::get<AdvancedSyntaxNode>(n);
            ONE_EXPR(NotExpression, NOT)
            ONE_EXPR(BitwiseNotExpression, BITWISENOT)
            ONE_EXPR(NegativeExpression,NEGATIVE)
            TWO_EXPR(AddExpression, ADD)
            TWO_EXPR(SubExpression, SUB)
            TWO_EXPR(TimesExpression, TIMES)
            TWO_EXPR(DivideExpression, DIVIDE)
            TWO_EXPR(ModExpression, MODULO)
            TWO_EXPR(LeftShiftExpression, LEFTSHIFT)
            TWO_EXPR(RightShiftExpression, RIGHTSHIFT)
            TWO_EXPR(BitwiseOrExpression, BITWISEOR)
            TWO_EXPR(BitwiseAndExpression, BITWISEAND)
            TWO_EXPR(LessThanExpression, LESSTHAN)
            TWO_EXPR(GreaterThanExpression, GREATERTHAN)
            TWO_EXPR(LessThanEqualsExpression, LESSTHANEQ)
            TWO_EXPR(GreaterThanEqualsExpression, GREATERTHANEQ)
            TWO_EXPR(NotEqualsExpression, NEQ)
            TWO_EXPR(EqualsExpression, EQ)
            TWO_EXPR(XOrExpression, XOR)
            if(adv.nodeName == TernaryExpression && adv.nodes.size() == 3)
            {
                 uint32_t ifId = NewId();
                std::string ifIdTrue = "__compGenTrue";
                ifIdTrue.append(std::to_string(ifId));
                std::string ifIdEnd = "__compGenEnd";
                ifIdEnd.append(std::to_string(ifId));

                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new JumpStyleInstruction(JMPC,ifIdTrue));
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[2]))
                {
                    GenNode(instructions,adv.nodes[2],scope,contscope,brkscope,contI,brkI);
                   
                }
                instructions.push_back(new JumpStyleInstruction(JMP,ifIdEnd));
                instructions.push_back(new LabelInstruction(ifIdTrue));    
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[1]))
                {
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                   
                }
                instructions.push_back(new LabelInstruction(ifIdEnd));

            }
            else if(adv.nodeName == CompoundAssignExpression && adv.nodes.size() == 1 && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[0]))
            {
                auto data = std::get<AdvancedSyntaxNode>(adv.nodes[0]);
                if(data.nodes.size() == 2)
                {
                    auto d = AdvancedSyntaxNode::Create(AssignExpression,true,{
                        data.nodes[0],
                        data
                    });
                    GenNode(instructions,d,scope,contscope,brkscope,contI,brkI);
                }
            }
            else if(adv.nodeName == PrefixIncrementExpression && adv.nodes.size() == 1)
            {  
                GenNode(instructions,
                AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{
                    AdvancedSyntaxNode::Create(AddExpression,true,{
                        adv.nodes[0]
                        ,
                        (int64_t)1
                    })
                })
                ,scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == PrefixDecrementExpression && adv.nodes.size() == 1)
            {    
                GenNode(instructions,
                AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{
                    AdvancedSyntaxNode::Create(SubExpression,true,{
                        adv.nodes[0]
                        ,
                        (int64_t)1
                    })
                })
                ,scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == PostfixIncrementExpression && adv.nodes.size() == 1)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                GenNode(instructions,
                AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{
                    AdvancedSyntaxNode::Create(AddExpression,true,{
                        adv.nodes[0]
                        ,
                        (int64_t)1
                    })
                })
                ,scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(POP));
            }
            else if(adv.nodeName == PostfixDecrementExpression && adv.nodes.size() == 1)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                GenNode(instructions,
                AdvancedSyntaxNode::Create(CompoundAssignExpression,true,{
                    AdvancedSyntaxNode::Create(SubExpression,true,{
                        adv.nodes[0]
                        ,
                        (int64_t)1
                    })
                })
                ,scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(POP));
            }
            else if(adv.nodeName == LogicalOrExpression && adv.nodes.size() == 2)
            {
                auto data= AdvancedSyntaxNode::Create(TernaryExpression,true,{adv.nodes[0],true, AdvancedSyntaxNode::Create(TernaryExpression,true,{adv.nodes[1],true,false}) });
                GenNode(instructions,data,scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == LogicalAndExpression && adv.nodes.size() == 2)
            {
                auto data= AdvancedSyntaxNode::Create(TernaryExpression,true,{adv.nodes[0], AdvancedSyntaxNode::Create(TernaryExpression,true,{adv.nodes[1],true,false}),false});
                GenNode(instructions,data,scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == EachStatement && adv.nodes.size() == 3)
            {
                auto item = adv.nodes[0];
                auto list = adv.nodes[1];
                auto body = adv.nodes[2];

                uint32_t compGenId = NewId();
                std::string compGenIttr = "__compGenIttr";
                compGenIttr.append(std::to_string(compGenId));

                auto each = AdvancedSyntaxNode::Create(NodeList,false,{
                    AdvancedSyntaxNode::Create(AssignExpression,true, {
                        AdvancedSyntaxNode::Create(DeclareExpression,true,{compGenIttr}),
                        AdvancedSyntaxNode::Create(FunctionCallExpression,true,{AdvancedSyntaxNode::Create(GetFieldExpression,true,{list,"GetEnumerator"})})
                    }),
                    AdvancedSyntaxNode::Create(DeferStatement,false,{AdvancedSyntaxNode::Create(FunctionCallExpression,true,{AdvancedSyntaxNode::Create(GetFieldExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{compGenIttr}), "Dispose"})})}),
                    AdvancedSyntaxNode::Create(WhileStatement,false,{
                        AdvancedSyntaxNode::Create(FunctionCallExpression,true,{AdvancedSyntaxNode::Create(GetFieldExpression,true,{AdvancedSyntaxNode::Create(GetVariableExpression,true,{compGenIttr}),"MoveNext" }),}),
                        AdvancedSyntaxNode::Create(NodeList,false,{
                            AdvancedSyntaxNode::Create(AssignExpression,true,{
                                item,
                                AdvancedSyntaxNode::Create(GetFieldExpression,true,{
                                    AdvancedSyntaxNode::Create(GetVariableExpression,true,{
                                        compGenIttr
                                    }),
                                    "Current"
                                })
                            }),
                            body
                        })
                    })
                });
                GenNode(instructions,each,scope,contscope,brkscope,contI,brkI);

            }
            else if(adv.nodeName == ArrayExpression)
            {
                instructions.push_back(new SimpleInstruction(CREATEARRAY));
                std::vector<SyntaxNode> itms;
                 if(adv.nodes.size() > 0)
                
                GetFunctionArgs(itms,adv.nodes[0]);
                for(auto item : itms)
                {
                    GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new SimpleInstruction(APPENDLIST));
                }
            }
            else if(adv.nodeName == DictionaryExpression)
            {
                instructions.push_back(new SimpleInstruction(CREATEDICTIONARY));
                std::vector<SyntaxNode> itms;
                if(adv.nodes.size() > 0)
                GetFunctionArgs(itms,adv.nodes[0]);
                for(auto item : itms)
                {
                    if(std::holds_alternative<AdvancedSyntaxNode>(item))
                    {
                        auto tkn = std::get<AdvancedSyntaxNode>(item);
                        if(tkn.nodeName == GetVariableExpression && !tkn.nodes.empty() && std::holds_alternative<std::string>(tkn.nodes[0]))
                        {
                            instructions.push_back(new StringInstruction(GetString(std::get<std::string>(tkn.nodes[0]))));
                            GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                            instructions.push_back(new SimpleInstruction(APPENDDICT));
                        }
                        else if(tkn.nodeName == AssignExpression && tkn.nodes.size()==2 && std::holds_alternative<AdvancedSyntaxNode>(tkn.nodes[0]))
                        {
                            auto myTn =  std::get<AdvancedSyntaxNode>(tkn.nodes[0]);
                            if(myTn.nodeName == GetVariableExpression && !myTn.nodes.empty() && std::holds_alternative<std::string>(myTn.nodes[0]))
                            {
                                instructions.push_back(new StringInstruction(GetString(std::get<std::string>(myTn.nodes[0]))));
                                GenNode(instructions,tkn.nodes[1],scope,contscope,brkscope,contI,brkI);
                                instructions.push_back(new SimpleInstruction(APPENDDICT));
                            }
                        }
                    }
                }
            }
            else if(adv.nodeName == FunctionCallExpression && adv.nodes.size() >= 1 && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[0]))
            {
                auto v = std::get<AdvancedSyntaxNode>(adv.nodes[0]);
                if(v.nodeName == GetFieldExpression && v.nodes.size() == 2)
                {
                    GenNode(instructions,v.nodes[0],scope,contscope,brkscope,contI,brkI);
                    GenNode(instructions,v.nodes[1],scope,contscope,brkscope,contI,brkI);
                    if(adv.nodes.size()==2)
                    {
                        std::vector<SyntaxNode> nodes;
                        GetFunctionArgs(nodes,adv.nodes[1]);
                        for(auto item : nodes)
                        GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                        instructions.push_back(new LongInstruction((int64_t)nodes.size()));
                    }
                    else {
                        instructions.push_back(new LongInstruction(0));
                    }
                    instructions.push_back(new SimpleInstruction(CALLMETHOD));
                }
                else if(v.nodeName != GetFieldExpression)
                {
                    GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                    if(adv.nodes.size()==2)
                    {
                        std::vector<SyntaxNode> nodes;
                        GetFunctionArgs(nodes,adv.nodes[1]);
                        for(auto item : nodes)
                        GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                        instructions.push_back(new LongInstruction((int64_t)nodes.size()));
                    }
                    else {
                        instructions.push_back(new LongInstruction(0));
                    }
                    instructions.push_back(new SimpleInstruction(CALLFUNCTION));
                }
            }
            else if(adv.nodeName == IfStatement && adv.nodes.size() == 3)
            {
                uint32_t ifId = NewId();
                std::string ifIdTrue = "__compGenTrue";
                ifIdTrue.append(std::to_string(ifId));
                std::string ifIdEnd = "__compGenEnd";
                ifIdEnd.append(std::to_string(ifId));

                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new JumpStyleInstruction(JMPC,ifIdTrue));
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[2]))
                {
                    GenNode(instructions,adv.nodes[2],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[2]);
                }
                instructions.push_back(new JumpStyleInstruction(JMP,ifIdEnd));
                instructions.push_back(new LabelInstruction(ifIdTrue));    
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[1]))
                {
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[1]);
                }
                instructions.push_back(new LabelInstruction(ifIdEnd));

            }
            else if(adv.nodeName == WhileStatement && adv.nodes.size() == 2)
            {
                auto old_contI = contI;
                auto old_brkI = brkI;
                auto old_contscope = contscope;
                auto old_brkscope = brkscope;

                contscope = scope;
                brkscope = scope;

                uint32_t whileId = NewId();
                std::string whileIdCont = "__compGenCont";
                whileIdCont.append(std::to_string(whileId));
                std::string whileIdBrk = "__compGenBrk";
                whileIdBrk.append(std::to_string(whileId));
                
                contI = whileId;
                brkI = whileId;

                instructions.push_back(new LabelInstruction(whileIdCont));
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(NOT));
                instructions.push_back(new JumpStyleInstruction(JMPC,whileIdBrk));
                
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[1]))
                {
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[1]);
                }

                instructions.push_back(new JumpStyleInstruction(JMP,whileIdCont));
                instructions.push_back(new LabelInstruction(whileIdBrk));

                contI = old_contI;
                brkI = old_brkI;
                contscope = old_contscope;
                brkscope = old_brkscope;

            }
            else if(adv.nodeName == DoStatement && adv.nodes.size() == 2)
            {
                auto old_contI = contI;
                auto old_brkI = brkI;
                auto old_contscope = contscope;
                auto old_brkscope = brkscope;

                contscope = scope;
                brkscope = scope;

                uint32_t doId = NewId();
                std::string doIdCont = "__compGenCont";
                doIdCont.append(std::to_string(doId));
                std::string doIdBrk = "__compGenBrk";
                doIdBrk.append(std::to_string(doId));
                std::string doIdStart = "__compGenStart";
                doIdStart.append(std::to_string(doId));
                
                contI = doId;
                brkI = doId;

                
                instructions.push_back(new LabelInstruction(doIdStart));
                
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[1]))
                {
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[1]);
                }
                instructions.push_back(new LabelInstruction(doIdCont));
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);

                instructions.push_back(new JumpStyleInstruction(JMPC,doIdStart));
                instructions.push_back(new LabelInstruction(doIdBrk));

                contI = old_contI;
                brkI = old_brkI;
                contscope = old_contscope;
                brkscope = old_brkscope;

            }
            else if(adv.nodeName == ForStatement && adv.nodes.size() == 4)
            {
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[0]))
                {
                    GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[0]);
                }
                auto old_contI = contI;
                auto old_brkI = brkI;
                auto old_contscope = contscope;
                auto old_brkscope = brkscope;

                contscope = scope;
                brkscope = scope;

                uint32_t doId = NewId();
                std::string doIdCont = "__compGenCont";
                doIdCont.append(std::to_string(doId));
                std::string doIdBrk = "__compGenBrk";
                doIdBrk.append(std::to_string(doId));
                std::string doIdStart = "__compGenStart";
                doIdStart.append(std::to_string(doId));
                std::string doIdStart2 = "__compGenCond";
                doIdStart2.append(std::to_string(doId));
                
                contI = doId;
                brkI = doId;

                instructions.push_back(new JumpStyleInstruction(JMP,doIdStart2));
                instructions.push_back(new LabelInstruction(doIdStart));
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[3]))
                {
                    GenNode(instructions,adv.nodes[3],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[3]);
                }

                instructions.push_back(new LabelInstruction(doIdCont));
                if(!std::holds_alternative<std::nullptr_t>(adv.nodes[2]))
                {
                    GenNode(instructions,adv.nodes[2],scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,adv.nodes[2]);
                }
                instructions.push_back(new LabelInstruction(doIdStart2));
                GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new JumpStyleInstruction(JMPC,doIdStart));
                instructions.push_back(new LabelInstruction(doIdBrk));
                 contI = old_contI;
                brkI = old_brkI;
                contscope = old_contscope;
                brkscope = old_brkscope; 
            }
            else if(adv.nodeName == CommaExpression && adv.nodeName.size() == 2)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                GenPop(instructions,adv.nodes[0]);
                GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                
            }
            else if(adv.nodeName == ContinueStatement)
            {
                if( contscope == -1)
                {
                    std::cout << "WARN: continue does nothing here\n";
                }
                else
                {
                    auto cont = scope-contscope;
                    if(cont > 0)
                    instructions.push_back(new ScopeEndTimesInstruction((uint32_t)cont));
                    
                    std::string myJmp = "__compGenCont";
                    myJmp.append(std::to_string(contI));

                    instructions.push_back(new JumpStyleInstruction(JMP,myJmp));
                }
            }
            else if(adv.nodeName == BreakStatement)
            {
                if( brkscope == -1)
                {
                    std::cout << "WARN: break does nothing here\n";
                }
                else
                {
                    auto _brk = scope-brkscope;
                    if(_brk > 0)
                    instructions.push_back(new ScopeEndTimesInstruction((uint32_t)_brk));
                    
                    std::string myJmp = "__compGenBrk";
                    myJmp.append(std::to_string(brkI));

                    instructions.push_back(new JumpStyleInstruction(JMP,myJmp));
                }
            }
            else if(adv.nodeName == GetVariableExpression && adv.nodes.size() == 1 && std::holds_alternative<std::string>(adv.nodes[0]))
            {
                instructions.push_back(new StringInstruction(GetString(std::get<std::string>(adv.nodes[0]))));
                instructions.push_back(new SimpleInstruction(GETVARIABLE));
            }
            else if(adv.nodeName == GetFieldExpression && adv.nodes.size() == 2 && std::holds_alternative<std::string>(adv.nodes[1]))
            {
                
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new StringInstruction(GetString(std::get<std::string>(adv.nodes[1]))));
                
                instructions.push_back(new SimpleInstruction(GETFIELD));
             
            }
            else if(adv.nodeName == GetArrayExpression && adv.nodes.size() == 2)
            {
                SyntaxNode n = AdvancedSyntaxNode::Create(FunctionCallExpression,true,{
                    AdvancedSyntaxNode::Create(GetFieldExpression,true,{adv.nodes[0],"GetAt"}),
                    adv.nodes[1]
                });
                GenNode(instructions,n,scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == AssignExpression && adv.nodes.size() == 2 && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[0]))
            {
                auto varNode = std::get<AdvancedSyntaxNode>(adv.nodes[0]);
                if(varNode.nodeName == GetVariableExpression && varNode.nodes.size() == 1 && std::holds_alternative<std::string>(varNode.nodes[0]))
                {
                    instructions.push_back(new StringInstruction(GetString(std::get<std::string>(varNode.nodes[0]))));
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new SimpleInstruction(SETVARIABLE));
                }
                else if(varNode.nodeName == DeclareExpression && varNode.nodes.size() == 1 && std::holds_alternative<std::string>(varNode.nodes[0]))
                {
                    instructions.push_back(new StringInstruction(GetString(std::get<std::string>(varNode.nodes[0]))));
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new SimpleInstruction(DECLAREVARIABLE));
                }
                else if(varNode.nodeName == GetFieldExpression && varNode.nodes.size() == 2 && std::holds_alternative<std::string>(varNode.nodes[1]))
                {
                    GenNode(instructions,varNode.nodes[0],scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new StringInstruction(GetString(std::get<std::string>(varNode.nodes[1]))));
                    GenNode(instructions,adv.nodes[1],scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new SimpleInstruction(SETFIELD));
                }
                else if(varNode.nodeName == GetArrayExpression && varNode.nodes.size() == 2)
                {
                    SyntaxNode n = AdvancedSyntaxNode::Create(FunctionCallExpression,true,{
                        AdvancedSyntaxNode::Create(GetFieldExpression,true,{varNode.nodes[0],"SetAt"}),
                       AdvancedSyntaxNode::Create(CommaExpression,true,
                       {
                        varNode.nodes[1],
                        adv.nodes[1]
                       })
                    });
                    GenNode(instructions,n,scope,contscope,brkscope,contI,brkI);
                }
            }
            else if(adv.nodeName == ThrowStatement && adv.nodes.size() == 1)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(THROW));
            }
            else if(adv.nodeName == ReturnStatement && adv.nodes.size() == 1)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(RET));
            }
            else if(adv.nodeName == ParenthesesExpression && adv.nodes.size() == 1)
            {
                GenNode(instructions,adv.nodes[0],scope,contscope,brkscope,contI,brkI);
            }
            else if(adv.nodeName == EmbedExpression && adv.nodes.size() == 1 && std::holds_alternative<std::string>(adv.nodes[0]))
            {
                std::string filename = std::get<std::string>(adv.nodes[0]);
                instructions.push_back(new EmbedInstruction(GetResource(filename)));

            }
            else if(adv.nodeName == ScopeNode)
            {
                scope++;
                instructions.push_back(new SimpleInstruction(SCOPEBEGIN));
                for(auto item : adv.nodes)
                {
                    GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,item);
                }
                instructions.push_back(new SimpleInstruction(SCOPEEND));
                scope--;
            }
             else if(adv.nodeName == NodeList)
            {
                
                for(auto item : adv.nodes)
                {
                    GenNode(instructions,item,scope,contscope,brkscope,contI,brkI);
                    GenPop(instructions,item);
                }
            }
            else if(adv.nodeName == DeferStatement && adv.nodes.size() == 1)
            {
                GenNode(instructions,AdvancedSyntaxNode::Create(ScopelessClosureExpression,true,{AdvancedSyntaxNode::Create(ParenthesesExpression,true,{}),adv.nodes[0]}),scope,contscope,brkscope,contI,brkI);
                instructions.push_back(new SimpleInstruction(DEFER));
            }
            else if(adv.nodeName == TryStatement && adv.nodes.size() == 4)
            {
                //AdvancedSyntaxNode::Create(TryStatement, false, {tryBody,catchNode,catchEx, finally});
                if(std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[3]))
                {
                    GenNode(instructions,AdvancedSyntaxNode::Create(DeferStatement,false, {adv.nodes[3]}),scope,contscope,brkscope,contI,brkI);
                }
                if(std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[0]) && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[1]) && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[2]))
                {
                    GenNode(instructions,AdvancedSyntaxNode::Create(ScopelessClosureExpression,true,{AdvancedSyntaxNode::Create(ParenthesesExpression,true,{}),adv.nodes[0]}),scope,contscope,brkscope,contI,brkI);
                    GenNode(instructions,AdvancedSyntaxNode::Create(ScopelessClosureExpression,true,{
                       AdvancedSyntaxNode::Create(ParenthesesExpression,true,{adv.nodes[2]}),
                       adv.nodes[1]
                    }),scope,contscope,brkscope,contI,brkI);
                    instructions.push_back(new SimpleInstruction(TRYCATCH));
                    uint32_t compGenId = NewId();
                    std::string compGenIttr = "__compGenRetThing";
                    compGenIttr.append(std::to_string(compGenId));
                    instructions.push_back(new JumpStyleInstruction(JMPUNDEFINED, compGenIttr));
                    instructions.push_back(new SimpleInstruction(RET));
                    instructions.push_back(new LabelInstruction(compGenIttr));
                }
                
            }
            else if(adv.nodeName == ScopelessClosureExpression && adv.nodes.size() == 2)
            {
                //()=>{}
                //Name => {}
                //(a,b) => {}
                //it has two args
                std::vector<uint32_t> args;
                GetFunctionArgs(args, adv.nodes[0]);

                std::vector<ByteCodeInstruction*> fnInstructions;
                size_t fnindex=this->chunks.size();
                this->chunks.resize(fnindex+1);

                auto body = adv.nodes[1];

                if(std::holds_alternative<AdvancedSyntaxNode>(body))
                {
                    auto res = std::get<AdvancedSyntaxNode>(body);
                    if(res.nodeName == ScopeNode)
                    {
                        res.nodeName = NodeList;
                    }
                    body = res;
                }
                

                GenNode(fnInstructions,body,0,-1,-1,-1,-1);

                this->chunks[fnindex] = std::pair<std::vector<uint32_t>,std::vector<ByteCodeInstruction*>>(args, fnInstructions);
                instructions.push_back(new ClosureInstruction((uint32_t)fnindex,false));
            }
            else if(adv.nodeName == ClosureExpression && adv.nodes.size() == 2)
            {
                //()=>{}
                //Name => {}
                //(a,b) => {}
                //it has two args
                std::vector<uint32_t> args;
                GetFunctionArgs(args, adv.nodes[0]);

                std::vector<ByteCodeInstruction*> fnInstructions;
                size_t fnindex=this->chunks.size();
                this->chunks.resize(fnindex+1);

                auto body = adv.nodes[1];

                if(std::holds_alternative<AdvancedSyntaxNode>(body))
                {
                    auto res = std::get<AdvancedSyntaxNode>(body);
                    if(res.nodeName == ScopeNode)
                    {
                        res.nodeName = NodeList;
                    }
                    body = res;
                }
                

                GenNode(fnInstructions,body,0,-1,-1,-1,-1);

                this->chunks[fnindex] = std::pair<std::vector<uint32_t>,std::vector<ByteCodeInstruction*>>(args, fnInstructions);
                instructions.push_back(new ClosureInstruction((uint32_t)fnindex));
            }
            else if(adv.nodeName == FunctionStatement && adv.nodes.size() == 2 && std::holds_alternative<AdvancedSyntaxNode>(adv.nodes[0])) 
            {
                //func NAME(ARGS) {}
                //we need to disect NAME(ARGS) and {}
                //then disect NAME and (ARGS)
                auto fcall = std::get<AdvancedSyntaxNode>(adv.nodes[0]);
                if(fcall.nodeName == FunctionCallExpression && adv.nodes.size() >= 1 && std::holds_alternative<AdvancedSyntaxNode>(fcall.nodes[0]))
                {
                    SyntaxNode args = AdvancedSyntaxNode::Create(ParenthesesExpression,true,{});
                    if(fcall.nodes.size() == 2)
                    {
                        args = AdvancedSyntaxNode::Create(ParenthesesExpression,true,{fcall.nodes[1]});
                    }
                    SyntaxNode closure = AdvancedSyntaxNode::Create(ClosureExpression,true,{args,adv.nodes[1]});
                    
                    SyntaxNode assign = AdvancedSyntaxNode::Create(AssignExpression,false,{
                        fcall.nodes[0],
                        closure
                    });
                    GenNode(instructions,assign,scope,contscope,brkscope,contI,brkI);

                    instructions.push_back(new SimpleInstruction(POP));
                }
            }
        
        }
    }
    void CodeGen::GetFunctionArgs(std::vector<SyntaxNode>& args, SyntaxNode n)
    {
        AdvancedSyntaxNode sn;
        if(std::holds_alternative<AdvancedSyntaxNode>(n) && (sn=std::get<AdvancedSyntaxNode>(n)).nodeName == CommaExpression && sn.nodes.size()==2)
        {
            GetFunctionArgs(args,sn.nodes[0]);
            GetFunctionArgs(args,sn.nodes[1]);
        }
        else
        {
            args.push_back(n);
        }
    }
    void CodeGen::GetFunctionArgs(std::vector<uint32_t>& name, SyntaxNode n)
    {
        if(std::holds_alternative<std::nullptr_t>(n))
            return;


        if(std::holds_alternative<AdvancedSyntaxNode>(n))
        {
            auto res = std::get<AdvancedSyntaxNode>(n);
            if(res.nodeName == ParenthesesExpression)
            {
                for(auto n : res.nodes)
                    GetFunctionArgs(name,n);
            }
            else if(res.nodeName == CommaExpression && res.nodes.size() == 2)
            {
                GetFunctionArgs(name,res.nodes[0]);
                GetFunctionArgs(name,res.nodes[1]);
            }
            else if(res.nodeName == GetVariableExpression && res.nodes.size() == 1)
            {
                if(std::holds_alternative<std::string>(res.nodes[0]))
                {
                    name.push_back(GetString(std::get<std::string>(res.nodes[0])));
                }
            }
        }
    }

    void CodeGen::GetFunctionName(std::vector<uint32_t>& name,SyntaxNode n)
    {
        if(std::holds_alternative<AdvancedSyntaxNode>(n))
        {
            auto res = std::get<AdvancedSyntaxNode>(n);
            if(res.nodeName == GetFieldExpression && res.nodes.size() == 2)
            {
                GetFunctionName(name, res.nodes[0]);
                if(std::holds_alternative<std::string>(res.nodes[1]))
                {
                    name.push_back(GetString(std::get<std::string>(res.nodes[1])));
                }
            }
            else if(res.nodeName == GetVariableExpression && res.nodes.size() == 1)
            {
                if(std::holds_alternative<std::string>(res.nodes[0]))
                {
                    name.push_back(GetString(std::get<std::string>(res.nodes[0])));
                }
            }
        }
    }

    void CodeGen::GenRoot(SyntaxNode n)
    {
        this->id = 0;
        std::vector<ByteCodeInstruction*> rootInstructions;
        size_t index=this->chunks.size();
        this->chunks.resize(index+1);

        if(std::holds_alternative<AdvancedSyntaxNode>(n))
        {
            auto res = std::get<AdvancedSyntaxNode>(n);

            if(res.nodeName == NodeList)
            {
                for(auto item : res.nodes)
                {
                    if(std::holds_alternative<AdvancedSyntaxNode>(item))
                    {
                        std::string documentation = "";
                        auto res2 = std::get<AdvancedSyntaxNode>(item);

                        if(res2.nodeName == DocumentationStatement)
                        {
                            if(res2.nodes.size() == 2)
                            {
                                if(std::holds_alternative<std::string>(res2.nodes[0]) && std::holds_alternative<AdvancedSyntaxNode>(res2.nodes[1]))
                                {
                                    documentation = std::get<std::string>(res2.nodes[0]);
                                    auto j = std::get<AdvancedSyntaxNode>(res2.nodes[1]);
                                    if(j.nodeName == FunctionStatement) {
                                        res2 = j;
                                    }
                                }
                            }
                        }

                        if(res2.nodeName == FunctionStatement)
                        {
                            if(res2.nodes.size()==2)
                            {
                                auto fcall = res2.nodes[0];
                                if(std::holds_alternative<AdvancedSyntaxNode>(fcall))
                                {
                                    auto fcalli = std::get<AdvancedSyntaxNode>(fcall);
                                    if(fcalli.nodeName == FunctionCallExpression)
                                    {
                                        if(fcalli.nodes.size() >= 1)
                                        {
                                            std::vector<uint32_t> functionName;
                                            std::vector<uint32_t> args;
                                            GetFunctionName(functionName,fcalli.nodes[0]);
                                            functionName.insert(functionName.begin(),{GetString(documentation)});
                                            if(fcalli.nodes.size()==2)
                                            GetFunctionArgs(args, fcalli.nodes[1]);

                                            std::vector<ByteCodeInstruction*> fnInstructions;
                                            size_t fnindex=this->chunks.size();
                                            this->chunks.resize(fnindex+1);

                                            GenNode(fnInstructions,res2.nodes[1],0,-1,-1,-1,-1);

                                            this->funcs.push_back(std::pair<std::vector<uint32_t>,uint32_t>(functionName,(uint32_t)fnindex));

                                            this->chunks[fnindex] = std::pair<std::vector<uint32_t>,std::vector<ByteCodeInstruction*>>(args, fnInstructions);
                                        }
                                        else
                                        {
                                            GenNode(rootInstructions,item,0,-1,-1,-1,-1);
                                            GenPop(rootInstructions,item);
                                        }
                                    }
                                    else
                                    {
                                        GenNode(rootInstructions,item,0,-1,-1,-1,-1);
                                        GenPop(rootInstructions,item);
                                    }
                                }
                            }
                            else
                            {
                                GenNode(rootInstructions,item,0,-1,-1,-1,-1);
                                GenPop(rootInstructions,item);
                            }
                        }
                        else
                        {
                            GenNode(rootInstructions,item,0,-1,-1,-1,-1);
                            GenPop(rootInstructions,item);
                        }
                        
                    }
                }
            }
            else
            {
                GenNode(rootInstructions,n,0,-1,-1,-1,-1);
                GenPop(rootInstructions,n);
            }
            
        }
        else
        {
            GenNode(rootInstructions,n,0,-1,-1,-1,-1);
            GenPop(rootInstructions,n);
        }

        this->chunks[index] = std::pair<std::vector<uint32_t>,std::vector<ByteCodeInstruction*>>({},rootInstructions);
    }

    uint32_t CodeGen::NewId()
    {
        return id++;
    }

    void CodeGen::GenPop(std::vector<ByteCodeInstruction*>& instrs,SyntaxNode n)
    {
        if(std::holds_alternative<AdvancedSyntaxNode>(n) && std::get<AdvancedSyntaxNode>(n).isExpression)
        {
            instrs.push_back(new SimpleInstruction(POP));
        }
        else
        {
            instrs.push_back(new SimpleInstruction(POP));
        }
    }
}