
#ifndef PORYGONLANG_SCRIPT_HPP
#define PORYGONLANG_SCRIPT_HPP

#include <utility>
#include <string>
#include <map>
#include "Diagnostics/DiagnosticsHolder.hpp"
#include "Binder/BoundStatements/BoundStatement.hpp"
#include "Evaluator/Evaluator.hpp"
#include "Evaluator/EvalValues/EvalValue.hpp"
#include "Utilities/HashedString.hpp"
#include "ScriptOptions.hpp"

using namespace std;
using namespace Porygon::Evaluation;

namespace Porygon{
    class Script {
        Evaluator* _evaluator;
        map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
        unordered_map<Utilities::HashedString, shared_ptr<ScriptType>>* _scriptTypes;
        shared_ptr<Binder::BoundScriptStatement> _boundScript;
        shared_ptr<const ScriptType> _returnType = nullptr;
        ScriptOptions* _scriptOptions;

        explicit Script(const u16string&);
        Script(shared_ptr<BoundScriptStatement> boundScript, shared_ptr<Diagnostics::DiagnosticsHolder> diagnostics);
        void Parse(const u16string& script);
    public:
        shared_ptr<Diagnostics::DiagnosticsHolder> Diagnostics;

        static Script* Create(const u16string& script);
        static Script* Create(const string& script);
        static Script* Clone(const Script* script);

        ~Script();

        inline shared_ptr<const ScriptType> GetReturnType(){
            return _returnType;
        }

        inline void SetReturnType(const shared_ptr<const ScriptType>& t){
            _returnType  = t;
        }

        [[nodiscard]] inline const ScriptOptions* GetScriptOptions() const{
            if (_scriptOptions == nullptr){
                return ScriptOptions::GetDefaultScriptOptions();
            }
            return _scriptOptions;
        }

        EvalValuePointer Evaluate();

        const EvalValue* GetVariable(const u16string& key);
        bool HasVariable(const u16string& key);
        shared_ptr<const ScriptType> GetVariableType(const Utilities::HashedString& key){
            return _scriptTypes->at(key);
        }

        const EvalValue* CallFunction(const u16string& key, const vector<EvalValue*>& variables);
        bool HasFunction(const u16string& key);

        const map<Utilities::HashedString, EvalValuePointer>* GetScriptVariables(){
            return _scriptVariables;
        }
    };

}


#endif //PORYGONLANG_SCRIPT_HPP
