//---------------------------------------------------------------------------
/*
        TJS2 Script Engine
        Copyright (C) 2000 W.Dee <dee@kikyou.info> and contributors

        See details of license at "license.txt"
*/
//---------------------------------------------------------------------------
// Script Block Management
//---------------------------------------------------------------------------
#ifndef tjsScriptBlockH
#define tjsScriptBlockH

#include "tjsInterface.h"
#include "tjsInterCodeGen.h"
#include "tjsLex.h"
#include "tjs.h"

#include <list>

namespace TJS {
    //---------------------------------------------------------------------------
    // tTJSScriptBlock - a class for managing the script block
    //---------------------------------------------------------------------------
    class tTJS;
    class tTJSInterCodeContext;
    class tTJSScriptBlock {
    public:
        tTJSScriptBlock(tTJS *owner);
        virtual ~tTJSScriptBlock();

        // for Bytecode
        static const int BYTECODE_FILE_TAG_SIZE = 8;

    private:
        tTJS *Owner;
        tjs_int RefCount;
        tjs_char *Script;
        tjs_char *Name;
        tjs_int LineOffset;

        std::list<tTJSInterCodeContext *> InterCodeContextList;

        std::stack<tTJSInterCodeContext *> ContextStack;

        tTJSLexicalAnalyzer *LexicalAnalyzer;

        tTJSInterCodeContext *InterCodeContext;

        std::vector<tjs_int> LineVector;
        std::vector<tjs_int> LineLengthVector;

        tTJSInterCodeContext *TopLevelContext;

        tTJSString FirstError;
        tjs_int FirstErrorPos;

        bool UsingPreProcessor;

    public:
        tjs_int CompileErrorCount;

        [[nodiscard]] tTJS *GetTJS() const { return Owner; }

        void AddRef();
        void Release();

        void Add(tTJSInterCodeContext *cntx);
        void Remove(tTJSInterCodeContext *cntx);

        [[nodiscard]] tjs_uint GetContextCount() const {
            return (tjs_uint)InterCodeContextList.size();
        }
        [[nodiscard]] tjs_uint GetTotalVMCodeSize()
            const; // returns in VM word size ( 1 word = 32bit )
        [[nodiscard]] tjs_uint
        GetTotalVMDataSize() const; // returns in tTJSVariant count

        [[nodiscard]] bool IsReusable() const {
            return GetContextCount() == 1 && TopLevelContext != nullptr &&
                !UsingPreProcessor;
        }

        const tjs_char *GetLine(tjs_int line, tjs_int *linelength) const;
        [[nodiscard]] tjs_int SrcPosToLine(tjs_int pos) const;
        [[nodiscard]] tjs_int LineToSrcPos(tjs_int line) const;

        [[nodiscard]] ttstr GetLineDescriptionString(tjs_int pos) const;

        [[nodiscard]] const tjs_char *GetScript() const { return Script; }

        void PushContextStack(const tjs_char *name, tTJSContextType type);
        void PopContextStack();
        void Parse(const tjs_char *script, bool isexpr, bool resultneeded);

        void SetFirstError(const tjs_char *error, tjs_int pos);

        tTJSLexicalAnalyzer *GetLexicalAnalyzer() { return LexicalAnalyzer; }
        tTJSInterCodeContext *GetCurrentContext() { return InterCodeContext; }

        [[nodiscard]] const tjs_char *GetName() const { return Name; }
        void SetName(const tjs_char *name, tjs_int lineofs);
        [[nodiscard]] ttstr GetNameInfo() const;

        [[nodiscard]] tjs_int GetLineOffset() const { return LineOffset; }

        void NotifyUsingPreProcessor() { UsingPreProcessor = true; }

        void Dump() const;
        void Dump(tTJSBinaryStream *stream) const;

        void ExportByteCode(bool outputdebug, class tTJSBinaryStream *output);

    private:
        static void ConsoleOutput(const tjs_char *msg, void *data);

        // for Bytecode
        static const int BYTECODE_TAG_SIZE = 4;
        static const int BYTECODE_CHUNK_SIZE_LEN = 4;
        static const tjs_uint8 BYTECODE_FILE_TAG[BYTECODE_FILE_TAG_SIZE];
        static const tjs_uint8 BYTECODE_CODE_TAG[BYTECODE_TAG_SIZE];
        static const tjs_uint8 BYTECODE_OBJ_TAG[BYTECODE_TAG_SIZE];
        static const tjs_uint8 BYTECODE_DATA_TAG[BYTECODE_TAG_SIZE];

        static inline void Write4Byte(tjs_uint8 output[4], int value) {
            output[0] = ((tjs_uint8)((value >> 0) & 0xff));
            output[1] = ((tjs_uint8)((value >> 8) & 0xff));
            output[2] = ((tjs_uint8)((value >> 16) & 0xff));
            output[3] = ((tjs_uint8)((value >> 24) & 0xff));
        }

    public:
        static void (*GetConsoleOutput())(const tjs_char *msg, void *data) {
            return ConsoleOutput;
        }

        void SetText(tTJSVariant *result, const tjs_char *text,
                     iTJSDispatch2 *context, bool isexpression);

        void ExecuteTopLevelScript(tTJSVariant *result, iTJSDispatch2 *context);

        // for Bytecode
        tTJSScriptBlock(tTJS *owner, const tjs_char *name, tjs_int lineoffset);

        void SetObjects(tTJSInterCodeContext *toplevel,
                        std::vector<tTJSInterCodeContext *> &objs, int count) {
            TopLevelContext = toplevel;
            for(int i = 0; i < count; i++) {
                Add(objs[i]);
                if(objs[i] != toplevel) {
                    AddRef();
                }
                objs[i] = nullptr;
            }
        }
        void ExecuteTopLevel(tTJSVariant *result, iTJSDispatch2 *context);

        int GetCodeIndex(const tTJSInterCodeContext *ctx) const;

        void Compile(const tjs_char *text, bool isexpression,
                     bool isresultneeded, bool outputdebug,
                     tTJSBinaryStream *output);
        void TranslateCodeAddress(tjs_int32 *code, tjs_int32 codeSize);
    };
    //---------------------------------------------------------------------------
} // namespace TJS

#endif
