#include "reference.h"
#include "state.h"

using namespace Frost;

const s_str Reference::TYPE = "Reference";

s_refptr<Symbol> Reference::Dereference() const
{
    s_str sError;
    s_refptr<Symbol> res = s->ParseSymbol("", r, sError);
    if (res) return res;
    else return s_refptr<Reference>(new Reference(name, s, ""));
}

s_refptr<Symbol> Reference::Dereference_() const
{
    s_str sError;
    s_refptr<Symbol> res = s->ParseSymbol("", r, sError);
    if (res) return res;
    else return nullptr;
}

template<>
s_refptr<Symbol> Frost::Dereference(s_refptr<Symbol> s)
{
    s_refptr<Reference> rs = s_refptr<Reference>::DynamicCast(s);
    if (rs)
    {
        if (rs->r.IsEmpty())
            return nullptr;

        return rs->Dereference();
    }
    else
        return s;
}

s_str Reference::PrintContent() const
{
    if (r.IsEmpty())
        return "null";

    s_refptr<Symbol> res = Dereference_();
    if (name.IsEmpty())
    {
        if (res)
            return res->PrintContent();
        else
            return r;
    }
    else
    {
        if (res)
            return name + " = " + res->PrintContent();
        else
            return name + " = " + r;
    }
}

s_str Reference::GetType() const
{
    if (r.IsEmpty())
        return TYPE;

    s_refptr<Symbol> res = Dereference_();
    if (res)
        return res->GetType();
    else
        return TYPE;
}

s_str Reference::GetTrueType() const
{
    return TYPE;
}

s_str Reference::GetFullType() const
{
    return TYPE;
}

s_refptr<Symbol> Reference::Clone() const
{
    return s_refptr<Symbol>(new Reference(*this));
}

s_bool Reference::MatchesConstructor(const s_str& s)
{
    return s.StartsWith("&");
}

s_refptr<Symbol> Reference::CreateFrom(State& mState, const s_str& n, const s_str& st, s_str& e)
{
    s_str s = st;
    s.EraseFromStart(1);
    s.Trim(' ');
    return s_refptr<Symbol>(new Reference(n, &mState, s));
}

void Reference::Register(State& mState)
{
    mState.RegisterSymbolType<Reference>();
    mState.RegisterConstructor<Reference>(&Reference::MatchesConstructor, &Reference::CreateFrom);
}
