#pragma once
#include <cstddef>
#include <initializer_list>
#include <vector>
#include <cstdint>
#include <string>
#include <cstdint>
#include <string>
#include <functional>
#include <variant>
#include <cstdio>
#include <atomic>
#include <filesystem>
#include <memory>
#include <cstring>
#include <TessesFramework/TessesFramework.hpp>
#include <regex>
#include <time.h>
#include <cstdbool>

/**
 * @brief CrossLang Runtime Major version
 * 
 */
#define TVM_MAJOR 1
/**
 * @brief CrossLang Runtime Minor version
 * 
 */
#define TVM_MINOR 0
/**
 * @brief CrossLang Runtime Patch version
 * 
 */
#define TVM_PATCH 0
/**
 * @brief CrossLang Runtime Build version
 * 
 */
#define TVM_BUILD 0
/**
 * @brief CrossLang Runtime Stage version
 * 
 */
#define TVM_VERSIONSTAGE DevVersion

#if defined(_WIN32)
#undef Yield
#endif
/**
 * @namespace Tesses::CrossLang
 * @brief The CrossLang namespace
 * 
 */
namespace Tesses::CrossLang {
    /**
     * @brief Escape a crosslang string (for generating source code)
     * 
     * @param text text to escape
     * @param quote whether to surround with double quotes
     * @return std::string the escaped string
     */
    std::string EscapeString(std::string text,bool quote);

    Tesses::Framework::Filesystem::VFSPath GetRealExecutablePath(Tesses::Framework::Filesystem::VFSPath realPath);
    /**
     * @brief Get the config folder used by crosslang
     * 
     * @return Tesses::Framework::Filesystem::VFSPath the config folder for crosslang
     */
    Tesses::Framework::Filesystem::VFSPath GetCrossLangConfigDir();



    /**
     * @brief Version stage
     * 
     */
    enum TVMVersionStage : uint8_t
    {
        DevVersion=0,
        AlphaVersion=1,
        BetaVersion=2,
        ProductionVersion=3
    };
    /**
     * @brief CrossLang version
     * 
     */
    class TVMVersion
    {
        uint8_t major;
        uint8_t minor;
        uint8_t patch;
        uint16_t build;
        public:
            /**
             * @brief Major
             * 
             * @return uint8_t The major
             */
            uint8_t Major()
            {
                return major;
            }
            /**
             * @brief Minor
             * 
             * @return uint8_t The minor
             */
            uint8_t Minor()
            {
                return minor;
            }
            /**
             * @brief Patch
             * 
             * @return uint8_t The patch
             */
            uint8_t Patch()
            {
                return patch;
            }
            /**
             * @brief Build
             * 
             * @return uint16_t The build
             */
            uint16_t Build()
            {
                return build >> 2;
            }
            /**
             * @brief Stage (dev, alpha, beta or prod)
             * 
             * @return TVMVersionStage The stage
             */
            TVMVersionStage VersionStage()
            {
                return (TVMVersionStage)(build & 3);
            }
            /**
             * @brief Set the Major
             * 
             * @param major The major
             */
            void SetMajor(uint8_t major)
            {
                this->major=major;
            }
             /**
             * @brief Set the Minor
             * 
             * @param minor The minor
             */
            void SetMinor(uint8_t minor)
            {
                this->minor=minor;
            }
             /**
             * @brief Set the Patch
             * 
             * @param patch The patch
             */
            void SetPatch(uint8_t patch)
            {
                this->patch=patch;
            }
             /**
             * @brief Set the Build
             * 
             * @param build The build << 2 | (stage & 3)
             */
            void SetBuild(uint16_t build)
            {
                this->build = build;
            }
            /**
             * @brief Set the Build (and stage)
             * 
             * @param build 
             * @param version 
             */
            void SetBuild(uint16_t build,TVMVersionStage version)
            {
                this->build = (build << 2) | version;
            }
            /**
             * @brief Set the Version
             * 
             * @param major The major
             * @param minor The minor
             * @param patch The patch
             * @param build The build
             * @param ver The stage
             */
            void SetVersion(uint8_t major, uint8_t minor, uint8_t patch, uint16_t build, TVMVersionStage ver)
            {
                this->SetMajor(major);
                this->SetMinor(minor);
                this->SetPatch(patch);
                this->SetBuild(build,ver);
            }
            /**
             * @brief Set the Version
             * 
             * @param major The major
             * @param minor The minor
             * @param patch The patch
             * @param build The build << 2 | (stage & 3)
             */
            void SetVersion(uint8_t major, uint8_t minor, uint8_t patch, uint16_t build)
            {
                this->SetMajor(major);
                this->SetMinor(minor);
                this->SetPatch(patch);
                this->SetBuild(build);
            }
            /**
             * @brief Set the Version
             * 
             * @param major The major
             * @param minor The minor
             * @param patch The patch
             * @param buildHigh The build (high 8 bits)
             * @param buildLow The build (low 6 bits) << 2 | (stage & 3)
             */
            void SetVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t buildHigh, uint8_t buildLow)
            {
                this->SetVersion(major, minor, patch, buildHigh << 8 | buildLow);
            }
            /**
             * @brief Set the Version
             * 
             * @param versionData same order as SetVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t buildHigh, uint8_t buildLow) but is a pointer to uint8_t version[5]
             */
            void SetVersion(uint8_t* versionData)
            {
                this->SetVersion(versionData[0],versionData[1],versionData[2],versionData[3],versionData[4]);
            }
            /**
             * @brief Set the version from long
             * 
             * @param v version serialized as long
             */
            void SetFromLong(uint64_t v)
            {

                this->major = (uint8_t)((v >> 32) & 0xFF);
                this->minor = (uint8_t)(v >> 24) & 0xFF;
                this->patch = (uint8_t)(v >> 16) & 0xFF;
                this->build = (uint16_t)(v & 0xFFFF);
            }
            /**
             * @brief Encodes the version to bytes to be decoded by SetVersion(uint8_t*) or the constructor
             * 
             * @param versionData an array that is 5 bytes long
             */
            void ToArray(uint8_t* versionData)
            {
                versionData[0] = major;
                versionData[1] = minor;
                versionData[2] = patch;
                versionData[3] = build >> 8;
                versionData[4] = build & 0xFF;
            }
            /**
             * @brief Construct a new Version object
             * 
             * @param major Major
             * @param minor Minor
             * @param patch Patch
             * @param build Build
             * @param version Stage (dev, alpha, beta, prod) see TVMVersionStage for actual names 
             */
            TVMVersion(uint8_t major, uint8_t minor, uint8_t patch, uint16_t build, TVMVersionStage version)
            {
                this->SetVersion(major,minor,patch,build,version);
            }
            /**
             * @brief Construct a new Version object
             * 
             * @param versionData same order as SetVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t buildHigh, uint8_t buildLow) but is a pointer to uint8_t version[5]
             */
            TVMVersion(uint8_t* versionData)
            {
                this->SetVersion(versionData);
            }
            /**
             * @brief Construct a new 1.0.0.0-dev Version object
             * 
             */
            TVMVersion()
            {
                this->major=1;
                this->minor=0;
                this->patch=0;
                this->SetBuild(0, TVMVersionStage::DevVersion);
            }
            /**
             * @brief Construct a new Version object from a long
             * 
             * @param v version serialized as long
             */
            TVMVersion(uint64_t v)
            {
                this->SetFromLong(v);
            }
            /**
             * @brief Compares to another version
             * 
             * @param version another version object
             * @return int returns 1 if this is newer than other version, 0 if same, -1 if this is older than other version
             */
            int CompareTo(TVMVersion& version)
            {
                if(this->major > version.major) return 1;
                if(this->major < version.major) return -1;
                if(this->minor > version.minor) return 1;
                if(this->minor < version.minor) return -1;
                if(this->patch > version.patch) return 1;
                if(this->patch < version.patch) return -1;
                if(this->build > version.build) return 1;
                if(this->build < version.build) return -1;
                return 0;
            }
            /**
             * @brief Serializes to a long
             * 
             * @return uint64_t serialized as a long
             */
            uint64_t AsLong()
            {
                uint64_t v = (uint64_t)major << 32;
                v |= (uint64_t)minor << 24;
                v |= (uint64_t)patch << 16;
                v |= (uint64_t)build;
                return v;
            }
            /**
             * @brief Compares this to the runtime version
             * 
             * @return int CompareTo(RuntimeVersion) where RuntimeVersion is the runtime version
             */
            int CompareToRuntime()
            {
                TVMVersion version(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE);
                return CompareTo(version);
            }
            /**
             * @brief Try to parse the version from a string
             * 
             * @param versionStr the version string like 1.0.0.0-prod (or dev, alpha, beta)
             * @param version a reference to a version
             * @return true the parsing succeeded
             * @return false the parsing failed
             */
            static bool TryParse(std::string versionStr, TVMVersion& version)
            {
                if(versionStr.empty()) return false;
                size_t sep=versionStr.find_last_of('-');

                std::string left = versionStr;
                std::string right = "prod";

                if(sep != std::string::npos)
                {
                    left = versionStr.substr(0, sep);
                    right = versionStr.substr(sep+1);
                }
                
                if(left.empty()) return false;

                

                TVMVersionStage stage;
                if(right == "dev")
                    stage = TVMVersionStage::DevVersion;
                else if(right == "alpha")
                    stage = TVMVersionStage::AlphaVersion;
                else if(right == "beta")
                    stage = TVMVersionStage::BetaVersion;
                else if(right == "prod")
                    stage = TVMVersionStage::ProductionVersion;
                else
                    return false;
                

                //1 0.0.0
                sep=left.find_first_of('.');

                if(sep != std::string::npos)
                {
                    auto lStr = left.substr(0, sep);
                    
                    right = left.substr(sep+1);
                    left = lStr;
                }
                else
                {
                    version.SetMajor((uint8_t)std::stoul(left));
                    version.SetBuild(0,stage);
                    return true;
                }
                version.SetMajor((uint8_t)std::stoul(left));
                left = right;
                sep=left.find_first_of('.');

                if(sep != std::string::npos)
                {
                    auto lStr = left.substr(0, sep);
                    
                    right = left.substr(sep+1);
                    left = lStr;
                }
                else
                {
                    version.SetMinor((uint8_t)std::stoul(left));
                    version.SetBuild(0,stage);
                    return true;
                }
                version.SetMinor((uint8_t)std::stoul(left));
                left = right;
                 sep=left.find_first_of('.');

                if(sep != std::string::npos)
                {
                    auto lStr = left.substr(0, sep);
                    
                    right = left.substr(sep+1);
                    left = lStr;
                }
                else
                {
                    version.SetPatch((uint8_t)std::stoul(left));
                    version.SetBuild(0,stage);
                    return true;
                }
                version.SetPatch((uint8_t)std::stoul(left));

                version.SetBuild((uint16_t)std::stoul(right),stage);
                return true;

            }
            /**
             * @brief Converts version to string
             * 
             * @return std::string the version string like 1.0.0.0-prod (or dev, alpha, beta)
             */
            std::string ToString()
            {
                std::string str={};
                str.append(std::to_string((int)this->Major()));
                str.push_back('.');
                str.append(std::to_string((int)this->Minor()));
                str.push_back('.');
                str.append(std::to_string((int)this->Patch()));
                str.push_back('.');
                str.append(std::to_string((int)this->Build()));
                if(this->VersionStage() == TVMVersionStage::DevVersion)
                {
                    str.append("-dev");
                }
                else if(this->VersionStage() == TVMVersionStage::AlphaVersion)
                {
                    str.append("-alpha");
                }
                else if(this->VersionStage() == TVMVersionStage::BetaVersion)
                {
                    str.append("-beta");
                }
                else if(this->VersionStage() == TVMVersionStage::ProductionVersion)
                {
                    str.append("-prod");
                }
                return str;
            }
    };

    /**
     * @brief Create a crvm archive file
     * 
     * @param vfs the filesystem used as root of the archive
     * @param strm the output stream
     * @param name the crvm name
     * @param version the crvm version
     * @param info the crvm info
     * @param icon the crvm icon
     */
    void CrossArchiveCreate(Tesses::Framework::Filesystem::VFS* vfs,Tesses::Framework::Streams::Stream* strm,std::string name,TVMVersion version,std::string info, std::string icon="");
    /**
     * @brief 
     * 
     * @param strm the input stream
     * @param vfs vfs to extract to (as root)
     * @return std::pair<std::pair<std::string,TVMVersion>,std::string> ((name, version),info)
     */
    std::pair<std::pair<std::string,TVMVersion>,std::string> CrossArchiveExtract(Tesses::Framework::Streams::Stream* strm,Tesses::Framework::Filesystem::VFS* vfs);



/**
 * @brief The token type for lexer
 * 
 */
typedef enum {
        Identifier,
        Symbol,
        String,
        Char,
        Documentation
} LexTokenType;
    /**
     * @brief The line info for lextoken
     * 
     */
    class LexTokenLineInfo
    {
        public:
            /**
             * @brief line number
             * 
             */
            int line;
            /**
             * @brief column number
             * 
             */
            int column;
            /**
             * @brief byte offset in file
             * 
             */
            int offset;
            /**
             * @brief filename
             * 
             */
            std::string filename;
            /**
             * @brief Add a char
             * 
             * @param c a char to add to line, column, offset
             */
            void Add(int c);
            /**
             * @brief Subtract from column and offset
             * 
             * @param c the number of chars
             */
            void Subtract(size_t c);
    };

    /**
     * @brief The lextoken for lexer
     * 
     */
    class LexToken {
        public:
            /**
             * @brief line information
             * 
             */
            LexTokenLineInfo lineInfo;
            /**
             * @brief the token type
             * 
             */
            LexTokenType type;
            /**
             * @brief the token text
             * 
             */
            std::string text;
            /**
             * @brief any white space characters before this token
             * 
             */
            std::string whiteSpaceCharsBefore;
    };
    /**
     * @brief Start lexing the file
     * 
     * @param filename the filename used for exception purposes
     * @param strm the character stream
     * @param tokens the tokens
     * @return int 0 on success, line number on failure
     */
    int Lex(std::string filename, std::istream& strm, std::vector<LexToken>& tokens);

/**
 * @brief Undefined type
 * 
 */
class Undefined
{

};
class AdvancedSyntaxNode;
/**
 * @brief Bytecode instruction enumeration
 * 
 */
typedef enum {
    ADD,
    SUB,
    TIMES,
    DIVIDE,
    MODULO,
    LEFTSHIFT,
    RIGHTSHIFT,
    BITWISEOR,
    BITWISEAND,
    BITWISENOT,
    LESSTHAN,
    GREATERTHAN,
    LESSTHANEQ,
    GREATERTHANEQ,
    EQ,
    NEQ,
    NOT,
    NEGATIVE,
    XOR,
    POP,
    DUP,
    NOP,
    PUSHCLOSURE,
    CREATEDICTIONARY,
    CREATEARRAY,
    APPENDLIST,
    APPENDDICT,
    PUSHRESOURCE,
    PUSHLONG,
    PUSHCHAR,
    PUSHDOUBLE,
    PUSHSTRING,
    PUSHNULL,
    PUSHUNDEFINED,
    SCOPEBEGIN,
    SCOPEEND,
    SCOPEENDTIMES,
    PUSHFALSE,
    PUSHTRUE,
    SETVARIABLE,
    GETVARIABLE,
    DECLAREVARIABLE,
    SETFIELD,
    GETFIELD,
    CALLFUNCTION,
    CALLMETHOD,
    RET,
    JMPC,
    JMP,
    JMPUNDEFINED,
    DEFER,
    TRYCATCH,
    THROW,
    PUSHSCOPELESSCLOSURE,
    YIELD,
    PUSHROOTPATH,
    PUSHRELATIVEPATH,
    BREAKPOINT
} Instruction;
/**
 * @brief Base type for bytecode instruction
 * 
 */
class ByteCodeInstruction {
    public:
        /**
         * @brief Size of instruction
         * 
         * @return size_t the length of the instruction
         */
        virtual size_t Size()=0;
        /**
         * @brief Write the instruction to vector
         * 
         * @param data the vector of uint8_t
         */
        virtual void Write(std::vector<uint8_t>& data)=0;
        virtual ~ByteCodeInstruction()
        {}
};
/**
 * @brief Basic bytecode instruction
 * 
 */
class SimpleInstruction : public ByteCodeInstruction {
    public:
        /**
         * @brief The instruction
         * 
         */
        Instruction instruction;
        /**
         * @brief Construct a new Simple Instruction object
         * 
         * @param instr the instruction
         */
        SimpleInstruction(Instruction instr);
        /**
         * @brief Size of instruction
         * 
         * @return size_t 1, because simple instructions are just an instruction
         */
        size_t Size();
        /**
         * @brief Write the opcode to the vector
         * 
         * @param data the vector of uint8_t
         */
        void Write(std::vector<uint8_t>& data);
};

/**
 * @brief A bit converter
 * 
 */
class BitConverter {
    public:
        /**
         * @brief Get the bits of a double from a int64_t
         * 
         * @param v a int64_t with double's bits
         * @return double the double
         */
        static double ToDoubleBits(uint64_t v);
        /**
         * @brief Get the bits of a int64_t from a double
         * 
         * @param v a double with int64_t's bits
         * @return uint64_t the int64_t
         */
        static uint64_t ToUintBits(double v);
        /**
         * @brief Get big endian double from uint8_t reference of first element of 8 byte array
         * 
         * @param b a reference to the first byte of an array
         * @return double the double
         */
        static double ToDoubleBE(uint8_t& b);
        /**
         * @brief Get big endian uint64_t from uint8_t reference of first element of 8 byte array
         * 
         * @param b a reference to the first byte of an array
         * @return uint64_t the uint64_t
         */
        static uint64_t ToUint64BE(uint8_t& b);
        static uint32_t ToUint32BE(uint8_t& b);
        static uint16_t ToUint16BE(uint8_t& b);
        static void FromDoubleBE(uint8_t& b, double v);
        static void FromUint64BE(uint8_t& b, uint64_t v);
        static void FromUint32BE(uint8_t& b, uint32_t v);
        static void FromUint16BE(uint8_t& b, uint16_t v);
};

class StringInstruction : public ByteCodeInstruction {
    public:
        uint32_t n;
        StringInstruction(uint32_t n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};
class ScopeEndTimesInstruction : public ByteCodeInstruction {
    public:
        uint32_t n;
        ScopeEndTimesInstruction(uint32_t n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class LabelInstruction : public ByteCodeInstruction {
    public:
        std::string label;
        LabelInstruction(std::string lbl);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};
class LabelableInstruction : public ByteCodeInstruction {
    public:
        uint32_t n;
        std::string label;
        virtual size_t Size();
        virtual void Write(std::vector<uint8_t>& data);
};

class JumpStyleInstruction : public LabelableInstruction {
    public:
        Instruction type;
       
        JumpStyleInstruction(Instruction conditional,std::string label);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class EmbedInstruction : public ByteCodeInstruction {
    public:
        uint32_t n;
        EmbedInstruction(uint32_t n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class ClosureInstruction : public ByteCodeInstruction {
    public:
        bool hasScope;
        uint32_t n;
        ClosureInstruction(uint32_t n,bool hasScope=true);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class DoubleInstruction : public ByteCodeInstruction {
    public:
        double n;
        DoubleInstruction(double n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class LongInstruction : public ByteCodeInstruction {
    public:
        int64_t n;
        LongInstruction(int64_t n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

class CharInstruction : public ByteCodeInstruction {
    public:
        char n;
        CharInstruction(char n);
        size_t Size();
        void Write(std::vector<uint8_t>& data);
};

using SyntaxNode = std::variant<int64_t, double, std::string, char, bool, std::nullptr_t, Undefined, AdvancedSyntaxNode>;

class ObjectEntry {
    public:
        std::vector<std::string> name;
        std::string doc;
        std::vector<std::pair<std::string,SyntaxNode>> variables;
        std::vector<std::pair<std::pair<std::string,std::string>,uint32_t>> funcs;
        std::vector<std::pair<std::pair<std::string,std::string>,uint32_t>> static_funcs;

};

class CodeGen {
    uint32_t id;
    uint32_t NewId();
    
    void GetFunctionArgs(std::vector<uint32_t>& args,SyntaxNode n);
    void GetFunctionName(std::vector<uint32_t>& name,SyntaxNode n);
    void GetFunctionArgs(std::vector<SyntaxNode>& args,SyntaxNode n);
    SyntaxNode StringifyListOfVars(SyntaxNode n);

    uint32_t GetString(std::string str);
    uint32_t GetResource(std::string res);
    std::vector<std::string> strs;
    std::vector<std::string> res;
    
    std::vector<std::pair<std::vector<uint32_t>, std::vector<ByteCodeInstruction*>>> chunks;
    std::vector<std::pair<std::vector<uint32_t>,uint32_t>> funcs;
    std::vector<ObjectEntry> objectEntries;

    void GenNode(std::vector<ByteCodeInstruction*>& instructions, SyntaxNode n,int32_t scope, int32_t contscope, int32_t brkscope, int32_t contI, int32_t brkI);
    void GenPop(std::vector<ByteCodeInstruction*>& instrs,SyntaxNode n);
    public:
        std::vector<std::pair<std::string, TVMVersion>> dependencies;
        std::vector<std::pair<std::string, TVMVersion>> tools;
        TVMVersion version;
        std::string name;
        std::string info;
        std::string icon;
        void GenRoot(SyntaxNode n);
        void Save(Tesses::Framework::Filesystem::VFS* embedFS,Tesses::Framework::Streams::Stream* output);
        ~CodeGen();
};
/**
 * @brief A html expression
 * 
 */
constexpr std::string_view HtmlRootExpression = "htmlRootExpression";
/**
 * @brief an intrinsic function that embeads a resource from the filename based on the constant string argument
 * 
 */
constexpr std::string_view EmbedExpression = "embedExpression";
/**
 * @brief Negative operator -EXPR
 * 
 */
constexpr std::string_view NegativeExpression = "negativeExpression";
/**
 * @brief Not operator !EXPR
 * 
 */
constexpr std::string_view NotExpression = "notExpression";
/**
 * @brief Bitwise not operator ~EXPR
 * 
 */
constexpr std::string_view BitwiseNotExpression = "bitwiseNotExpression";
/**
 * @brief ++i
 * 
 */
constexpr std::string_view PrefixIncrementExpression = "prefixIncrementExpression";
/**
 * @brief --i
 * 
 */
constexpr std::string_view PrefixDecrementExpression = "prefixDecrementExpression";
/**
 * @brief i++
 * 
 */
constexpr std::string_view PostfixIncrementExpression = "postfixIncrementExpression";
/**
 * @brief i--
 * 
 */
constexpr std::string_view PostfixDecrementExpression = "postfixDecrementExpression";
/**
 * @brief Gets variable value
 * 
 */
constexpr std::string_view GetVariableExpression = "getVariableExpression";
/**
 * @brief Get field or get property (EXPR.getNAME()) (unless it is wrapped in AssignExpression) which then it sets the field or calls EXPR.setNAME(VALUE)
 * 
 */
constexpr std::string_view GetFieldExpression = "getFieldExpression";
/**
 * @brief Syntax sugar for EXPR.GetAt(index) (unless it is wrapped in AssignExpresion) which then it is EXPR.SetAt(index, VALUE)
 * 
 */
constexpr std::string_view GetArrayExpression = "getArrayExpression";
/**
 * @brief Function Call myFn() or MyDict.Fn()
 * 
 */
constexpr std::string_view FunctionCallExpression = "functionCallExpression";
/**
 * @brief Used for compound assignment like VAR += EXPR
 * 
 */
constexpr std::string_view CompoundAssignExpression = "compoundAssignExpression";
/**
 * @brief Assign something using =
 * 
 */
constexpr std::string_view AssignExpression = "assignExpression";
/**
 * @brief A char litteral
 * 
 */
constexpr std::string_view CharExpression = "charExpression";
/**
 * @brief The undefined literal
 * 
 */
constexpr std::string_view UndefinedExpression = "undefinedExpression";
/**
 * @brief A list of nodes that does create a new scope
 * 
 */
constexpr std::string_view ScopeNode = "scopeNode";
/**
 * @brief A list of nodes that doesn't create a new scope
 * 
 */
constexpr std::string_view NodeList = "nodeList";
/**
 * @brief Array expresion [1,2,10]
 * 
 */
constexpr std::string_view ArrayExpression = "arrayExpression";
/**
 * @brief Dictionary expression  { a = 42, b = false}
 * 
 */
constexpr std::string_view DictionaryExpression = "dictionaryExpression";
/**
 * @brief var v = 59;
 * 
 */
constexpr std::string_view DeclareExpression = "declareExpression";
/**
 * @brief Closure expression (a,b)=> a * b
 * 
 */
constexpr std::string_view ClosureExpression = "closureExpression";
/**
 * @brief Just like normal closures but doesn't create its own scope
 * 
 */
constexpr std::string_view ScopelessClosureExpression = "scopelessClosureExpression";
/**
 * @brief Ternary operator COND ? EXPR : EXPR
 * 
 */
constexpr std::string_view TernaryExpression = "ternaryExpression";
/**
 * @brief If statement if(COND) {} else if(COND) {} else {}
 * 
 */
constexpr std::string_view IfStatement = "ifStatement";
/**
 * @brief For statement for(var i = 0; i < 42; i++)
 * 
 */
constexpr std::string_view ForStatement = "forStatement";
/**
 * @brief While statement while(COND)
 * 
 */
constexpr std::string_view WhileStatement = "whileStatement";
/**
 * @brief Do statement do(COND)
 * 
 */
constexpr std::string_view DoStatement = "doStatement";
/**
 * @brief Each statement each(var item : EXPR)
 * 
 */
constexpr std::string_view EachStatement = "eachStatement";
/**
 * @brief Return statement return EXPR;
 * 
 */
constexpr std::string_view ReturnStatement = "returnStatement";
/**
 * @brief Break statement break;
 * 
 */
constexpr std::string_view BreakStatement = "breakStatement";
/**
 * @brief Continue statement continue;
 * 
 */
constexpr std::string_view ContinueStatement = "continueStatement";
/**
 * @brief Function statement func main(args)
 * 
 */
constexpr std::string_view FunctionStatement = "functionStatement";
/**
 * @brief The documentation statement /^ Documentation ^/ func main(args) {}
 * 
 */
constexpr std::string_view DocumentationStatement = "documentationStatement";
/**
 * @brief The comma expression EXPR, EXPR
 * 
 */
constexpr std::string_view CommaExpression = "commaExpression";
/**
 * @brief The addition operator EXPR + EXPR
 * 
 */
constexpr std::string_view AddExpression = "addExpression";
/**
 * @brief The subtraction operator EXPR - EXPR
 * 
 */
constexpr std::string_view SubExpression = "subtractExpression";
/**
 * @brief The times operator EXPR * EXPR
 * 
 */
constexpr std::string_view TimesExpression = "timesExpression";
/**
 * @brief The divsion operator EXPR / EXPR
 * 
 */
constexpr std::string_view DivideExpression = "divideExpression";
/**
 * @brief The modulo operator (get remainder) EXPR % EXPR
 * 
 */
constexpr std::string_view ModExpression = "modExpression";
/**
 * @brief The left shift operator EXPR << EXPR
 * 
 */
constexpr std::string_view LeftShiftExpression = "leftShiftExpression";
/**
 * @brief The right shift operator EXPR >> EXPR
 * 
 */
constexpr std::string_view RightShiftExpression = "rightShiftExpression";
/**
 * @brief The bitwise or expression EXPR | EXPR
 * 
 */
constexpr std::string_view BitwiseOrExpression = "bitwiseOrExpression";
/**
 * @brief The bitwise and expression EXPR & EXPR
 * 
 */
constexpr std::string_view BitwiseAndExpression = "bitwiseAndExpression";
/**
 * @brief The xor expression EXPR ^ EXPR
 * 
 */
constexpr std::string_view XOrExpression = "xOrExpression";
/**
 * @brief The logical or expression EXPR || EXPR
 * 
 */
constexpr std::string_view LogicalOrExpression = "logicalOrExpression";
/**
 * @brief The logical and expression EXPR && EXPR
 * 
 */
constexpr std::string_view LogicalAndExpression = "logicalAndExpression";
/**
 * @brief The equals expression EXPR == EXPR
 * 
 */
constexpr std::string_view EqualsExpression = "equalsExpression";
/**
 * @brief The not equals expression EXPR != EXPR
 * 
 */
constexpr std::string_view NotEqualsExpression = "notEqualsExpression";
/**
 * @brief The less than expression EXPR < EXPR
 * 
 */
constexpr std::string_view LessThanExpression = "lessThanExpression";
/**
 * @brief The greater than expression EXPR > EXPR
 * 
 */
constexpr std::string_view GreaterThanExpression = "greaterThanExpression";
/**
 * @brief The less than equals expression EXPR <= EXPR
 * 
 */
constexpr std::string_view LessThanEqualsExpression = "lessThanEqualsExpression";
/**
 * @brief The greater than equals expression EXPR >= EXPR
 * 
 */
constexpr std::string_view GreaterThanEqualsExpression = "greaterThanEqualsExpression";
/**
 * @brief The parentheses expression (expression)
 * 
 */
constexpr std::string_view ParenthesesExpression = "parenthesesExpression";
/**
 * @brief The throw statement
 * 
 */
constexpr std::string_view ThrowStatement = "throwStatement";
/**
 * @brief The breakpoint statement
 */
constexpr std::string_view BreakpointStatement = "breakpointStatement";
/**
 * @brief The try statement
 * 
 */
constexpr std::string_view TryStatement = "tryStatement";
/**
 * @brief The defer statement
 * 
 */
constexpr std::string_view DeferStatement = "deferStatement";
/**
 * @brief The defer statement
 * 
 */
constexpr std::string_view YieldStatement = "yieldStatement";
/**
 * @brief The enumerable statement
 * 
 */
constexpr std::string_view EnumerableStatement = "enumerableStatement";
/**
 * @brief The switch statement
 * 
 */
constexpr std::string_view SwitchStatement = "switchStatement";
/**
 * @brief Case clause of switch statement
 * 
 */
constexpr std::string_view CaseStatement = "caseStatement";
/**
 * @brief Default clause of switch statement
 * 
 */
constexpr std::string_view DefaultStatement = "defaultStatement";
/**
 * @brief Object statement (like class but not having inheritence) (not implemented)
 * 
 */
constexpr std::string_view ObjectStatement = "objectStatement";
/**
 * @brief Static statement (static methods in object statement)
 * 
 */
constexpr std::string_view StaticStatement = "staticStatement";

/**
 * @brief Method declaration method fun() {} in object statement
 * 
 */

constexpr std::string_view MethodDeclaration = "methodDeclaration";
/**
 * @brief Root path expression / "path" / "to" / "file" (fullfils the first /)
 * 
 */
constexpr std::string_view RootPathExpression = "rootPathExpression";
/**
 * @brief The relative path expression ./ "path" / "to" / "subdir" (fullfills the ./)
 * 
 */
constexpr std::string_view RelativePathExpression = "relativePathExpression";
/**
 * @brief Advanced AST node
 * 
 */
class AdvancedSyntaxNode {
    public:
        /**
         * @brief The name of the advanced AST node
         * 
         */
        std::string nodeName;
        /**
         * @brief The advanced syntax node is an expression
         * 
         */
        bool isExpression;
        /**
         * @brief The subnodes of the abstract syntax node
         * 
         */
        std::vector<SyntaxNode> nodes;
        /**
         * @brief Create abstract syntax node
         * 
         * @param nodeName The name of the advanced AST node
         * @param isExpression The advanced syntax node is an expression
         * @param n The subnodes of the abstract syntax node
         * @return AdvancedSyntaxNode The AST node
         */
        static AdvancedSyntaxNode Create(std::string_view nodeName,bool isExpression, std::vector<SyntaxNode> n);
};



SyntaxNode Deserialize(std::string astData);

std::string Serialize(SyntaxNode node);
/**
 * @brief Token Parser
 * 
 */
class Parser {
    uint32_t id;
    uint32_t NewId();
    uint64_t i;
    std::vector<LexToken> tokens;
        
    LexToken tkn;
    void EnsureSymbol(std::string txt);
    bool IsIdentifier(std::string txt,bool pop=true);
    bool IsAnyIdentifier(std::initializer_list<std::string> idents, bool pop=true);
    bool IsSymbol(std::string txt,bool pop=true);
    bool IsAnySymbol(std::initializer_list<std::string> idents, bool pop=true);
    
    SyntaxNode ParseExpression();
    SyntaxNode ParseTernary();
    SyntaxNode ParseNode(bool isRoot=false);
    SyntaxNode ParseAssignment();
    SyntaxNode ParseXOr();
    SyntaxNode ParseBOr();
    SyntaxNode ParseLOr();
    SyntaxNode ParseLAnd();
    SyntaxNode ParseBAnd();
    SyntaxNode ParseEq();
    SyntaxNode ParseRel();
    SyntaxNode ParseShift();
    SyntaxNode ParseSum();
    SyntaxNode ParseFactor();
    SyntaxNode ParseValue();
    SyntaxNode ParseUnary();
    void ParseHtml(std::vector<SyntaxNode>& nodes,std::string var);
    public:
        /**
         * @brief Construct a new Parser object
         * 
         * @param tokens the tokens from lexer
         */
        Parser(std::vector<LexToken> tokens);
        /**
         * @brief Turn tokens into abstract syntax tree
         * 
         * @return SyntaxNode the abstract syntax tree
         */
        SyntaxNode ParseRoot()
        {
            return ParseNode(true);
        }
};
   class THeapObject;
    class CallStackEntry;
    class InterperterThread;
        class THeapObject {
        public:
            bool marked;
        virtual void Mark()
        {
            marked = true;
        }
          
        virtual ~THeapObject()
        {

        }

        
    };
    
        class THeapObjectHolder
    {
        public:
            THeapObject* obj;
            THeapObjectHolder(THeapObject* obj)
            {
                this->obj = obj;
            }
            THeapObjectHolder()
            {
                
            }
    };
    //this is a dummy type with
    class MethodInvoker {

    };
    /**
     * @brief A script object
     * 
     */
using TObject = std::variant<int64_t,double,char,bool,std::string,std::regex,Tesses::Framework::Filesystem::VFSPath,std::nullptr_t,Undefined,MethodInvoker,THeapObjectHolder,TVMVersion>;
class TRootEnvironment;
class GC;
class GC {
    Tesses::Framework::Threading::Thread* thrd;
    Tesses::Framework::Threading::Mutex* mtx;
    volatile std::atomic<bool> running;
    std::vector<THeapObject*> roots;
    std::vector<THeapObject*> objects;
    std::vector<std::function<void(GC* gc, TRootEnvironment* env)>> register_everything;
    public:
        bool UsingNullThreads();
        GC();
        void Start();
        bool IsRunning();
        void BarrierBegin();
        void BarrierEnd();
        void Collect();
        void Watch(TObject obj);
        void Unwatch(TObject obj);
        void SetRoot(TObject obj);
        void UnsetRoot(TObject obj);
        static void Mark(TObject obj);
        void RegisterEverythingCallback(std::function<void(GC* gc, TRootEnvironment* env)> cb);
        void RegisterEverything(TRootEnvironment* env);
        ~GC();
};


    std::string ToString(GC* gc, TObject obj);

    class GCList : public THeapObject
    {   
        std::vector<THeapObject*> items;
        
        GC* gc;
        public:
            GCList(GC* gc);
            GCList(GC& gc);
            GC* GetGC();
            void Add(TObject v);
            void Remove(TObject v);
            void Mark();

            
            
            ~GCList();

    };

  


    
    class TFile;
    
    class TFileChunk : public THeapObject
    {
        public:
            static TFileChunk* Create(GCList* gc);
            static TFileChunk* Create(GCList& gc);
            TFile* file;
            std::vector<uint8_t> code;
            std::vector<std::string> args;
            void Mark();
    };
    
  

    
    class TByteArray : public THeapObject
    {
        public:
            std::vector<uint8_t> data;
            static TByteArray* Create(GCList* gc);
            static TByteArray* Create(GCList& gc);
    };

    class TFile : public THeapObject
    {
        public:
            static TFile* Create(GCList* gc);
            static TFile* Create(GCList& gc);
            std::vector<TFileChunk*> chunks;
            
            std::vector<std::string> strings;
            std::vector<std::pair<std::vector<std::string>, uint32_t>> functions;
            std::vector<std::pair<std::string,TVMVersion>> dependencies;
            std::vector<std::pair<std::string,TVMVersion>> tools;
            std::vector<std::vector<uint8_t>> resources;
            std::string name;
            TVMVersion version;
            std::string info;
            int32_t icon;
        
            void Load(GC* gc, Tesses::Framework::Streams::Stream* strm);
            void Skip(Tesses::Framework::Streams::Stream* strm,size_t len);
            void Ensure(Tesses::Framework::Streams::Stream* strm,uint8_t* buffer, size_t len);
            uint32_t EnsureInt(Tesses::Framework::Streams::Stream* strm);
            std::string EnsureString(Tesses::Framework::Streams::Stream* strm);

            std::string GetString(Tesses::Framework::Streams::Stream* strm);
            void Mark();
    };

    class TList : public THeapObject
    {
        public:
            std::vector<TObject> items;
            static TList* Create(GCList* gc);
            static TList* Create(GCList& gc);
            template<typename Itterator>
            static TList* Create(GCList* gc, Itterator begin, Itterator end)
            {
                auto list = Create(gc);
                gc->GetGC()->BarrierBegin();
                for(Itterator i = begin; i != end; ++i)
                {
                    TObject item = *i;
                    list->Add(item);
                }
                gc->GetGC()->BarrierEnd();
                return list;
            }
            template<typename Itterator>
            static TList* Create(GCList& gc, Itterator begin, Itterator end)
            {
                return Create(&gc,begin,end);
            }
            static TList* Create(GCList* gc, std::initializer_list<TObject> il)
            {
                return Create(gc,il.begin(),il.end());
            }
            static TList* Create(GCList& gc, std::initializer_list<TObject> il)
            {
                return Create(gc,il.begin(),il.end());
            }
            virtual int64_t Count();
            virtual TObject Get(int64_t index);
            virtual void Set(int64_t index, TObject value);
            virtual void Insert(int64_t index,TObject value);
            virtual void Add(TObject value);
            virtual void RemoveAt(int64_t index);
            virtual void Clear();
            virtual void Mark();

    };
    using TDItem = std::pair<std::string, TObject>;

    class TDictionary : public THeapObject
    {
        public:
            std::map<std::string,TObject> items;
            static TDictionary* Create(GCList* gc);
            static TDictionary* Create(GCList& gc);
            template<typename Itterator>
            static TDictionary* Create(GCList* gc, Itterator begin, Itterator end)
            {
                auto dict = Create(gc);
                gc->GetGC()->BarrierBegin();
                for(Itterator i = begin; i != end; ++i)
                {
                    TDItem item = *i;
                    dict->SetValue(item.first, item.second);
                }
                gc->GetGC()->BarrierEnd();
                return dict;
            }
            template<typename Itterator>
            static TDictionary* Create(GCList& gc, Itterator begin, Itterator end)
            {
                return Create(&gc,begin,end);
            }

            static TDictionary* Create(GCList* gc, std::initializer_list<TDItem> il)
            {
                return Create(gc,il.begin(),il.end());
            }
            static TDictionary* Create(GCList& gc, std::initializer_list<TDItem> il)
            {
                return Create(gc, il.begin(),il.end());
            }
            

          
            virtual bool HasValue(std::string key);
            virtual bool MethodExists(GCList& ls,std::string key);
            virtual TObject GetValue(std::string key);
            virtual void SetValue(std::string key, TObject value);
            virtual void Mark();
            void DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            void DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            void DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);
            void DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);
            TObject CallMethod(GCList& ls, std::string key, std::vector<TObject> args);
    };

   

    class TRootEnvironment;
    class TSubEnvironment;

    class TCallable : public THeapObject
    {
        public:
            TObject tag;
            std::string documentation;
            virtual TObject Call(GCList& ls,std::vector<TObject> args)=0;
            virtual void Mark();
    };

    

    class TEnvironment : public THeapObject {
        
        public:
            std::vector<TCallable*> defers;
            TObject LoadFile(GC* gc, TFile* f);
            TObject Eval(GCList& ls,std::string code);
            virtual bool HasVariable(std::string key)=0;
            virtual bool HasVariableRecurse(std::string key)=0;
            virtual TObject GetVariable(std::string key)=0;
            virtual void SetVariable(std::string key, TObject value)=0;
            virtual void DeclareVariable(std::string key, TObject value)=0;
            virtual TRootEnvironment* GetRootEnvironment()=0;
            virtual TEnvironment* GetParentEnvironment()=0;
            virtual TSubEnvironment* GetSubEnvironment(TDictionary* dict);
            TSubEnvironment* GetSubEnvironment(GCList* gc);
            TSubEnvironment* GetSubEnvironment(GCList& gc);

            void DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            void DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            void DeclareFunction(GC* gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);
            void DeclareFunction(GC& gc,std::string key,std::string documentation, std::vector<std::string> argNames, std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);
            TObject CallFunction(GCList& ls, std::string key, std::vector<TObject> args);
    
    };

    class EnvironmentPermissions {
        public:
            EnvironmentPermissions();
            Tesses::Framework::Filesystem::VFSPath sqliteOffsetPath;
            bool canRegisterEverything;
            bool canRegisterConsole;
            bool canRegisterIO;
            bool canRegisterLocalFS;
            bool canRegisterNet;
            bool canRegisterSqlite;
            bool canRegisterVM;
            bool canRegisterDictionary;
            bool canRegisterJSON;
            bool canRegisterCrypto;
            bool canRegisterRoot;
            bool canRegisterProcess;
            bool canRegisterPath;
            bool canRegisterOGC;
            bool canRegisterEnv;
            bool canRegisterTime;
            bool sqlite3Scoped;
            bool locked;
    };

    class TRootEnvironment : public TEnvironment
    {
        TDictionary* dict;
        TCallable* error=nullptr;
        
        void LoadDependency(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, std::pair<std::string,TVMVersion> dep);
        public:
            EnvironmentPermissions permissions;

            std::vector<std::pair<std::string, TVMVersion>> dependencies;
            
            void LoadFileWithDependencies(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, TFile* f);
            void LoadFileWithDependencies(GC* gc,Tesses::Framework::Filesystem::VFS* vfs, Tesses::Framework::Filesystem::VFSPath path);
    
            TDictionary* GetDictionary();
            static TRootEnvironment* Create(GCList* gc,TDictionary* dict);
            static TRootEnvironment* Create(GCList& gc,TDictionary* dict);
            TRootEnvironment(TDictionary* dict);
            bool HasVariable(std::string key);
            bool HasVariableRecurse(std::string key);
            TObject GetVariable(std::string key);
            void SetVariable(std::string key, TObject value);
            void DeclareVariable(std::string key, TObject value);
            TRootEnvironment* GetRootEnvironment();
            TEnvironment* GetParentEnvironment();
            void RegisterOnError(TCallable* callable);
            bool HandleException(GC* gc,TEnvironment* env, TObject err);
            bool HandleBreakpoint(GC* gc,TEnvironment* env, TObject err);
            void Mark();
    };
    class TStd {
        public:
            static void RegisterStd(GC* gc, TRootEnvironment* env);
            static void RegisterConsole(GC* gc,TRootEnvironment* env);
            static void RegisterIO(GC* gc,TRootEnvironment* env, bool enableLocalFilesystem=true);
            static void RegisterNet(GC* gc, TRootEnvironment* env);
            static void RegisterSqlite(GC* gc,TRootEnvironment* env);
            static void RegisterVM(GC* gc,TRootEnvironment* env);
            static void RegisterDictionary(GC* gc, TRootEnvironment* env);
            static void RegisterJson(GC* gc, TRootEnvironment* env);
            static void RegisterCrypto(GC* gc,TRootEnvironment* env);
            static void RegisterRoot(GC* gc, TRootEnvironment* env);
            static void RegisterPath(GC* gc, TRootEnvironment* env);
            static void RegisterOGC(GC* gc, TRootEnvironment* env);
            static void RegisterEnv(GC* gc, TRootEnvironment* env);
            static void RegisterProcess(GC* gc, TRootEnvironment* env);
            static void RegisterTime(GC* gc, TRootEnvironment* env);

    };

    class TSubEnvironment : public TEnvironment
    {
        TEnvironment* env;
        TDictionary* dict;
        public:
            static TSubEnvironment* Create(GCList* gc,TEnvironment* env,TDictionary* dict);
            static TSubEnvironment* Create(GCList& gc,TEnvironment* env,TDictionary* dict);
            TSubEnvironment(TEnvironment* env,TDictionary* dict);
            bool HasVariable(std::string key);
            bool HasVariableRecurse(std::string key);
            TObject GetVariable(std::string key);
            void SetVariable(std::string key, TObject value);
            void DeclareVariable(std::string key, TObject value);
            TRootEnvironment* GetRootEnvironment();
            TEnvironment* GetParentEnvironment();
            TDictionary* GetDictionary();
            void Mark();
    };

    
    TDictionary* CreateThread(GCList& ls, TCallable* callable,bool detached);

    class TArgWrapper : public TCallable
    {
        public:
            TCallable* callable;
            static TArgWrapper* Create(GCList& ls, TCallable* callable);
            static TArgWrapper* Create(GCList* ls, TCallable* callable);
            TObject Call(GCList& ls,std::vector<TObject> args);
            void Mark();
    };

    class TExternalMethod : public TCallable
    {
        std::function<TObject(GCList& ls, std::vector<TObject> args)> cb;
        std::function<void()> destroy;
    
        public: 
            std::vector<std::string> args;
            std::vector<TObject> watch;
            TExternalMethod(std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::string documentation, std::vector<std::string> argNames,std::function<void()> destroy);
            static TExternalMethod* Create(GCList& ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);
            static TExternalMethod* Create(GCList* ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb,std::function<void()> destroy);

            static TExternalMethod* Create(GCList& ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            static TExternalMethod* Create(GCList* ls,std::string documentation, std::vector<std::string> argNames,std::function<TObject(GCList& ls, std::vector<TObject> args)> cb);
            TObject Call(GCList& ls, std::vector<TObject> args);

            ~TExternalMethod();
            void Mark()
            {
                if(this->marked) return;
                this->marked=true;
                for(auto item : watch)
                    GC::Mark(item);
                    GC::Mark(this->tag);
            }
            
    };
    
    class TEnumerator : public THeapObject
    {
        public:
            virtual bool MoveNext(GC* ls)=0;
            virtual TObject GetCurrent(GCList& ls)=0;
            static TEnumerator* CreateFromObject(GCList& ls, TObject obj);
    };

    class TCustomEnumerator : public TEnumerator {
        public:
            TDictionary* dict;
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            void Mark();
            static TCustomEnumerator* Create(GCList& ls,TDictionary* dict);
            static TCustomEnumerator* Create(GCList* ls,TDictionary* dict);
            
    };

    class TYieldEnumerator : public TEnumerator
    {
        bool hasStarted;
        TObject enumerator;
        TObject current;
        public:
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            void Mark();
            static TYieldEnumerator* Create(GCList& ls,TObject v);
            static TYieldEnumerator* Create(GCList* ls,TObject v);
            
    };
    
    class TStringEnumerator : public TEnumerator 
    {
        bool hasStarted;
        size_t index;
        std::string str;
        public:
            static TStringEnumerator* Create(GCList& ls,std::string str);
            static TStringEnumerator* Create(GCList* ls,std::string str);
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
    };
    class TListEnumerator : public TEnumerator 
    {
        int64_t index;
        TList* ls;
        public:
            static TListEnumerator* Create(GCList& ls, TList* list);
            static TListEnumerator* Create(GCList* ls, TList* list);
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            void Mark();
    };
    class TDynamicList;
    class TDynamicDictionary;
    class TDynamicListEnumerator : public TEnumerator 
    {
        int64_t index;
        TDynamicList* ls;
        public:
            static TDynamicListEnumerator* Create(GCList& ls, TDynamicList* list);
            static TDynamicListEnumerator* Create(GCList* ls, TDynamicList* list);
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            void Mark();
    };
    class TVFSPathEnumerator : public TEnumerator
    {
        Tesses::Framework::Filesystem::VFSPathEnumerator enumerator;
        public:
            static TVFSPathEnumerator* Create(GCList& ls, Tesses::Framework::Filesystem::VFSPathEnumerator list);
            static TVFSPathEnumerator* Create(GCList* ls, Tesses::Framework::Filesystem::VFSPathEnumerator list);
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            
    };


    class TStreamHeapObject : public THeapObject
    {
        public:
            Tesses::Framework::Streams::Stream* stream;

            static TStreamHeapObject* Create(GCList& ls, Tesses::Framework::Streams::Stream* strm);
            static TStreamHeapObject* Create(GCList* ls, Tesses::Framework::Streams::Stream* strm);
            ~TStreamHeapObject();
            void Close();

    };

    class TVFSHeapObject : public THeapObject
    {
        public:
            Tesses::Framework::Filesystem::VFS* vfs;
            static TVFSHeapObject* Create(GCList& ls, Tesses::Framework::Filesystem::VFS* vfs);
            static TVFSHeapObject* Create(GCList* ls, Tesses::Framework::Filesystem::VFS* vfs);
            ~TVFSHeapObject();
            void Close();
    };

    class TServerHeapObject : public THeapObject
    {
        public:
            Tesses::Framework::Http::IHttpServer* server;
            static TServerHeapObject* Create(GCList& ls, Tesses::Framework::Http::IHttpServer* vfs);
            static TServerHeapObject* Create(GCList* ls, Tesses::Framework::Http::IHttpServer* vfs);
            ~TServerHeapObject();
            void Close();
    };
    class TObjectVFS : public Tesses::Framework::Filesystem::VFS
    {
        public:
            TObject obj;
            GCList* ls;
            TObjectVFS(GC* gc, TObject obj);
            Tesses::Framework::Streams::Stream* OpenFile(Tesses::Framework::Filesystem::VFSPath path, std::string mode);
            void CreateDirectory(Tesses::Framework::Filesystem::VFSPath path);
            void DeleteDirectory(Tesses::Framework::Filesystem::VFSPath path);
            bool RegularFileExists(Tesses::Framework::Filesystem::VFSPath path);
            bool SymlinkExists(Tesses::Framework::Filesystem::VFSPath path);
            bool CharacterDeviceExists(Tesses::Framework::Filesystem::VFSPath path);
            bool BlockDeviceExists(Tesses::Framework::Filesystem::VFSPath path);
            bool SocketFileExists(Tesses::Framework::Filesystem::VFSPath path);
            bool FIFOFileExists(Tesses::Framework::Filesystem::VFSPath path);
            bool FileExists(Tesses::Framework::Filesystem::VFSPath path);
            bool SpecialFileExists(Tesses::Framework::Filesystem::VFSPath path);
            void CreateSymlink(Tesses::Framework::Filesystem::VFSPath existingFile, Tesses::Framework::Filesystem::VFSPath symlinkFile);
            void CreateHardlink(Tesses::Framework::Filesystem::VFSPath existingFile, Tesses::Framework::Filesystem::VFSPath newName);
            bool DirectoryExists(Tesses::Framework::Filesystem::VFSPath path);
            void DeleteFile(Tesses::Framework::Filesystem::VFSPath path);
            void DeleteDirectoryRecurse(Tesses::Framework::Filesystem::VFSPath path);
            Tesses::Framework::Filesystem::VFSPathEnumerator EnumeratePaths(Tesses::Framework::Filesystem::VFSPath path);
            void MoveFile(Tesses::Framework::Filesystem::VFSPath src, Tesses::Framework::Filesystem::VFSPath dest);
            void MoveDirectory(Tesses::Framework::Filesystem::VFSPath src, Tesses::Framework::Filesystem::VFSPath dest);
            Tesses::Framework::Filesystem::VFSPath ReadLink(Tesses::Framework::Filesystem::VFSPath path);
            std::string VFSPathToSystem(Tesses::Framework::Filesystem::VFSPath path);
            Tesses::Framework::Filesystem::VFSPath SystemToVFSPath(std::string path);
            void GetDate(Tesses::Framework::Filesystem::VFSPath path, time_t& lastWrite, time_t& lastAccess);
            void SetDate(Tesses::Framework::Filesystem::VFSPath path, time_t lastWrite, time_t lastAccess);
            ~TObjectVFS();
    };

    class TObjectStream : public Tesses::Framework::Streams::Stream
    {
        int64_t _GetPosInternal();
        public:
            TObject obj;
            GCList* ls;
            TObjectStream(GC* gc, TObject obj);
            bool EndOfStream();
            size_t Read(uint8_t* buff, size_t sz);
            size_t Write(const uint8_t* buff, size_t sz);
            bool CanRead();
            bool CanWrite();
            bool CanSeek();
            int64_t GetPosition();
            int64_t GetLength();
            void Flush();
            void Seek(int64_t pos, Tesses::Framework::Streams::SeekOrigin whence);
            ~TObjectStream();
    };

    class TObjectHttpServer : public Tesses::Framework::Http::IHttpServer
    {
        public:
            TObject obj;
            GCList* ls;
            TObjectHttpServer(GC* gc,TObject obj);
            bool Handle(Tesses::Framework::Http::ServerContext& ctx);
            ~TObjectHttpServer();
    };
    

    class TDictionaryEnumerator : public TEnumerator 
    {
        bool hasStarted;
        std::map<std::string, Tesses::CrossLang::TObject>::iterator ittr;
        TDictionary* dict;
        public:
            static TDictionaryEnumerator* Create(GCList& ls, TDictionary* dict);
            static TDictionaryEnumerator* Create(GCList* ls, TDictionary* dict);
            bool MoveNext(GC* ls);
            TObject GetCurrent(GCList& ls);
            void Mark();
    };

    class TClosure : public TCallable
    {
        public:
            static TClosure* Create(GCList& ls,TEnvironment* env,TFile* file,uint32_t chunkId,bool ownScope=true);
            static TClosure* Create(GCList* ls,TEnvironment* env,TFile* file,uint32_t chunkId,bool ownScope=true);
            bool ownScope;
            TFileChunk* closure;
            TFile* file;
            uint32_t chunkId;
            TEnvironment* env;
            TObject Call(GCList& ls,std::vector<TObject> args);
            void Mark();
    };
    class TDynamicList : public THeapObject
    {
        public:
            TCallable* cb;
            static TDynamicList* Create(GCList& ls,TCallable* callable);
            static TDynamicList* Create(GCList* ls,TCallable* callable);

            void Mark();

            int64_t Count(GCList& ls);

            TObject GetAt(GCList& ls, int64_t index);

            void SetAt(GCList& ls, int64_t index, TObject val);

            ~TDynamicList();
    };
    class TDynamicDictionary : public THeapObject
    {
        public:
            TCallable* cb;

            static TDynamicDictionary* Create(GCList& ls,TCallable* callable);
            static TDynamicDictionary* Create(GCList* ls,TCallable* callable);

            void Mark();

            TObject GetField(GCList& ls, std::string key);

            void SetField(GCList& ls, std::string key, TObject value);

            TObject CallMethod(GCList& ls, std::string name, std::vector<TObject> args);      

            bool MethodExists(GCList& ls, std::string name);

            TEnumerator* GetEnumerator(GCList& ls);


            ~TDynamicDictionary();
    };

    
     class CallStackEntry : public THeapObject
    {
        public:
            static CallStackEntry* Create(GCList* ls);
            static CallStackEntry* Create(GCList& ls);
            std::vector<TObject> stack;
            TEnvironment* env;
            TClosure* callable;
            uint32_t ip;
            uint32_t scopes; 
            bool mustReturn;
            
            void Mark();
            void Push(GC* gc,TObject v);
            TObject Pop(GCList& gcl);

            TObject Resume(GCList& ls);
    };
    extern thread_local CallStackEntry* current_function;
    
    class InterperterThread : public THeapObject
    {
        private:
            bool InvokeTwo(GCList& ls,TObject fn, TObject left, TObject right);
            bool InvokeOne(GCList& ls,TObject fn, TObject arg);
            bool InvokeMethod(GCList& ls, TObject fn, TObject instance, std::vector<TObject> args);
            bool ExecuteMethod2(GC* gc, TObject instance, std::string key, std::vector<TObject> args);
        protected:
            static void* ThreadCallback(void* ptr);
            bool Add(GC* gc);
            bool Sub(GC* gc);
            bool Times(GC* gc);
            bool Divide(GC* gc);
            bool Mod(GC* gc);
            bool Neg(GC* gc);
            bool Lt(GC* gc);
            bool Gt(GC* gc);
            bool Lte(GC* gc);
            bool Gte(GC* gc);
            bool Eq(GC* gc);
            bool NEq(GC* gc);
            bool LShift(GC* gc);
            bool RShift(GC* gc);
            bool BOr(GC* gc);
            bool XOr(GC* gc);
            bool LNot(GC* gc);
            bool BNot(GC* gc);
            bool BAnd(GC* gc);
            bool ExecuteFunction(GC* gc);
            bool ExecuteMethod(GC* gc);
            bool GetVariable(GC* gc);
            bool SetVariable(GC* gc);
            bool GetField(GC* gc);
            bool SetField(GC* gc);
            bool GetArray(GC* gc);
            bool SetArray(GC* gc);
            bool DeclareVariable(GC* gc);
            bool PushLong(GC* gc);
            bool PushDouble(GC* gc);
            bool PushChar(GC* gc);
            bool PushString(GC* gc);
            bool PushClosure(GC* gc);
            bool PushScopelessClosure(GC* gc);
            bool PushResource(GC* gc);
            bool Illegal(GC* gc);
            bool Throw(GC* gc);
            bool Yield(GC* gc);
            bool Jump(GC* gc);
            bool JumpConditional(GC* gc);
            bool JumpUndefined(GC* gc);
            bool Defer(GC* gc);
            bool TryCatch(GC* gc);
            bool Return(GC* gc);
            bool ScopeBegin(GC* gc);
            bool ScopeEnd(GC* gc);
            bool ScopeEndTimes(GC* gc);
            bool PushTrue(GC* gc);
            bool PushFalse(GC* gc);
            bool PushNull(GC* gc);
            bool PushUndefined(GC* gc);
            bool CreateDictionary(GC* gc);
            bool CreateArray(GC* gc);
            bool AppendList(GC* gc);
            bool AppendDictionary(GC* gc);
            bool PushRootPath(GC* gc);
            bool PushRelativePath(GC* gc);
            bool Pop(GC* gc);
            bool Dup(GC* gc);
            bool Nop(GC* gc);
            bool Breakpoint(GC* gc);
        public:
            static InterperterThread* Create(GCList* ls);
            static InterperterThread* Create(GCList& ls);
            std::vector<CallStackEntry*> call_stack_entries;
            virtual void Execute(GC* gc);
            void AddCallStackEntry(GCList& ls,TClosure* closure, std::vector<TObject> args);
            void Mark();
    };

    


    class VMException : public std::exception {
        
        std::string error_message;
        public:
           
            VMException(std::string ex)
            {
                error_message = "VMException: ";
                error_message.append(ex);
            }

           
            
        const char * what() const noexcept override
        {
            return error_message.c_str();
        }
    };
    class TNative : public THeapObject
    {
        std::atomic<bool> destroyed;
        void* ptr;
        std::function<void(void*)> destroy;
        public:
            TObject other;    
            TNative(void* ptr,std::function<void(void*)> destroy);
            bool GetDestroyed();
            void* GetPointer();
            void Destroy();
            void Mark();
            static TNative* Create(GCList& ls, void* ptr,std::function<void(void*)> destroy);
            static TNative* Create(GCList* ls, void* ptr,std::function<void(void*)> destroy);
            ~TNative();
    };

    class ThreadHandle : public THeapObject {
        public:
            Tesses::Framework::Threading::Thread* thrd;
            std::atomic<bool> hasInit;
            std::atomic<bool> hasReturned;
            std::atomic<bool> detached;
            TCallable* callable;
            TObject returnValue;
            GC* gc;
            

            void Mark()
            {
                if(this->marked) return;
                this->marked = true;
                callable->Mark();
                GC::Mark(returnValue);
            }
            ~ThreadHandle()
            {
                if(this->detached)
                {
                    this->thrd->Join();
                    delete this->thrd;
                }
                
            }
    };

   
    TObject ExecuteFunction(GCList& ls,TCallable* callable,std::vector<TObject> args);
    class VMByteCodeException : public std::exception
    {
        std::string lastErrorText;
        std::shared_ptr<GCList> gcList;
        
        public:

            TObject exception;
            
            VMByteCodeException()
            {
            }

            VMByteCodeException(GC* gc,TObject obj)
            {
                gcList = std::make_shared<GCList>(gc);
                gcList.get()->Add(obj);
                this->exception = obj;   
                UpdateError();
            }

            void UpdateError()
            {
                lastErrorText = ToString(gcList.get()->GetGC(),exception);

            }

            const char * what() const noexcept override
            {
                return lastErrorText.c_str();
            }

    };

    class SyntaxException : public std::exception
    {
        std::string msg={};
        public:
            SyntaxException(LexTokenLineInfo lineInfo, std::string message)
            {
                msg.append("in file: ");
                msg.append(lineInfo.filename);
                msg.append(":");
                msg.append(std::to_string(lineInfo.line));
                msg.append(":");
                msg.append(std::to_string(lineInfo.column));
                msg.append(":");
                msg.append(std::to_string(lineInfo.offset));
                msg.append(": ");
                msg.append(message);
            }
            const char * what() const noexcept override
            {
                return msg.c_str();
            }
    };

    
    template<typename T>
    bool GetObject(TObject& obj, T& res)
    {
        if(!std::holds_alternative<T>(obj)) return false;
        res = std::get<T>(obj);
        return true;
    }
    template<typename T>
    bool GetArgument(std::vector<TObject>& args, size_t index, T& obj)
    {
        if( index >= args.size()) return false;
        return GetObject(args[index], obj);
    }

    template<typename T>
    bool GetObjectHeap(TObject& obj, T& res)
    {
        THeapObjectHolder h;
        if(!GetObject<THeapObjectHolder>(obj,h)) return false;
        auto v = dynamic_cast<T>(h.obj);
        if(v == nullptr) return false;
        res = v;
        return true;
    }

    template<typename T>
    bool GetArgumentHeap(std::vector<TObject>& args, size_t index, T& obj)
    {
        if( index >= args.size()) return false;
        return GetObjectHeap(args[index], obj);
    }
    bool GetObjectAsPath(TObject& obj, Tesses::Framework::Filesystem::VFSPath& path, bool allowString=true);
    bool GetArgumentAsPath(std::vector<TObject>& args, size_t index, Tesses::Framework::Filesystem::VFSPath& path,bool allowString=true);
    bool ToBool(TObject obj);
    bool Equals(GC* gc, TObject left, TObject right);
    typedef void (*PluginFunction)(GC* gc,TRootEnvironment* env);
    #if !defined(_WIN32)
    #define DLLEXPORT
    #else
    #define DLLEXPORT __declspec(dllexport)
    #endif
    #define CROSSLANG_PLUGIN(plugin) DLLEXPORT extern "C" void CrossLangPluginInit(GC* gc, TRootEnvironment* env) { plugin(gc,env); }

    void LoadPlugin(GC* gc, TRootEnvironment* env, Tesses::Framework::Filesystem::VFSPath sharedObjectPath);
    std::string Json_Encode(TObject o,bool indent=false);
    TObject Json_Decode(GCList ls,std::string str);
    //DO NOT USE DIRECTLY
    class SharedPtrTObject {
        GCList* ls;
        TObject o;
        public:
            SharedPtrTObject(GC* gc, TObject o);
            TObject& GetObject();
            GC* GetGC();
            ~SharedPtrTObject();
    };
    using MarkedTObject = std::shared_ptr<SharedPtrTObject>;

    MarkedTObject CreateMarkedTObject(GC* gc, TObject o);
    MarkedTObject CreateMarkedTObject(GC& gc, TObject o);
    MarkedTObject CreateMarkedTObject(GCList* gc, TObject o);
    MarkedTObject CreateMarkedTObject(GCList& gc, TObject o);
};
