
#ifndef PORYGONLANG_TABLEEVALVALUE_HPP
#define PORYGONLANG_TABLEEVALVALUE_HPP
#include <utility>
#include <map>
#include "EvalValue.hpp"
#include "../../Utilities/Random.hpp"
#include "../EvalValuePointer.hpp"

using namespace std;

namespace Porygon::Evaluation {
    class TableEvalValue : public EvalValue {
        const shared_ptr<map<Utilities::HashedString, EvalValuePointer>> _table;
        const size_t _hash;

        explicit TableEvalValue(shared_ptr<map<Utilities::HashedString, EvalValuePointer>>  table, size_t hash)
        :  _table(std::move(table)),
           _hash(hash)
        {
        }

    public:
        explicit TableEvalValue(shared_ptr<map<Utilities::HashedString, EvalValuePointer>> table) :
            _table(std::move(table)),
            _hash(Utilities::Random::Get())
        {
        }

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

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

        [[nodiscard]]
        inline bool operator==(const EvalValue *b) const final {
            return this->_hash == b->GetHashCode();
        }

        [[nodiscard]]
        inline EvalValue* Clone() const final {
            return new TableEvalValue(_table, _hash);
        }

        [[nodiscard]]
        inline EvalValue* IndexValue(const EvalValue *val) const final {
            const auto stringKey = val->EvaluateString();
            return this->_table->at(Utilities::HashedString::CreateLookup(stringKey))->Clone();
        }

        [[nodiscard]]
        inline EvalValue* IndexValue(uint32_t hash) const final {
            return this->_table->at(Utilities::HashedString::CreateLookup(hash))->Clone();
        }

        [[nodiscard]]
        inline const EvalValue* IndexValue(const char *val) const {
            auto hash = Utilities::HashedString::ConstHash(val);
            return this->_table->at(Utilities::HashedString::CreateLookup(hash)).Clone();
        }

        inline void SetIndexValue(const EvalValue *key, const EvalValue* value) const final {
            auto hash = key->GetHashCode();
            this->_table->at(Utilities::HashedString::CreateLookup(hash)).ClearAssign(value);
        }

        [[nodiscard]]
        Iterator * GetKeyIterator() const final;

        [[nodiscard]]
        inline _Rb_tree_const_iterator<pair<const Utilities::HashedString, EvalValuePointer>> GetTableIterator() const{
            return _table->cbegin();
        };
        [[nodiscard]]
        inline _Rb_tree_const_iterator<pair<const Utilities::HashedString, EvalValuePointer>> GetTableIteratorEnd() const{
            return _table->cend();
        };
    };
}

#endif //PORYGONLANG_TABLEEVALVALUE_HPP
