
#ifndef PORYGONLANG_EVALUATOR_HPP
#define PORYGONLANG_EVALUATOR_HPP

#include <string>
#include "../Binder/BoundStatements/BoundStatement.hpp"
#include "EvalValues/EvalValue.hpp"
#include "EvalValues/NumericEvalValue.hpp"
#include "EvalValues/StringEvalValue.hpp"
#include "EvalValues/StringEvalValue.hpp"
#include "EvalValues/ScriptFunctionEvalValue.hpp"
#include "EvaluationScope/EvaluationScope.hpp"
#include "EvalValuePointer.hpp"
#include "../ScriptOptions.hpp"

using namespace std;

namespace Porygon::Evaluation{
    class Evaluator {
        EvalValuePointer _returnValue;
        map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
        bool _hasReturned;
        bool _hasBroken;

        const Porygon::ScriptOptions* _scriptOptions;
        shared_ptr<EvaluationScope> _evaluationScope;

        void EvaluateStatement(const BoundStatement* statement);
        void EvaluateBlockStatement(const BoundBlockStatement *statement);
        void EvaluateExpressionStatement(const BoundExpressionStatement* statement);
        void EvaluateAssignmentStatement(const BoundAssignmentStatement* statement);
        void EvaluateIndexAssignmentStatement(const BoundIndexAssignmentStatement* statement);
        void EvaluateFunctionDeclarationStatement(const BoundFunctionDeclarationStatement *statement);
        void EvaluateReturnStatement(const BoundReturnStatement *statement);
        void EvaluateConditionalStatement(const BoundConditionalStatement *statement);
        void EvaluateNumericalForStatement(const BoundNumericalForStatement *statement);
        void EvaluateGenericForStatement(const BoundGenericForStatement *statement);
        void EvaluateWhileStatement(const BoundWhileStatement *statement);

        EvalValuePointer EvaluateExpression(const BoundExpression *expression);
        EvalValuePointer EvaluateBinary(const BoundBinaryExpression *expression);
        EvalValuePointer EvaluateUnary(const BoundUnaryExpression *expression);

        EvalValuePointer EvaluateFunctionCallExpression(const BoundExpression *expression);
        EvalValuePointer EvaluateIndexExpression(const BoundExpression *expression);
        EvalValuePointer EvaluatePeriodIndexExpression(const BoundExpression *expression);
        EvalValuePointer EvaluateNumericTableExpression(const BoundExpression *expression);
        EvalValuePointer EvaluateComplexTableExpression(const BoundExpression *expression);

        EvalValuePointer EvaluateRequireExpression(const BoundExpression* expression);

        EvalValuePointer GetVariable(const BoundVariableExpression *expression);
    public:
        explicit Evaluator(map<Utilities::HashedString, EvalValuePointer>* scriptVariables, const ScriptOptions* options)
            : _scriptVariables(scriptVariables), _hasReturned(false), _hasBroken(false), _returnValue(nullptr),
            _evaluationScope(nullptr), _scriptOptions(options){
        }

        const EvalValue* Evaluate(const BoundScriptStatement* statement);
        const EvalValue* EvaluateFunction(const GenericFunctionEvalValue *function,
                                                     const vector<EvalValue *> &parameters);

    };
}



#endif //PORYGONLANG_EVALUATOR_HPP
