#include "vector.h"
#include "scalar.h"
#include "reference.h"
#include "state.h"

using namespace Frost;

const s_str Vector::TYPE = "Vector";

s_str Vector::PrintContent() const
{
    s_str sResult;

    sResult += "<";
    s_ctnr<s_refptr<Symbol>>::const_iterator iter;
    foreach (iter, v)
    {
        sResult += (*iter)->PrintContent() + ", ";
    }

    if (!v.IsEmpty())
        sResult.EraseFromEnd(2);

    sResult += ">";

    if (name.IsEmpty())
        return sResult;
    else
        return name + " = " + sResult;
}

s_str Vector::GetType() const
{
    return TYPE;
}

s_str Vector::GetFullType() const
{
    return v.GetSize() + "-" + TYPE;
}

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

s_bool Vector::MatchesConstructor(const s_str& s)
{
    return (s.StartsWith("<") && s.EndsWith(">"));
}

s_refptr<Symbol> Vector::CreateFrom(State& mState, const s_str& n, const s_str& st, s_str& e)
{
    s_str s = st;
    s.EraseFromStart(1); s.EraseFromEnd(1);

    auto pCutFunc = [] (s_str::const_iterator iter, const s_str& s, s_str& o) -> s_bool {
        o = ","; return (*iter == ',');
    };

    s_ctnr<s_str> lvs;
    if (!mState.CutExpression(lvs, s, pCutFunc, e, false))
        return nullptr;

    s_ctnr<s_refptr<Symbol>> v;

    s_str sType;
    s_ctnr<s_str>::iterator iterV;
    foreach (iterV, lvs)
    {
        iterV->Trim(' ');
        s_refptr<Symbol> r = mState.ParseSymbol("", *iterV, e);
        if (!r) return nullptr;

        s_str sRType = r->GetFullType();
        if (sRType != Reference::TYPE)
        {
            if (!sType.IsEmpty() && sType != sRType)
            {
                if (!e.IsEmpty()) e.PushBack('\n');
                e += "\tError : Non-homogeneous Vectors are not allowed.\n"
                     "\t        Trying to mix " + sType + " with " + sRType;
                return nullptr;
            }
            else
                sType = sRType;
        }

        v.PushBack(r);
    }

    s_ctnr<s_refptr<Symbol>>::iterator iterS;
    foreach (iterS, v)
    {
        if ((*iterS)->GetFullType() == Reference::TYPE &&
            (*iterS)->GetType() != sType)
        {
            if (!e.IsEmpty()) e.PushBack('\n');
            e += "\tError : Non-homogeneous Vectors are not allowed.\n"
                 "\t        Trying to mix " + sType + " with " + (*iterS)->GetType();
            return nullptr;
        }
    }

    return s_refptr<Symbol>(new Vector(n, v));
}

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

    mState.RegisterBinaryOperator<Scalar,Vector>("*", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Scalar> sa = s_refptr<Scalar>::StaticCast(a);
        s_refptr<Vector> sb = s_refptr<Vector>::StaticCast(b);

        s_ctnr<s_refptr<Symbol>> rv;
        s_ctnr<s_refptr<Symbol>>::iterator iter;
        foreach (iter, sb->v)
        {
            s_refptr<Symbol> r = s.BinaryOperator("*", sa, *iter, e);
            if (!r) return nullptr;
            rv.PushBack(r);
        }
        return s_refptr<Symbol>(new Vector("", rv));
    });
    mState.RegisterBinaryOperator<Vector,Scalar>("*", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);
        s_refptr<Scalar> sb = s_refptr<Scalar>::StaticCast(b);

        s_ctnr<s_refptr<Symbol>> rv;
        s_ctnr<s_refptr<Symbol>>::iterator iter;
        foreach (iter, sa->v)
        {
            s_refptr<Symbol> r = s.BinaryOperator("*", *iter, sb, e);
            if (!r) return nullptr;
            rv.PushBack(r);
        }
        return s_refptr<Symbol>(new Vector("", rv));
    });
    mState.RegisterBinaryOperator<Vector,Vector>("+", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);
        s_refptr<Vector> sb = s_refptr<Vector>::StaticCast(b);

        if (sa->v.GetSize() != sb->v.GetSize())
        {
            if (!e.IsEmpty()) e.PushBack('\n');
            e += "\tError : Cannot add two Vectors of different length (" + sa->v.GetSize() + " and " + sb->v.GetSize() + ")";
            return nullptr;
        }

        s_ctnr<s_refptr<Symbol>> rv;
        s_ctnr<s_refptr<Symbol>>::iterator itera, iterb = sb->v.Begin();
        foreach (itera, sa->v)
        {
            s_refptr<Symbol> r = s.BinaryOperator("+", *itera, *iterb, e);
            if (!r) return nullptr;
            rv.PushBack(r);
            ++iterb;
        }
        return s_refptr<Symbol>(new Vector("", rv));
    });
    mState.RegisterBinaryOperator<Vector,Vector>("-", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);
        s_refptr<Vector> sb = s_refptr<Vector>::StaticCast(b);

        if (sa->v.GetSize() != sb->v.GetSize())
        {
            if (!e.IsEmpty()) e.PushBack('\n');
            e += "\tError : Cannot substract two Vectors of different length (" + sa->v.GetSize() + " and " + sb->v.GetSize() + ")";
            return nullptr;
        }

        s_ctnr<s_refptr<Symbol>> rv;
        s_ctnr<s_refptr<Symbol>>::iterator itera, iterb = sb->v.Begin();
        foreach (itera, sa->v)
        {
            s_refptr<Symbol> r = s.BinaryOperator("-", *itera, *iterb, e);
            if (!r) return nullptr;
            rv.PushBack(r);
            ++iterb;
        }
        return s_refptr<Symbol>(new Vector("", rv));
    });
    mState.RegisterBinaryOperator<Vector,Vector>("^", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);
        s_refptr<Vector> sb = s_refptr<Vector>::StaticCast(b);

        if (sa->v.GetSize() != sb->v.GetSize() || (sa->v.GetSize() != 3))
        {
            if (!e.IsEmpty()) e.PushBack('\n');
            e += "\tError : The cross product '^' is only available for 3-Vectors";
            return nullptr;
        }

        s_ctnr<s_refptr<Symbol>> rv; rv.Resize(3);
        rv[0] = s.BinaryOperator("-",
            s.BinaryOperator("*",
                sa->v[1], sb->v[2], e
            ), s.BinaryOperator("*",
                sa->v[2], sb->v[1], e
            ), e
        );

        rv[1] = s.BinaryOperator("-",
            s.BinaryOperator("*",
                sa->v[2], sb->v[0], e
            ), s.BinaryOperator("*",
                sa->v[0], sb->v[2], e
            ), e
        );

        rv[2] = s.BinaryOperator("-",
            s.BinaryOperator("*",
                sa->v[0], sb->v[1], e
            ), s.BinaryOperator("*",
                sa->v[1], sb->v[0], e
            ), e
        );
        return s_refptr<Symbol>(new Vector("", rv));
    });
    mState.RegisterBinaryOperator<Vector,Scalar>("[]", [] (State& s, s_refptr<Symbol> a, s_refptr<Symbol> b, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);
        s_refptr<Scalar> sb = s_refptr<Scalar>::StaticCast(b);

        s_int i = s_int(sb->v);
        s_int n = s_int(sa->v.GetSize());
        while (i < 0) i += n;
        while (i >= n) i -= n;

        s_refptr<Symbol> r = sa->v[s_uint(i)]->Clone();
        r->name = "";
        return r;
    });
    mState.RegisterRightUnaryOperator<Vector>("+", [] (State& s, s_refptr<Symbol> a, s_str& e) -> s_refptr<Symbol> {
        return a->Clone();
    });
    mState.RegisterRightUnaryOperator<Vector>("-", [] (State& s, s_refptr<Symbol> a, s_str& e) -> s_refptr<Symbol> {
        s_refptr<Vector> sa = s_refptr<Vector>::StaticCast(a);

        s_ctnr<s_refptr<Symbol>> rv;
        s_ctnr<s_refptr<Symbol>>::iterator itera;
        foreach (itera, sa->v)
        {
            s_refptr<Symbol> r = s.RightUnaryOperator("-", *itera, e);
            if (!r) return nullptr;
            rv.PushBack(r);
        }
        return s_refptr<Symbol>(new Vector("", rv));
    });
}
