#include "DataRecord.h"
#include "datatable/encoding/ClassicEncodingSettings.h"
#include "util/StringUtils.h"
#include "datatable/FieldFormat.h"
#include "ValidationException.h"
#include "IllegalStateException.h"
#include "IllegalArgumentException.h"
#include "util/simpleobject/AgInteger.h"
#include "util/simpleobject/AgString.h"
#include "util/simpleobject/AgBoolean.h"
#include "util/simpleobject/AgLong.h"
#include "util/simpleobject/AgFloat.h"
#include "util/simpleobject/AgDouble.h"
#include "util/CloneUtils.h"
#include "util/MessageFormat.h"
#include "util/Util.h"
#include "Cres.h"

DataRecord::DataRecord() : table(NULL)
{
}

void DataRecord::init(TableFormatPtr tableFormat)
{
    if (tableFormat.get() != NULL)
    {
        tableFormat->makeImmutable(NULL);
        format = tableFormat;
    }
}

DataRecord::DataRecord(TableFormatPtr tableFormat) : table(NULL)
{
    init(tableFormat);
}

DataRecord::DataRecord(TableFormatPtr tableFormat, std::list<AgObjectPtr>& data) : table(NULL)
{
    init(tableFormat);
    for (std::list<AgObjectPtr>::iterator it = data.begin(); it != data.end(); ++it)
    {
        addValue(*it);
    }
}

DataRecord::DataRecord(TableFormatPtr tableFormat, AgObjectPtr data) : table(NULL)
{
    init(tableFormat);
    addValue(data);
}

DataRecord::DataRecord(TableFormatPtr tableFormat, const AgString& dataString, ClassicEncodingSettingsPtr settings, bool validate, std::vector<AgString> fieldNamesInData)
: table(NULL)
{
    init(tableFormat);
    setData(dataString, settings, validate, fieldNamesInData);
}

DataRecord::DataRecord(TableFormatPtr tableFormat, const AgString& dataString) : table(NULL)
{
    init(tableFormat);
    setData(dataString, ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)), true, std::vector<AgString>());
}

int DataRecord::getFieldCount()
{
    if (format.get() == NULL)
    {
        return 0;
    }
    else
    {
        return format->getFieldCount();
    }
}

TableFormatPtr DataRecord::getFormat()
{
    return format;
}

FieldFormat* DataRecord::getFormat(int index)
{
    return format->getField(index);
}

FieldFormat* DataRecord::getFormat(const AgString &name)
{
    return format->getField(name);
}

AgString DataRecord::getId()
{
    return id;
}

DataTable* DataRecord::getTable()
{
    return table;
}

AgString DataRecord::encode(bool useVisibleSeparators)
{
    return encode(ClassicEncodingSettingsPtr(new ClassicEncodingSettings(useVisibleSeparators)));
}

AgString DataRecord::encode(ClassicEncodingSettingsPtr settings)
{
    AgString encodedData("");
    bool useVisibleSeparators = settings.get() != NULL ? settings->isUseVisibleSeparators() : false;

    if (!getId().empty())
    {
        Element el(ELEMENT_ID(), getId());
        encodedData += el.encode(useVisibleSeparators);
    }

    if (format.get())
        for (int i = 0; i < format->getFieldCount(); i++)
        {
            FieldFormat* ff = format->getField(i);
            AgObjectPtr value = getValue(ff);
            Element el("", ff->valueToEncodedString(value, settings));
            encodedData += el.encode(useVisibleSeparators);
        }

    return encodedData;
}

DataRecord &DataRecord::addInt(int val)
{
    AgObjectPtr obj = AgObjectPtr(new AgInteger(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addString(const AgString &val)
{
    AgObjectPtr obj = AgObjectPtr(new AgString(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addBoolean(bool val)
{
    AgObjectPtr obj = AgObjectPtr(new AgBoolean(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addLong(int64_t val)
{
    AgObjectPtr obj = AgObjectPtr(new AgLong(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addFloat(float val)
{
    AgObjectPtr obj = AgObjectPtr(new AgFloat(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addDouble(double val)
{
    AgObjectPtr obj = AgObjectPtr(new AgDouble(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addDate(AgDate val)
{
    AgObjectPtr obj = AgObjectPtr(new AgDate(val));
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addDataTable(DataTablePtr val)
{
    AgObjectPtr obj = boost::dynamic_pointer_cast<AgObject>(val);

    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addColor(AgColorPtr val)
{
    AgObjectPtr obj = boost::dynamic_pointer_cast<AgObject>(val);
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::addData(DataPtr val)
{
    AgObjectPtr obj = boost::dynamic_pointer_cast<AgObject>(val);
    checkNumberOfDataFieldsSet(obj);
    return setValue(data.size(), obj);
}

DataRecord &DataRecord::setValue(int index, AgObjectPtr value)
{
    return setValue(index, value, true);
}

DataRecord &DataRecord::setValue(const AgString &name, AgObjectPtr value)
{
    return setValue(findIndex(name), value, true);
}

DataRecord &DataRecord::setValue(const AgString &name, AgObjectPtr value, bool validate)
{
    return setValue(findIndex(name), value, validate);
}

DataRecord &DataRecord::setValueSmart(int index, AgObjectPtr value)
{
    FieldFormat* ff = getFormat(index);
    return setValueSmart(ff->getName(), value);
}

DataRecord &DataRecord::setValueSmart(const AgString &name, AgObjectPtr value)
{
    FieldFormat* ff = getFormat()->getField(name);

    if (ff == NULL)
    {
        throw IllegalArgumentException(MessageFormat::format(Cres::get()->getString("dtFieldNotFound"), name) + ": " + dataAsString(true, true));
    }

    if (value.get() == NULL || ff->getFieldClass() == value->getClass() || ff->getFieldWrappedClass() == value->getClass())
    {
        return setValue(ff->getName(), value);
    }
    else
    {
        AgString stringValue = value->toString();
        try
        {
            return setValue(ff->getName(), AgObjectPtr(ff->valueFromString(stringValue)));
        }
        catch (AggreGateException ex)
        {
            std::vector< std::pair<AgObjectPtr,AgString> > &m = ff->getSelectionValues();
            if (m.size() != 0)
            {
                for (std::vector< std::pair<AgObjectPtr,AgString> >::iterator it = m.begin(); it != m.end(); ++it)
                {
                    // todo need check
                    AgString svdesc = it->second;
                    //String svdesc = ff.getSelectionValues().get(sv).toString();
                    if (stringValue == svdesc)
                    {
                        return setValue(ff->getName(), it->first);
                    }
                }
            }

            throw IllegalArgumentException(MessageFormat::format(Cres::get()->getString("dtIllegalFieldValue"), Util::getObjectDescription(value), ff->toDetailedString()) + ex.getMessage());
        }
    }
}

DataRecord &DataRecord::setValue(int index, AgObjectPtr value, bool validate)
{
    FieldFormat* ff = getFormat()->getField(index);

    try
    {
        value = ff->checkAndConvertValue(value, validate);
    }
    catch (ValidationException ex)
    {
        throw IllegalArgumentException(MessageFormat::format(Cres::get()->getString("dtIllegalFieldValue"), value->toString(), ff->toDetailedString()) + ex.getMessage());
    }

    AgObjectPtr oldValue = data[ff->getName()];

    try
    {
        data[ff->getName()] = value;
        if (table != NULL)
        {
            table->validateRecord(shared_from_this());
        }
    }
    catch (ValidationException ex1)
    {
        data[ff->getName()] = oldValue;
        throw IllegalArgumentException(ex1.getMessage(), ex1.getDetails());
    }

    return *this;
}

DataRecord &DataRecord::addValue(AgObjectPtr value)
{
    checkNumberOfDataFieldsSet(value);
    return setValue(data.size(), value);
}

AgString DataRecord::getString(const AgString &name)
{
    return getString(findIndex(name));
}

AgString DataRecord::getString(int index)
{
    AgObjectPtr obj = getValue(index);
    if (obj.get() == NULL)
    {
        return "";
    }
    AgString *s = dynamic_cast<AgString *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getString illegal agrgument");
    }
    return *s;
}

int DataRecord::getInt(const AgString &name)
{
    return getInt(findIndex(name));
}

int DataRecord::getInt(int index)
{
    AgObjectPtr obj = getValue(index);
    AgInteger *s = dynamic_cast<AgInteger *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getInt illegal argument");
    }
    return s->getValue();
}

bool DataRecord::getBoolean(const AgString &name)
{
    return getBoolean(findIndex(name));
}

bool DataRecord::getBoolean(int index)
{
    AgObjectPtr obj = getValue(index);
    AgBoolean *s = dynamic_cast<AgBoolean *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getBoolean illegal agrgument");
    }
    return s->getValue();
}

int64_t DataRecord::getLong(const AgString &name)
{
    return getLong(findIndex(name));
}

int64_t DataRecord::getLong(int index)
{
    AgObjectPtr obj = getValue(index);
    if (obj == NULL)
    {
        return 0;
    }
    AgLong *s = dynamic_cast<AgLong *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getLong illegal argument");
    }
    return s->getValue();
}

float DataRecord::getFloat(const AgString &name)
{
    return getFloat(findIndex(name));
}

float DataRecord::getFloat(int index)
{
    AgObjectPtr obj = getValue(index);
    AgFloat *s = dynamic_cast<AgFloat *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getFloat illegal argument");
    }
    return s->getValue();
}

double DataRecord::getDouble(const AgString &name)
{
    return getDouble(findIndex(name));
}

double DataRecord::getDouble(int index)
{
    AgObjectPtr obj = getValue(index);
    AgDouble *s = dynamic_cast<AgDouble *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getDouble illegal agrgument");
    }
    return s->getValue();
}

AgDate DataRecord::getDate(const AgString &name)
{
    return getDate(findIndex(name));
}

AgDate DataRecord::getDate(int index)
{
    AgObjectPtr obj = getValue(index);
    AgDate *s = dynamic_cast<AgDate *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getDate illegal agrgument");
    }
    return *s;
}

DataTablePtr DataRecord::getDataTable(const AgString &name)
{
    return getDataTable(findIndex(name));
}

DataTablePtr DataRecord::getDataTable(int index)
{
    AgObjectPtr obj = getValue(index);
    DataTable *s = dynamic_cast<DataTable *>(obj.get());
    if (!s)
    {
        return nullptr;
        //throw IllegalArgumentException("DataRecord::DataTable illegal agrgument");
    }
    return boost::dynamic_pointer_cast<DataTable>(obj);
}

AgColor DataRecord::getColor(const AgString &name)
{
    return getColor(findIndex(name));
}

AgColor DataRecord::getColor(int index)
{
    AgObjectPtr obj = getValue(index);
    AgColor *s = dynamic_cast<AgColor *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getColor illegal agrgument");
    }
    return *s;
}

DataPtr DataRecord::getData(const AgString &name)
{
    return getData(findIndex(name));
}

DataPtr DataRecord::getData(int index)
{
    AgObjectPtr obj = getValue(index);
    Data *s = dynamic_cast<Data *>(obj.get());
    if (!s)
    {
        throw IllegalArgumentException("DataRecord::getData illegal agrgument");
    }
    return boost::dynamic_pointer_cast<Data>(obj);
}

AgObjectPtr DataRecord::getValue(int index)
{
    FieldFormat* ff = format->getField(index);
    return getValue(ff);
}

AgObjectPtr DataRecord::getValue(const AgString &name)
{
    return getValue(findIndex(name));
}

AgString DataRecord::getValueDescription(const AgString &name)
{
    AgObjectPtr value = getValue(name);
    FieldFormat* ff = getFormat(name);

    std::vector< std::pair<AgObjectPtr,AgString> > &sv = ff->getSelectionValues();
    if (sv.size() > 0)
    {
        //find value in sv
        AgString description;
        for (std::vector< std::pair<AgObjectPtr,AgString> >::iterator it = sv.begin(); it != sv.end(); ++it)
        {
            if (it->first->equals(value.get()))
            {
                description = it->second;
                break;
            }
        }

        return description.size()  ? description : ff->valueToString(value);
    }
    else
    {
        return ff->valueToString(value);
    }
}

AgString DataRecord::getValueAsString(const AgString &name)
{
    return getValueAsString(findIndex(name));
}

AgString DataRecord::getValueAsString(int index)
{
    return format->getField(index)->valueToString(getValue(index));
}

DataRecord &DataRecord::setId(const AgString &id)
{
    this->id = id;
    return *this;
}

void DataRecord::setFormat(TableFormatPtr format)
{
    format->makeImmutable(NULL);
    this->format = format;
}

bool DataRecord::hasField(const AgString &name)
{
    return getFormat()->hasField(name);
}

void DataRecord::cloneFormatFromTable()
{
    if (table != NULL)
    {
        format = TableFormatPtr(table->getFormat()->clone());
    }
    else
    {
        format = TableFormatPtr(format->clone());
    }
}

AgString DataRecord::dataAsString(bool showFieldNames, bool showHiddenFields)
{
    AgString res;
    bool needSeparator = false;

    for (int j = 0; j < getFieldCount(); j++)
    {
        FieldFormat* ff = getFormat()->getField(j);

        if (ff->isHidden() && !showHiddenFields)
        {
            continue;
        }

        if (needSeparator)
        {
            res += AgString(", ");
        }
        else
        {
            needSeparator = true;
        }

        AgString value = valueAsString(ff->getName());
        res += (showFieldNames ? ff->toString() + "=" : "") + value;
    }

    return res;
}

AgString DataRecord::valueAsString(const AgString &name)
{
    FieldFormat* ff = getFormat(name);
    AgObjectPtr val = getValue(name);

    AgString value = val.get() != NULL ? val->toString() : "NULL";

    if (ff->hasSelectionValues())
    {
        //find val in getSelectionValues()
        AgString sv;
        for (std::vector< std::pair<AgObjectPtr,AgString> >::iterator it = ff->getSelectionValues().begin(); it != ff->getSelectionValues().end(); ++it)
        {
            if (it->first != NULL && it->first->equals(val.get()))
            {
                sv = it->second;
                break;
            }
        }

        value = sv.length() ? sv : value;
    }

    return value;
}

AgString DataRecord::toString()
{
    return dataAsString(true, true);
}

DataTablePtr DataRecord::wrap()
{
    return DataTablePtr(new DataTable(shared_from_this()));
}

DataRecord *DataRecord::clone()
{
    DataRecord *cl = new DataRecord();

    cl->id = id;

    std::map<AgString, AgObjectPtr> data2;
    for (std::map<AgString, AgObjectPtr>::const_iterator it = data.begin(); it != data.end(); ++it)
    {
        AgObjectPtr obj = it->second;
        if (obj != NULL)
        {
            Cloneable *cl = dynamic_cast<Cloneable *>(obj.get());
            assert(cl);
            Cloneable *newcl = cl->clone();
            assert(newcl);
            AgObjectPtr newobj = AgObjectPtr(dynamic_cast<AgObject *>(newcl));
            data2[it->first] = newobj;
        }
        else
        {
            data2[it->first] = obj;
        }
    }
    cl->data = data2;

    return cl;
}

bool DataRecord::equals(AgObjectPtr obj)
{
    if (obj.get() == NULL)
    {
        return false;
    }

    DataRecord *rec = dynamic_cast<DataRecord *>(obj.get());
    if (!rec)
    {
        return false;
    }

    if (getId() != rec->getId())
    {
        return false;
    }

    // Formats are compared only if record does not belong to a table
    if (table == NULL)
    {
        if (!format->equals(rec->getFormat().get()))
        {
            return false;
        }
    }

    for (int i = 0; i < getFieldCount(); i++)
    {
        AgObjectPtr field = getValue(i);
        AgObjectPtr value = rec->getValue(i);
        if (field.get() != NULL ? !field->equals(&(*value)) : value.get() != NULL)
        {
            return false;
        }
    }
    return true;
}

void DataRecord::setTable(DataTable *table)
{
    this->table = table;
}

bool DataRecord::meetToCondition(QueryConditionPtr cond)
{
    if (hasField(cond->getField()))
    {
        AgObjectPtr val = getValue(cond->getField());

        if (val.get() == NULL)
        {
            if (cond->getOperator() != QueryCondition::EQ)
            {
                throw IllegalArgumentException("Can't compare value to NULL");
            }

            if (cond->getValue().get() == NULL)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        if ((cond->getOperator() & QueryCondition::EQ) > 0)
        {
            return val->equals(&(*cond->getValue()));
        }
        else
        {
            Comparable *comp = dynamic_cast<Comparable *>(val.get());
            if (!comp)
            {
                throw IllegalArgumentException("Value isn't comparable");
            }

            if ((cond->getOperator() & QueryCondition::GT) > 0)
            {
                Comparable *comp2 = dynamic_cast<Comparable *>(cond->getValue().get());
                assert(comp2);
                return comp->compareTo(comp2) > 0;
            }
            else
            {
                if ((cond->getOperator() & QueryCondition::LT) > 0)
                {
                    Comparable *comp2 = dynamic_cast<Comparable *>(cond->getValue().get());
                    assert(comp2);
                    return comp->compareTo(comp2) < 0;
                }
            }
        }

        throw IllegalArgumentException("Illegal operator: " + AgString::fromInt(cond->getOperator()));
    }
    return false;
}

void DataRecord::setData(const AgString &dataString, ClassicEncodingSettingsPtr settings, bool validate, std::vector<AgString> fieldNamesInData)
{
    ElementListPtr recs = StringUtils::elements(dataString, settings->isUseVisibleSeparators());

    int i = 0;
    for (std::vector<Element *>::iterator el = recs->elements.begin(); el != recs->elements.end(); ++el)
    {
        if ((*el)->getName().length() != 0)
        {
            if ((*el)->getName() == ELEMENT_ID())
            {
                setId((*el)->getValue());
            }
            else
            {
                // This code exists for compatibility reason only
                FieldFormat* ff = format->getField((*el)->getName());
                if (ff != NULL)
                {
                    setValue((*el)->getName(), ff->valueFromEncodedString((*el)->getValue(), settings, validate), validate);
                }
            }
        }
        else
        {
            if ((int)fieldNamesInData.size() > i)
            {
                AgString fieldName = fieldNamesInData[i];
                if (getFormat()->hasField(fieldName))
                {
                    AgObjectPtr value = format->getField(fieldName)->valueFromEncodedString((*el)->getValue(), settings, validate);
                    setValue(fieldName, value, validate);
                }
            }
            else if (i < format->getFieldCount())
            {
                AgObjectPtr value = format->getField(i)->valueFromEncodedString((*el)->getValue(), settings, validate);
                setValue(i, value, validate);
            }
            i++;
        }
    }
}

void DataRecord::checkNumberOfDataFieldsSet(AgObjectPtr value)
{
    if ((int)data.size() >= format->getFieldCount())
    {
          throw IllegalStateException("Can't add data to data record since all data fields defined by format are already set:" + value->toString());
    }
}

int DataRecord::findIndex(const AgString &name)
{
    int index = format->getFieldIndex(name);
    if (index == -1)
    {
        std::list<AgString> fields;
        std::vector<FieldFormat*> &fs = getFormat()->getFields();
        for (std::vector<FieldFormat*>::iterator ff = fs.begin(); ff != fs.end(); ++ff)
        {
            fields.push_back((*ff)->getName());
        }

        throw IllegalArgumentException(MessageFormat::format(Cres::get()->getString("dtFieldNotFound"), name) + ": " + StringUtils::print(fields));
    }
    return index;
}

AgObjectPtr DataRecord::getValue(FieldFormat* ff)
{
    std::map<AgString, AgObjectPtr>::iterator it = data.find(ff->getName());
    if (it != data.end())
    {
        return data[ff->getName()];
    }
    return ff->isDefaultOverride() ? AgObjectPtr() : ff->getDefaultValueCopy();
}

