
#ifndef PORYGONLANG_HASHEDSTRING_HPP
#define PORYGONLANG_HASHEDSTRING_HPP

#include <string>
#include <memory>

namespace Porygon::Utilities{
    class HashedString{
        const uint32_t _hash;
        const std::shared_ptr<const std::u16string> _string;

        explicit HashedString(const std::u16string& s) : _hash(ConstHash(s.c_str())),_string(nullptr){
        }

        explicit HashedString(uint32_t hash) : _hash(hash), _string(nullptr){
        }

    public:
        explicit HashedString(const std::u16string* s)
            : _hash(ConstHash(s->c_str())),
              _string(std::shared_ptr<const std::u16string>(s))
        {
        }
        inline static HashedString CreateLookup(const std::u16string& s){
            return HashedString(s);
        }
        inline static HashedString CreateLookup(uint32_t hash){
            return HashedString(hash);
        }

        HashedString(const HashedString& b) = default;

        inline static uint32_t constexpr ConstHash(char16_t const *input) {
            return *input ?
                   static_cast<uint32_t>(*input) + 33 * ConstHash(input + 1) :
                   5381;
        }

        inline static uint32_t constexpr ConstHash(char const *input) {
            return *input ?
                   static_cast<uint32_t>(*input) + 33 * ConstHash(input + 1) :
                   5381;
        }

        inline const uint32_t GetHash() const{
            return _hash;
        }

        inline const std::shared_ptr<const std::u16string> GetString() const{
            return _string;
        }

        inline bool operator==(const HashedString& b) const{
            return _hash == b._hash;
        }
        inline bool operator!=(const HashedString& b) const{
            return _hash != b._hash;
        }
        inline bool operator<(const HashedString& b) const{
            return _hash < b._hash;
        }
        inline bool operator>(const HashedString& b) const{
            return _hash > b._hash;
        }
        std::size_t operator()(const HashedString& k) const {
            return _hash;
        }
    };
}

namespace std {
    template <>
    struct hash<Porygon::Utilities::HashedString> {
        std::size_t operator()(const Porygon::Utilities::HashedString& k) const{
            return k.GetHash();
        }
    };
};


#endif //PORYGONLANG_HASHEDSTRING_HPP
