#include "Reference.h"

#include "context/ContextUtils.h"
#include "expression/ExpressionUtils.h"
#include "util/simpleobject/AgString.h"
#include "IllegalArgumentException.h"
#include <sstream>

Reference::Reference()
{
    init();
}

Reference::Reference(const AgString & source)
{
    init();
    parse(source);
}

Reference::Reference(const AgString & server, const AgString & context)
{
    init();
    this->server = server;
    this->context = context;
}

Reference::Reference(const AgString & entity, int entityType, const AgString & field)
{
    init();
    this->entity = entity;
    this->entityType = entityType;
    this->field = field;
}

Reference::Reference(const AgString & context, const AgString & entity, int entityType, const AgString & field)
{
    init();
    this->context = context;
    this->entity = entity;
    this->entityType = entityType;
    this->field = field;
}

Reference::Reference(const AgString & context, const AgString & entity, int entityType)
{
    init();
    this->context = context;
    this->entity = entity;
    this->entityType = entityType;
}

Reference::Reference(const AgString & context, const AgString & function, std::vector<boost::shared_ptr<void> > )
{
    init();
    this->context = context;
    this->entity = function;
    this->entityType = ContextUtils::ENTITY_FUNCTION;
}

void Reference::init()
{
    entityType = ContextUtils::ENTITY_VARIABLE;
    row = 0;
}

void Reference::parse(const AgString& source)
{   
    AgString src = source;
    src = src.trim();

    bool isFunction = false;
    bool isEvent = false;
    bool isAction = false;

    image = src;

    int paramsBegin = src.IndexOf(PARAMS_BEGIN);
    int paramsEnd = src.LastIndexOf(PARAMS_END);

    if (paramsBegin != -1)
    {
        if (paramsEnd == -1)
            throw IllegalArgumentException("No closing ')' for function parameters");

        isFunction = true;

        AgString paramsSrc = src.Substring(paramsBegin + 1, paramsEnd);

        parameters = ExpressionUtils::getFunctionParameters(paramsSrc, true);

        entityType = ContextUtils::ENTITY_FUNCTION;

        src = src.Substring(0, paramsBegin) + src.Substring(paramsEnd + 1, src.Length());
    }
    else
    {
        int eventSignPos = src.LastIndexOf(EVENT_SIGN);

        if (eventSignPos != -1)
        {
            isEvent = true;

            entityType = ContextUtils::ENTITY_EVENT;

            src = src.Substring(0, eventSignPos) + src.Substring(eventSignPos + 1, src.Length());
        }
        else
        {
            int actionSignPos = src.LastIndexOf(ACTION_SIGN);

            if (actionSignPos != -1)
            {
                isAction = true;

                entityType = ContextUtils::ENTITY_ACTION;

                src = src.Substring(0, actionSignPos) + src.Substring(actionSignPos + 1, src.Length());
            }
        }
    }

    int schemaEnd = src.IndexOf(SCHEMA_END);

    if (schemaEnd != -1)
    {
        schema = src.Substring(0, schemaEnd);
        src = src.Substring(schemaEnd + 1, src.Length());
    }

    int serverEnd = src.IndexOf(SERVER_END);

    if (serverEnd != -1)
    {
        server = src.Substring(0, serverEnd);
        src = src.Substring(serverEnd + 1, src.Length());
    }

    int contextEnd = src.IndexOf(CONTEXT_END);

    if (contextEnd != -1)
    {
        context = src.Substring(0, contextEnd);
        src = src.Substring(contextEnd + 1, src.Length());
    }

    int propertyBegin = src.IndexOf(PROPERTY_BEGIN);

    if (propertyBegin != -1)
    {
        property = src.Substring(propertyBegin + 1, src.Length());
        src = src.Substring(0, propertyBegin);
    }

    int rowBegin = src.IndexOf(ROW_BEGIN);
    int rowEnd = src.IndexOf(ROW_END);

    if (rowBegin != -1)
    {
        if (rowEnd == -1)
        {
            throw IllegalArgumentException("No closing ']' in row reference");
        }

        row = atoi(src.Substring(rowBegin + 1, rowEnd).toUtf8().c_str());

        src = src.Substring(0, rowBegin);
    }

    int fieldBegin = src.IndexOf(FIELD_BEGIN);

    if (fieldBegin != -1)
    {
        entity = src.Substring(0, fieldBegin);
        field = (fieldBegin != src.Length() - 1) ? src.Substring(fieldBegin + 1, src.Length()) : "";
    }
    else if (src.length() > 0)
    {
        if (!context.toUtf8().empty() || isFunction || isEvent || isAction)
        {
            entity = src;
        }
        else
        {
            field = src;
        }
    }
}

AgString Reference::getServer()
{
    return server;
}

AgString Reference::getContext()
{
    return context;
}

AgString Reference::getEntity()
{
    return entity;
}

int Reference::getEntityType()
{
    return entityType;
}

AgString Reference::getField()
{
    return field;
}

std::vector<boost::shared_ptr<void> > Reference::getParameters()
{
    return parameters;
}

int Reference::getRow()
{
    return row;
}

AgString Reference::getSchema()
{
    return schema;
}

AgString Reference::getProperty()
{
    return property;
}

AgString Reference::getImage()
{
    if (image.length()) {
        return image;
    }else {
        return createImage();
    }
}

AgString Reference::createImage()
{    
    return "";
}

AgString Reference::toString()
{
    return getImage();
}

void Reference::setContext(const AgString & context)
{
    this->context = context;
}

void Reference::setEntity(const AgString & entity)
{
    this->entity = entity;
}

void Reference::setEntityType(int entityType)
{
    this->entityType = entityType;
}

void Reference::addParameter(const AgString & parameter)
{
    parameters.push_back(boost::shared_ptr<AgString>(new AgString(parameter)) );
}

void Reference::addParameter(boost::shared_ptr<Expression> parameter)
{
    parameters.push_back(parameter);
}

void Reference::setField(const AgString & field)
{
    this->field = field;    
}

void Reference::setProperty(const AgString & property)
{
    this->property = property;    
}

void Reference::setSchema(const AgString & schema)
{
    this->schema = schema;   
}

void Reference::setRow(int  row)
{
    this->row = row;   
}

void Reference::setServer(const AgString & server)
{
    this->server = server;
    image.clear();
}

Reference* Reference::clone()
{
    Reference *ref = new Reference();
    ref->image = image;
    ref->schema = schema;
    ref->server = server;
    ref->context = context;
    ref->entity = entity;
    ref->entityType = entityType;
    ref->field = field;;
    ref->property = property;
    ref->row = row;
    return ref;
}

bool Reference::equals(Reference* obj)
{
    UNUSED(obj);
    //TODO:
    assert(0);
    return false;
}
