#ifndef PORYGONLANG_USERDATACOLLECTIONVALUE_HPP
#define PORYGONLANG_USERDATACOLLECTIONVALUE_HPP

#include <utility>

#include "UserDataCollectionType.hpp"
#include "../../Evaluator/EvalValues/EvalValue.hpp"
#include "../../Utilities/Random.hpp"

using namespace Porygon::Evaluation;

namespace Porygon::UserData {
    class UserDataCollectionHelper{
        void* _parentObject;
        const EvalValue* (*_get)(void*, const EvalValue*);
        void (*_set)(void*, const EvalValue* , const EvalValue*);
        Iterator* (*_getIterator)(void*);
    public:
        UserDataCollectionHelper(void* parentObject,
                                 const EvalValue* (*get)(void*, const EvalValue*),
                                 void (*set)(void*, const EvalValue*, const EvalValue*),
                                 Iterator* (*getIterator)(void*))
                                 : _parentObject(parentObject), _get(get), _set(set), _getIterator(getIterator){}

        const EvalValue* Get(const EvalValue* key) const{
            return _get(_parentObject, key);
        }

        void Set(const EvalValue* key, const EvalValue* value) const{
            _set(_parentObject, key, value);
        }

        [[nodiscard]] Iterator* GetIterator() const{
            return _getIterator(_parentObject);
        }
    };

    class UserDataCollectionValue : public Evaluation::EvalValue{
        shared_ptr<UserDataCollectionType> _type;
        shared_ptr<const UserDataCollectionHelper> _helper;
        const size_t _hash;
        UserDataCollectionValue(shared_ptr<UserDataCollectionType> type,
                                shared_ptr<const UserDataCollectionHelper> helper, size_t hash)
                : _type(std::move(type)), _helper(std::move(helper)), _hash(hash)
        {
        }
    public:

        UserDataCollectionValue(ScriptType* type, const UserDataCollectionHelper* helper)
                : _type(dynamic_cast<UserDataCollectionType*>(type)), _helper(helper), _hash(Utilities::Random::Get())
        {
        }


        [[nodiscard]] TypeClass GetTypeClass() const final{
            return TypeClass ::Table;
        }

        bool operator==(const EvalValue *b) const final{
            return b->GetHashCode() == _hash;
        }

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

        [[nodiscard]] std::size_t GetHashCode() const final{
            return _hash;
        }

        const EvalValue* IndexValue(const EvalValue *val) const final{
            return _helper->Get(val);
        }

        [[nodiscard]]
        Iterator * GetKeyIterator() const final{
            return _helper->GetIterator();
        }

        void SetIndexValue(const EvalValue *key, const EvalValue* value) const final{
            _helper->Set(key, value);
            delete value;
        }
    };
}


#endif //PORYGONLANG_USERDATACOLLECTIONVALUE_HPP
