#include "datatable/validator/LimitsValidator.h"
#include <list>
#include <sstream>
#include "util/StringUtils.h"
#include "datatable/FieldFormat.h"
#include "util/simpleobject/AgInteger.h"
#include "util/simpleobject/AgString.h"
#include "AggreGateException.h"
#include "data/Data.h"
#include "datatable/ValidationException.h"
#include "Cres.h"
#include "GlobalVars.h"

LimitsValidator::LimitsValidator(FieldFormat &fieldFormat, const AgString &source)
{
    std::vector<AgString> minMax = StringUtils::split(source, MIN_MAX_SEPARATOR);

    if (fieldFormat.getType() == AGG_GLOBAL.DATA_FIELD || fieldFormat.getType() == AGG_GLOBAL.STRING_FIELD)
    {
        if (minMax.size() > 1)
        {


            min = ComparablePtr(new AgInteger(minMax[0].toInt()));
            max = ComparablePtr(new AgInteger(minMax[1].toInt()));
        }
        else
        {
            max = ComparablePtr(new AgInteger(minMax[0].toInt()));
        }
    }
    else
    {
        if (minMax.size() > 1)
        {
            ComparablePtr c1 = boost::dynamic_pointer_cast<Comparable>(fieldFormat.valueFromString(minMax[0]));
            ComparablePtr c2 = boost::dynamic_pointer_cast<Comparable>(fieldFormat.valueFromString(minMax[1]));
            if (c1.get() == NULL || c2.get() == NULL)
            {
                throw AggreGateException("Fatal error in LimitsValidator::LimitsValidator(FieldFormatPtr fieldFormat, const AgString &source)");
            }

            min = c1;
            max = c2;
        }
        else
        {
            ComparablePtr c1 = boost::dynamic_pointer_cast<Comparable>(fieldFormat.valueFromString(minMax[0]));
            if (c1.get() == NULL)
            {
                throw AggreGateException("Fatal error in LimitsValidator::LimitsValidator(FieldFormatPtr fieldFormat, const AgString &source)");
            }

            max = c1;
        }
    }
}

LimitsValidator::LimitsValidator(ComparablePtr min, ComparablePtr max)
{
    if (min.get() != NULL && max.get() != NULL && typeid(min.get()) != typeid(max.get()))
    {
        throw AggreGateException("min and max Limits Validator parameters should be the same type, LimitsValidator::LimitsValidator()");
    }
    // now supported only Integer limits
    Cloneable *i1 = dynamic_cast<Cloneable *>(min.get());
    Cloneable *i2 = dynamic_cast<Cloneable *>(max.get());
    assert(i1);
    assert(i2);

    this->min = ComparablePtr(dynamic_cast<Comparable *>(i1->clone()));
    this->max = ComparablePtr(dynamic_cast<Comparable *>(i2->clone()));
}

LimitsValidator::LimitsValidator(int min, int max)
{
    this->min = ComparablePtr(new AgInteger(min));
    this->max = ComparablePtr(new AgInteger(max));
}

bool LimitsValidator::shouldEncode()
{
    return true;
}

char LimitsValidator::getType()
{
    return AGG_GLOBAL.VALIDATOR_LIMITS;
}

ComparablePtr LimitsValidator::getMin()
{
    return min;
}

ComparablePtr LimitsValidator::getMax()
{
    return max;
}

AgString LimitsValidator::encode()
{
    AgString ss;
    AgObject *objMin = dynamic_cast<AgObject *>(min.get());
    AgObject *objMax = dynamic_cast<AgObject *>(max.get());

    if (min.get() != 0)
    {

        ss += objMin->toString();
        if (max.get() != 0)
        {
            ss += MIN_MAX_SEPARATOR;
            ss += objMax->toString();
        }
    }
    else {
        ss += (max.get() != 0 ? objMax->toString() : "");
    }
    return ss;
}

AgObjectPtr LimitsValidator::validate(AgObjectPtr value)
{
    if (value.get() == 0)
        return value;

    if (Data *data = dynamic_cast<Data *>(value.get()))
    {
        AgInteger i(data->getData().size());
        compare(i, "", "");
    }
    else if (AgString *str = dynamic_cast<AgString *>(value.get()))
    {
        AgInteger i(str->length());
        compare(i, Cres::get()->getString("dtValueTooShort"), Cres::get()->getString("dtValueTooLong"));
    }
    else
    {
        Comparable *cv = dynamic_cast<Comparable *>(value.get());
        if (cv)
        {
            compare(*cv, AgString(), AgString());
        }
        else
        {
            throw ValidationException("Value not comparable, LimitsValidator::validate");
        }

    }
    return value;
}


void LimitsValidator::compare(Comparable &cv, const AgString& smallMessage, const AgString& bigMessage)
{
    if (min.get() != 0)
    {
        if (cv.compareTo(min.get()) < 0)
        {
            if (!smallMessage.empty())
            {
                throw ValidationException(smallMessage);
            }
            else
            {
                throw ValidationException("Value too small");
            }
            //todo
            //throw new ValidationException(MessageFormat.format(smallMessage != null ? smallMessage : Cres.get().getString("dtValueTooSmall"), cv, min));
        }
    }

    if (cv.compareTo(max.get()) > 0)
    {
        if (!bigMessage.empty())
        {
            throw ValidationException(bigMessage);
        }
        else
        {
            throw ValidationException("Value too big");
        }
        //todo
        //throw new ValidationException(MessageFormat.format(bigMessage != 0 ? bigMessage : Cres::get()->getString("dtValueTooBig"), cv, max));
    }
}

int LimitsValidator::hashCode()
{
    int prime = 31;
    int result = 1;
    AgObject *objMin = dynamic_cast<AgObject *>(min.get());
    AgObject *objMax = dynamic_cast<AgObject *>(max.get());
    if (objMin == NULL || objMax == NULL)
    {
        throw AggreGateException("LimitsValidator::hashCode(), error cast to AgObject");
    }
    result = prime * result + ((max.get() == NULL) ? 0 : objMax->hashCode());
    result = prime * result + ((min.get() == NULL) ? 0 : objMin->hashCode());
    return result;
}


bool LimitsValidator::equals(AgObject* obj)
{
    if (this == obj)
    {
        return true;
    }

    if (!AbstractFieldValidator::equals(obj))
    {
        return false;
    }

    LimitsValidator *other = dynamic_cast<LimitsValidator*>(obj);
    AgObject *objMin = dynamic_cast<AgObject *>(min.get());
    AgObject *objMax = dynamic_cast<AgObject *>(max.get());
    AgObject *objMinOther = dynamic_cast<AgObject *>(other->min.get());
    AgObject *objMaxOther = dynamic_cast<AgObject *>(other->max.get());

    if (max.get() == NULL)
    {
        if (other->max.get() != NULL)
        {
            return false;
        }
    }
    else if (!objMax->equals(objMaxOther))
    {
        return false;
    }

    if (min.get() == 0)
    {
        if (other->min.get() != 0)
        {
            return false;
        }
    }
    else if (!objMin->equals(objMinOther))
    {
        return false;
    }

    return true;
}

LimitsValidator *LimitsValidator::clone()
{
    return new LimitsValidator(min, max);
}


