
#ifndef PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP
#define PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP

#include <memory>
#include <utility>
#include <unordered_map>
#include "../../ScriptTypes/ScriptType.hpp"
#include "EvalValue.hpp"
#include "../../Binder/BoundStatements/BoundStatement.hpp"
#include "../EvaluationScope/EvaluationScope.hpp"
#include "../../ScriptTypes/FunctionScriptType.hpp"

using namespace std;

namespace Porygon::Evaluation {
    class GenericFunctionOption{
    public:
        virtual ~GenericFunctionOption() = default;
    };

    class EvaluationScriptFunctionOption : public GenericFunctionOption{
        const std::shared_ptr<const BoundBlockStatement> _innerBlock;
        const std::shared_ptr<EvaluationScope> _scope;

    public:
        EvaluationScriptFunctionOption(shared_ptr<const BoundBlockStatement>  innerBlock, shared_ptr<EvaluationScope>  scope)
                : GenericFunctionOption(), _innerBlock(std::move(innerBlock)), _scope(std::move(scope)) {
        }
        ~EvaluationScriptFunctionOption() final{};

        [[nodiscard]] inline std::shared_ptr<const BoundBlockStatement> GetInnerBlock() const {
            return _innerBlock;
        }

        [[nodiscard]] inline const std::shared_ptr<EvaluationScope> &GetScope() const {
            return _scope;
        }
    };

    class GenericFunctionEvalValue : public EvalValue{
    protected:
        const shared_ptr<const GenericFunctionScriptType> _type;
        const size_t _hash;
        shared_ptr<vector<shared_ptr<GenericFunctionOption>>> _options;
        GenericFunctionEvalValue(shared_ptr<const GenericFunctionScriptType> type, size_t hash,
                shared_ptr<vector<shared_ptr<GenericFunctionOption>>> options)
        :_type(std::move(type)), _hash(hash), _options(std::move(options))
        {}
    public:
        GenericFunctionEvalValue(shared_ptr<const GenericFunctionScriptType> type, size_t hash)
            : _type(move(type)),
              _hash(hash),
              _options(make_shared<vector<shared_ptr<GenericFunctionOption>>>()){
        }

        GenericFunctionEvalValue(const GenericFunctionEvalValue& _) = delete;
        GenericFunctionEvalValue() = delete;
        GenericFunctionEvalValue& operator =(GenericFunctionEvalValue v) = delete;

        [[nodiscard]]
        EvalValue* Clone() const final {
            return new GenericFunctionEvalValue(_type, _hash, _options);
        }

        inline void RegisterOption(GenericFunctionOption* option) const{
            _options->push_back(shared_ptr<GenericFunctionOption>(option));
        }

        [[nodiscard]]
        inline std::shared_ptr<const GenericFunctionScriptType> GetType() const {
            return _type;
        }

        inline TypeClass GetTypeClass() const final {
            return TypeClass::Function;
        }

        bool operator==(const EvalValue *b) const final {
            if (b->GetTypeClass() != TypeClass::Function)
                return false;
            return this->_hash == ((GenericFunctionEvalValue *) b)->_hash;
        };

        inline std::size_t GetHashCode() const final {
            return _hash;
        }

        [[nodiscard]]
        inline shared_ptr<const GenericFunctionOption> GetOption(const size_t id) const{
            return this->_options->at(id);
        }
    };
}


#endif //PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP
