
#ifndef PORYGONLANG_STRINGEVALVALUE_HPP
#define PORYGONLANG_STRINGEVALVALUE_HPP

#include <string>
#include "EvalValue.hpp"
#include "../../Utilities/HashedString.hpp"

using namespace std;

namespace Porygon::Evaluation {
    class StringEvalValue : public EvalValue {
        shared_ptr<u16string> _value;
        size_t _hash;
        explicit StringEvalValue(shared_ptr<u16string> s) : _value(move(s)){
            _hash = Utilities::HashedString::ConstHash(_value->c_str());
        }
    public:
        explicit StringEvalValue(const u16string& s) : _value(make_shared<u16string>(s)){
            _hash = Utilities::HashedString::ConstHash(_value->c_str());
        }


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

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

        [[nodiscard]]
        inline u16string EvaluateString() const final {
            return *_value.get();
        }

        [[nodiscard]]
        inline EvalValue* Clone() const final {
            return new StringEvalValue(_value);
        }

        EvalValue* IndexValue(const EvalValue *val) const final {
            // Porygon is 1-indexed, so we convert to that.
            auto l = val->EvaluateInteger() - 1;
            return new StringEvalValue(u16string(1, (*_value)[l]));
        }

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

        [[nodiscard]]
        inline EvalValue* BinaryOperation(Binder::BoundBinaryOperation operation, const EvalValue* b) const final{
            if (operation != Binder::BoundBinaryOperation::Concatenation){
                throw EvaluationException("Binary operation not supported for strings.");
            }
            std::basic_ostringstream<char16_t> stringStream;
            stringStream << *_value;
            stringStream << b->EvaluateString();
            auto str = stringStream.str();
            return new StringEvalValue(str);
        }
    };
}

#endif //PORYGONLANG_STRINGEVALVALUE_HPP
