#define NOMINMAX
#include "datatable/DataTableReplication.h"
#include "datatable/DataTable.h"
#include "datatable/DataRecord.h"
#include "datatable/TableFormat.h"
#include "datatable/FieldFormat.h"
#include "datatable/DataTableQuery.h"
#include "datatable/QueryCondition.h"
#include "Cres.h"
#include "AggreGateException.h"
#include "util/Log.h"
#include "util/CloneUtils.h"
#include "util/Util.h"
#include "util/MessageFormat.h"
#include "GlobalVars.h"

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target)
{
    return copy(source, target, false, false, true, true, false, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields)
{
    return copy(source, target, copyReadOnlyFields, false, true, true, false, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields)
{
    return copy(source, target, copyReadOnlyFields, copyNonReplicatableFields, true, true, false, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget)
{
    return copy(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, true, false, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget)
{
    return copy(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, false, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget, bool ignoreUnresizable)
{
    return copy(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, ignoreUnresizable, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copy(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget, bool ignoreUnresizable, std::list<AgString> fields)
{
    if (target->getFormat()->getKeyFields().size() == 0)
    {
        return copyWithoutKeyFields(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, ignoreUnresizable, fields);
    }
    else
    {
        return copyWithKeyFields(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, ignoreUnresizable, fields);
    }
}

std::set<AgString> DataTableReplication::copyWithKeyFields(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget, bool ignoreUnresizable, std::list<AgString> fields)
{
    std::set<AgString> errors;

    std::list<AgString> keyFields = target->getFormat()->getKeyFields();

    for (std::list<AgString>::iterator fieldName = keyFields.begin(); fieldName != keyFields.end(); ++fieldName)
    {
        if (!source.getFormat()->hasField(*fieldName))
        {
            return copyWithoutKeyFields(source, target, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, ignoreUnresizable, fields);
        }
    }

    std::vector<DataRecordPtr>::iterator iter = target->iteratorBegin();
    while (iter != target->iteratorEnd())
    {
        DataRecordPtr targetRec = *iter;

        DataTableQueryPtr query = DataTableQueryPtr(new DataTableQuery());

        for (std::list<AgString>::iterator keyField = keyFields.begin(); keyField != keyFields.end(); ++keyField)
        {
            query->addCondition(QueryConditionPtr(new QueryCondition(*keyField, targetRec->getValue(*keyField))));
        }

        DataRecordPtr sourceRec = source.select(query);

        if (removeRecordsFromTarget && sourceRec.get() == NULL && (ignoreUnresizable || !target->getFormat()->isUnresizable()))
        {
            if (target->getRecordCount() > target->getFormat()->getMinRecords())
            {
                iter = target->getRecords().erase(iter);
            }
            else
            {
                if (source.getFormat()->getMinRecords() != source.getFormat()->getMaxRecords())
                {
                    errors.insert(Cres::get()->getString("dtTargetTableMinRecordsReached"));
                }
                ++iter;
                break;
            }
        }
        else
        {
            ++iter;
        }
    }

    for (std::vector<DataRecordPtr>::iterator sourceRec = source.iteratorBegin(); sourceRec != source.iteratorEnd(); ++sourceRec)
    {
        DataTableQueryPtr query = DataTableQueryPtr(new DataTableQuery());

        for (std::list<AgString>::iterator keyField = keyFields.begin(); keyField != keyFields.end(); ++keyField)
        {
            query->addCondition(QueryConditionPtr(new QueryCondition(*keyField, (*sourceRec)->getValue(*keyField))));
        }

        DataRecordPtr targetRec = target->select(query);

        if (targetRec.get() == NULL)
        {
            if (addRecordsToTarget && (ignoreUnresizable || !target->getFormat()->isUnresizable()))
            {
                if (target->getRecordCount() < target->getFormat()->getMaxRecords())
                {
                    DataRecordPtr newRec = DataRecordPtr(new DataRecord(target->getFormat())); // We are not using target.addRecord() to avoid key field validation errors
                    std::set<AgString> res = DataTableReplication::copyRecord(*(*sourceRec), newRec, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, fields);
                    for (std::set<AgString>::iterator r = res.begin(); r != res.end(); ++r)
                    {
                        errors.insert(*r);
                    }
                    try
                    {
                        target->addRecord(newRec);
                    }
                    catch (AggreGateException ex)
                    {
                        errors.insert(Cres::get()->getString("dtCannotAddRecord") + ex.getMessage());
                    }
                }
                else
                {
                    if (source.getFormat()->getMinRecords() != source.getFormat()->getMaxRecords())
                    {
                        errors.insert(Cres::get()->getString("dtTargetTableMaxRecordsReached"));
                    }
                }
            }
        }
        else
        {
            std::set<AgString> res = DataTableReplication::copyRecord(*(*sourceRec), targetRec, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, fields);
            for (std::set<AgString>::iterator r = res.begin(); r != res.end(); ++r)
            {
                errors.insert(*r);
            }
        }
    }

    return errors;
}

std::set<AgString> DataTableReplication::copyWithoutKeyFields(DataTable& source, DataTablePtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget, bool ignoreUnresizable, std::list<AgString> fields)
{
    std::set<AgString> errors;

    if (removeRecordsFromTarget && (ignoreUnresizable || !target->getFormat()->isUnresizable()))
    {
        while (target->getRecordCount() > source.getRecordCount())
        {
            if (target->getRecordCount() > target->getFormat()->getMinRecords())
            {
                target->removeRecord(target->getRecordCount() - 1);
            }
            else
            {
                if (source.getFormat()->getMinRecords() != source.getFormat()->getMaxRecords())
                {
                    errors.insert(Cres::get()->getString("dtTargetTableMinRecordsReached"));
                }
                break;
            }
       }
    }

    int minCount = std::min(source.getRecordCount(), target->getRecordCount());
    for (int i = 0; i < minCount; i++)
    {
        DataRecordPtr srcRec = source.getRecord(i);
        DataRecordPtr tgtRec = target->getRecord(i);

        std::set<AgString> res = copyRecord(*srcRec, tgtRec, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, fields);
        for (std::set<AgString>::iterator r = res.begin(); r != res.end(); ++r)
        {
            errors.insert(*r);
        }
    }

    if (addRecordsToTarget && (ignoreUnresizable || !target->getFormat()->isUnresizable()))
    {
        if (source.getRecordCount() > target->getRecordCount())
        {
            int minCount = std::min(target->getFormat()->getMaxRecords(), source.getRecordCount());
            for (int i = target->getRecordCount(); i < minCount; i++)
            {
                std::set<AgString> res = copyRecord(*source.getRecord(i), target->addRecord(), copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget, fields);
                for (std::set<AgString>::iterator r = res.begin(); r != res.end(); ++r)
                {
                    errors.insert(*r);
                }
            }
        }
    }

    if (source.getRecordCount() > target->getFormat()->getMaxRecords())
    {
        if (source.getFormat()->getMinRecords() != source.getFormat()->getMaxRecords())
        {
            errors.insert(Cres::get()->getString("dtTargetTableMaxRecordsReached"));
        }
    }

    return errors;
}

std::set<AgString> DataTableReplication::copyRecord(DataRecord& source, DataRecordPtr target)
{
    return copyRecord(source, target, false, false, true, true, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copyRecord(DataRecord& source, DataRecordPtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields)
{
    return copyRecord(source, target, copyReadOnlyFields, copyNonReplicatableFields, true, true, std::list<AgString>());
}

std::set<AgString> DataTableReplication::copyRecord(DataRecord& source, DataRecordPtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, std::list<AgString> fields)
{
    return copyRecord(source, target, copyReadOnlyFields, copyNonReplicatableFields, true, true, fields);
}

std::set<AgString> DataTableReplication::copyRecord(DataRecord& source, DataRecordPtr target, bool copyReadOnlyFields, bool copyNonReplicatableFields, bool removeRecordsFromTarget, bool addRecordsToTarget, std::list<AgString> fields)
{
    std::set<AgString> errors;

    for (int i = 0; i < target->getFormat()->getFieldCount(); i++)
    {
        FieldFormat* tgtFf = target->getFormat()->getField(i);
        AgString fieldName = tgtFf->getName();

        FieldFormat* srcFf;

        srcFf = source.getFormat()->getField(fieldName);

        if (fields.size() != 0 && std::find(fields.begin(), fields.end(), tgtFf->getName()) != fields.end())
        {
            continue;
        }

        if (srcFf == NULL)
        {
            continue;
        }

        if (tgtFf->isReadonly() && !copyReadOnlyFields)
        {
            continue;
        }

        if (!copyNonReplicatableFields)
        {
            if (tgtFf->isNotReplicated() || srcFf->isNotReplicated())
            {
                continue;
            }
        }
        try
        {
            if (srcFf->getType() == AGG_GLOBAL.DATATABLE_FIELD && tgtFf->getType() == AGG_GLOBAL.DATATABLE_FIELD)
            {
                DataTablePtr sourceTable = source.getDataTable(fieldName);
                DataTablePtr targetTable = target->getDataTable(fieldName);
                if (sourceTable.get() != NULL && targetTable.get() != NULL)
                {
                    TableFormatPtr tf = targetTable->getFormat();
                    if (Util::equals(tf, DataTable::DEFAULT_FORMAT))
                    {
                        target->setValue(fieldName, DataTablePtr(sourceTable->clone()));
                    }
                    else
                    {
                        std::set<AgString> res = DataTableReplication::copy(*sourceTable, targetTable, copyReadOnlyFields, copyNonReplicatableFields, removeRecordsFromTarget, addRecordsToTarget);
                        for (std::set<AgString>::iterator r = res.begin(); r != res.end(); ++r)
                        {
                            errors.insert(*r);
                        }

                        target->setValue(fieldName, targetTable);
                    }
                    continue;
                }
            }

            if (srcFf->getFieldWrappedClass() == tgtFf->getFieldWrappedClass())
            {
                target->setValue(fieldName, CloneUtils::genericClone(source.getValue(fieldName)));
            }
            else
            {
                target->setValue(fieldName, tgtFf->valueFromString(srcFf->valueToString(source.getValue(fieldName))));
            }
        }
        catch (AggreGateException ex2)
        {
            AgString msg = MessageFormat::format(Cres::get()->getString("dtErrCopyingField"), fieldName);
            LOG_DATATABLE_ERROR(msg.toUtf8());
            LOG_DATATABLE_ERROR((msg + ": " + ex2.getMessage()).toUtf8());
            errors.insert(msg + ": " + ex2.getMessage());
            continue;
        }
    }

    return errors;
}
