#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::SimplifyParenthesis(s_str& sError)
{
    //Log("#");
    s_bool res = SimplifyParenthesis_(s_str(r), r, sError);
    //Log("## : "+r);
    return res;
}

s_bool Reference::SimplifyParenthesis_(const s_str& ref, s_str& res, s_str& sError)
{
    res = "";

    s_uint uiOpenPar = ref.FindPos("(");
    if (uiOpenPar.IsValid())
    {
        //Log("Simplify : "+ref);

        s_ctnr<s_str> exp;
        if (!s->CutOperators(exp, ref, sError))
            return false;

        //Log(exp);

        // Horrible !
        if (exp.GetSize() == 1)
        {
            s_str sTemp = exp[0];

            sTemp.Trim(' ');
            sTemp.EraseFromStart(1);
            sTemp.EraseFromEnd(1);

            s_ctnr<s_str> exp2;
            if (!s->CutOperators(exp2, sTemp, sError))
                return false;

            if (exp2.GetSize() == 1)
                return SimplifyParenthesis_(sTemp, res, sError);
            else
            {
                res = exp[0];
                return true;
            }
        }
        else
        {
            s_str o;
            s_uint w = s_uint::INF;

            s_ctnr<s_str>::iterator iter;
            foreach (iter, exp)
            {
                if (s->IsOperator(*iter))
                {
                    s_uint tw = s->GetOperatorWeight(*iter);
                    if (tw < w)
                    {
                        o = *iter;
                        w = tw;
                    }

                    res += *iter;
                }
                else
                {
                    s_str tmp;
                    SimplifyParenthesis_(*iter, tmp, sError);
                    res += tmp;
                }
            }

            //Log("Lowest operator : '"+o+"' ("+w+")");
        }

        return true;
    }
    else
    {
        res = ref;
        return true;
    }
}

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_bool inl, s_str& e)
{
    s_str s = st;

    if (inl)
        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);
}
