#include <stdarg.h>
#include "datatable/DataTable.h"
#include "datatable/DataRecord.h"
#include "datatable/TableFormat.h"
#include "datatable/encoding/ClassicEncodingSettings.h"
#include "util/StringUtils.h"
#include "util/Util.h"
#include "util/Log.h"
#include "util/Element.h"
#include "util/MessageFormat.h"
#include "IllegalStateException.h"
#include "IllegalArgumentException.h"
#include "ValidationException.h"
#include "util/simpleobject/AgString.h"
#include "Cres.h"
#include "datatable/validator/TableValidator.h"
#include "datatable/FieldFormat.h"
#include "datatable/validator/RecordValidator.h"
#include "datatable/QueryCondition.h"
#include "datatable/DataTableQuery.h"
#include "expression/Evaluator.h"
#include "GlobalVars.h"
#include "field/DateFieldFormat.h"

TableFormatPtr DataTable::DEFAULT_FORMAT = DataTable::initDEFAULT_FORMAT();


TableFormatPtr DataTable::initDEFAULT_FORMAT()
{
    TableFormatPtr tf = TableFormatPtr(new TableFormat());
    tf->makeImmutable(NULL);
    return tf;
}

void DataTable::init()
{
    format = DEFAULT_FORMAT;
    id = 0;

    timestamp = AgDatePtr(new AgDate());
    quality = GlobalVars::instance().DEFAULT_QUALITY_VALUE;
}

DataTable::DataTable()
{
    init();
}

DataTable::DataTable(TableFormatPtr format)
{
    init();
    setFormat(format);
}

DataTable::DataTable(TableFormatPtr format, int emptyRecords)
{
    init();
    setFormat(format);
    for (int i = 0; i < emptyRecords; i++)
    {
        addRecord();
    }
}

DataTable::DataTable(TableFormatPtr format, bool createEmptyRecords)
{
    int emptyRecords = createEmptyRecords ? (format.get() != NULL ? format->getMinRecords() : 0) : 0;

    init();
    setFormat(format);
    for (int i = 0; i < emptyRecords; i++)
    {
        addRecord();
    }
}

DataTable::DataTable(DataRecordPtr record)
{
    init();
    addRecord(record);
}

DataTable::DataTable(TableFormatPtr format, const AgString &dataString, ClassicEncodingSettingsPtr settings)
{
    init();
    setFormat(format);

    std::vector<AgString> fieldNames;
    ElementListPtr recs = StringUtils::elements(dataString, false);
    for (std::vector<Element *>::iterator el = recs->elements.begin(); el != recs->elements.end(); ++el)
    {
        if ((*el)->getName() == ELEMENT_FIELD_NAME)
        {
            fieldNames.push_back((*el)->getValue());
        }
        else if ((*el)->getName() == ELEMENT_RECORD)
        {
            DataRecordPtr rec = DataRecordPtr(new DataRecord(getFormat(), (*el)->getValue(), settings, true, fieldNames));
            addRecord(rec);
        }
        else
        {
            LOG_DATATABLE_WARN("Invalid element found: " + (*el)->encode(true).toUtf8());
        }
    }
}

DataTable::DataTable(TableFormatPtr format, std::list<AgObjectPtr> firstRowData)
{
    init();
    setFormat(format);
    if (firstRowData.size() > 0)
    {
        addRecord(DataRecordPtr(new DataRecord(format, firstRowData)));
    }
}

DataTable::DataTable(const AgString &data)
{
    init(data, ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)), true);
}

DataTable::DataTable(const AgString &data, bool validate)
{
    init(data, ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)), validate);
}


void DataTable::init(const AgString &data, ClassicEncodingSettingsPtr settings, bool validate)
{
    init();
    if (data.length() == 0)
    {
        return;
    }

    bool found = false;
    AgString encodedFormat;
    std::vector<AgString> fieldNames;

    ElementListPtr recs = StringUtils::elements(data, settings.get() != NULL ? settings->isUseVisibleSeparators() : false);

    for (std::vector<Element *>::iterator el = recs->elements.begin(); el != recs->elements.end(); ++el)
    {
        if ((*el)->getName().length() != 0)
        {
            if ((*el)->getName() == ELEMENT_FORMAT_ID)
            {
                int formatId = (*el)->getValue().toInt();

                if (settings->getFormatCache().get() == NULL)
                {
                    throw IllegalStateException("Can't use format ID - format cache not found");
                }

                if (encodedFormat.length()) // If format was already found in the encoded data
                {
                    TableFormatPtr format = TableFormatPtr(new TableFormat(encodedFormat, settings, validate));
                    settings->getFormatCache()->put(formatId, format);
                    continue;
                }

                TableFormatPtr format = settings->getFormatCache()->get(formatId);

                if (format.get() == NULL)
                {
                    throw IllegalStateException("Format with specified ID not found in the cache: " + AgString::fromInt(formatId));
                }

                setFormat(format);

                found = true;

                continue;
            }
            else if ((*el)->getName() == ELEMENT_FORMAT)
            {
                encodedFormat = (*el)->getValue();
                setFormat(TableFormatPtr(new TableFormat(encodedFormat, settings, validate)));
                found = true;
                continue;
            }
            else if ((*el)->getName() == ELEMENT_RECORD)
            {
                // Using table's format if encodedFormat is not NULL (i.e. was found in the encoded data)
                TableFormatPtr format = found ? getFormat() : (settings.get() != NULL ? settings->getFormat() : TableFormatPtr());

                if (format.get() == NULL)
                {
                    throw IllegalStateException("Table format is neither found in encoded table nor provided by decoding environment");
                }

                addRecord(DataRecordPtr(new DataRecord(format, (*el)->getValue(), settings, validate, fieldNames)));
                continue;
            }
            else if ((*el)->getName() == ELEMENT_FIELD_NAME)
            {
                fieldNames.push_back((*el)->getValue());
                continue;
            }
            else if ((*el)->getName() == ELEMENT_TIMESTAMP)
            {
                std::string time = (*el)->getValue().toUtf8();
                if (time[4] == '-') {
                    timestamp->setValue(DateFieldFormat::timestampFromString(time));
                } else {
                    std::time_t time_t = std::time_t(boost::lexical_cast<double>(time));
                    boost::posix_time::ptime pr = boost::posix_time::from_time_t(time_t / 1000)
                            + boost::posix_time::millisec(time_t % 1000);
                    timestamp->setValue(pr);
                }
                continue;
            }
            else if ((*el)->getName() == ELEMENT_QUALITY)
            {
                quality = (boost::lexical_cast<int>((*el)->getValue().toUtf8()));
                continue;
            }
            else if ((*el)->getName() == ELEMENT_INVALIDATOR)
            {
                invalidationMessage = (*el)->getValue();
                continue;
            }
        }
    }
}

DataTable::DataTable(const AgString &data, ClassicEncodingSettingsPtr settings, bool validate)
{
    init(data, settings, validate);
}

int DataTable::getRecordCount()
{
    return records.size();
}

int DataTable::getFieldCount()
{
    return format->getFieldCount();
}

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

FieldFormat* DataTable::getFormat(int field)
{
    return getFormat()->getField(field);
}

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

int64_t DataTable::getId()
{
    return id;
}

DataTable& DataTable::setFormat(TableFormatPtr format)
{
    if (format != NULL)
    {
        format->makeImmutable(this);
        this->format = format;
    }
    return *this;
}

void DataTable::setId(int64_t id)
{
    this->id = id;
}

bool DataTable::hasField(const AgString &field)
{
    return format->hasField(field);
}

void DataTable::setInvalidationMessage(const AgString &invalidationMessage)
{
    this->invalidationMessage = invalidationMessage;
}

void DataTable::addRecordImpl(boost::shared_ptr<int> index, DataRecordPtr record)
{
    int rc = getRecordCount();
    TableFormat tf = format.get();

    if (rc >= tf.getMaxRecords())
    {
        throw IllegalStateException(Cres::get()->getString("dtCannotAddRecord") + "maximum number of records is reached: " + AgString::fromInt(format->getMaxRecords()));
    }

    try
    {
        validateRecord(record);
    }
    catch (ValidationException &ex)
    {
        throw IllegalStateException(ex.getMessage());
    }

    if (index.get() != NULL)
    {
        records.insert(records.begin() + *index, record);
    }
    else
    {
        records.push_back(record);
    }

    record->setTable(this);
}

void DataTable::removeRecordImpl(int index)
{
    if (getRecordCount() <= format->getMinRecords())
    {
        throw IllegalStateException("Cannot remove record: minimum number of records is reached: " + AgString::fromInt(format->getMinRecords()));
    }

    records.erase(records.begin() + index);
}

AgString DataTable::toDefaultString()
{
    if (getRecordCount() == 1)
    {
        return dataAsString();
    }
    else
    {
        return MessageFormat::format(Cres::get()->getString("dtTable"), AgString::fromInt(getRecordCount()));
    }
}

ExpressionPtr DataTable::getNamingExpression()
{
    return format.get() == NULL ? ExpressionPtr() : format->getNamingExpression();
}

EvaluatorPtr DataTable::ensureEvaluator()
{
    // todo expression
    assert(0);
    /*if (namingEvaluator.get() == NULL)
    {
        DefaultReferenceResolver defaultResolver = new DefaultReferenceResolver();
        defaultResolver.setDefaultTable(this);

        namingEvaluator = new Evaluator(defaultResolver);

        namingEvaluator.setResolver(Reference.SCHEMA_ENVIRONMENT, new DataTableReferenceResolver());
    }

    return namingEvaluator;*/
    return EvaluatorPtr();
}

void DataTable::checkOrSetFormat(DataRecordPtr record)
{
    if (format->getFieldCount() != 0)
    {
        if (format.get() != record->getFormat().get())
        {
            AgString message = record->getFormat()->extendMessage(format);
            if (message.length())
            {
                throw IllegalArgumentException("Format of new record differs from format of data table " + message);
            }
         }
    }
    else
    {
        format = record->getFormat();
    }
}

DataTable& DataTable::addRecord(DataRecordPtr record)
{
    checkOrSetFormat(record);
    boost::shared_ptr<int> p(new int(0));
    addRecordImpl(p, record);
    return *this;
}

DataRecordPtr DataTable::addRecord(int cnt, ...)
{
    va_list vl;

    va_start(vl, cnt);
    DataRecordPtr rec = addRecord();
    while (cnt--)
    {
       char* str;
       str = va_arg(vl, char*);
       AgString ast = AgString((const char*) str);
       rec->addValue(ast.toObjectPtr()); // fix gcc error: cannot receive objects of non-trivially-copyable type 'AgObjectPtr {aka class boost::shared_ptr<AgObject>}' through '...'
    }
    va_end (vl);

    return rec;
}

DataTable& DataTable::addRecord(int index, DataRecordPtr record)
{
    checkOrSetFormat(record);
    addRecordImpl(boost::shared_ptr<int>(new int(index)), record);
    return *this;
}

DataRecordPtr DataTable::addRecord()
{
    if (getFormat().get() == NULL)
    {
        throw IllegalStateException("Can't add empty record because format of data table was not set");
    }
    DataRecordPtr record = DataRecordPtr(new DataRecord(getFormat()));
    boost::shared_ptr<int> p(new int(0));
    addRecordImpl(p, record);
    return record;
}

void DataTable::validate()
{
    if (isInvalid())
    {
        throw ValidationException(invalidationMessage);
    }
    std::list<TableValidatorPtr> list = getFormat()->getTableValidators();
    for (std::list<TableValidatorPtr>::iterator tv = list.begin(); tv != list.end(); ++tv)
    {
        (*tv)->validate(shared_from_this());
    }

    std::vector<FieldFormat*> &fields = getFormat()->getFields();
    for (std::vector<FieldFormat*>::iterator ff = fields.begin(); ff != fields.end(); ++ff)
    {
        if ((*ff)->getType() == AGG_GLOBAL.DATATABLE_FIELD)
        {
            for (std::vector<DataRecordPtr>::iterator rec = records.begin(); rec != records.end(); ++rec)
            {
                DataTablePtr nested = (*rec)->getDataTable((*ff)->getName());
                if (nested.get() != NULL)
                {
                    nested->validate();
                }
            }
        }
    }
    //DataTablePtr dt = DataTablePtr(DataTablePtr(new DataTable(encode(ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)))))); // Reconstruction of table from string causes its full validation*/
}

void DataTable::validateRecord(DataRecordPtr record)
{
    if (!getFormat().get())
        return;

    std::list<RecordValidatorPtr> validators = getFormat().get()->getRecordValidators();
    for (std::list<RecordValidatorPtr>::iterator rv = validators.begin(); rv != validators.end(); ++rv)
    {
        (*rv)->validate(shared_from_this(), record);
    }
}

DataTable& DataTable::setRecord(int index, DataRecordPtr record)
{
    checkOrSetFormat(record);
    records[index]->setTable(NULL);
    records[index] = record;
    record->setTable(this);
    return *this;
}

void DataTable::swapRecords(int index1, int index2)
{
    DataRecordPtr r1 = records[index1];
    DataRecordPtr r2 = records[index2];
    records[index1] = r2;
    records[index2] = r1;
}

std::vector<DataRecordPtr> &DataTable::getRecords()
{
    return records;
}

bool DataTable::isInvalid()
{
    return invalidationMessage.length();
}

AgString DataTable::getInvalidationMessage()
{
    return invalidationMessage;
}

AgDatePtr DataTable::getTimestamp()
{
//    return AgDatePtr(new AgDate(timestamp));
    return timestamp;
}

void DataTable::setTimestamp(AgDatePtr _timestamp)
{
    this->timestamp = _timestamp; //->toTime_t();
}

int DataTable::getQuality()
{
    return quality;
}

void DataTable::setQuality(int _quality)
{
    quality = _quality;
}

DataRecordPtr DataTable::getRecord(int number)
{
    return records[number];
}

DataRecordPtr DataTable::getRecordById(const AgString &id)
{
    if (!id.length())
    {
        return DataRecordPtr();
    }

    for (std::vector<DataRecordPtr>::iterator rec = records.begin(); rec != records.end(); ++rec)
    {
        if (!(*rec)->getId().empty() && (*rec)->getId() == id)
        {
            return *rec;
        }
    }

    return DataRecordPtr();
}

void DataTable::removeRecord(int index)
{
    removeRecordImpl(index);
}

void DataTable::removeRecords(DataRecordPtr rec)
{
    for (int i = records.size() - 1; i >= 0; i--)
    {
        if (Util::equals(rec, records[i]))
        {
            removeRecordImpl(i);
        }
    }
}

void DataTable::reorderRecord(DataRecordPtr /*record*/, int /*index*/)
{
    //function not used
    assert(0);
}

bool DataTable::equals(AgObject *obj)
{
    DataTable *other = dynamic_cast<DataTable *>(obj);

    if (other == NULL)
    {
        return false;
    }

    if (!format->equals(other->getFormat().get()))
    {
        return false;
    }

    if (getRecordCount() != other->getRecordCount())
    {
        return false;
    }

    for (int i = 0; i < getRecordCount(); i++)
    {
        if (!getRecord(i)->equals(other->getRecord(i)))
        {
            return false;
        }
    }
    return true;
}

AgString DataTable::getEncodedData(ClassicEncodingSettingsPtr settings)
{
    AgString encodedData;
    bool encodeFieldNames = settings.get() != NULL ? settings->isEncodeFieldNames() : true;

    if (encodeFieldNames)
    {
        for (int i = 0; i < format->getFieldCount(); i++)
        {
            Element el(AgString(ELEMENT_FIELD_NAME), format->getField(i)->getName());
            encodedData += el.encode(settings.get() != NULL ? settings->isUseVisibleSeparators() : false);
        }
    }

    for (int i = 0; i < getRecordCount(); i++)
    {
        Element el(AgString(ELEMENT_RECORD), getRecord(i)->encode(settings));
        encodedData += el.encode(settings.get() != NULL ? settings->isUseVisibleSeparators() : false);
    }

    return encodedData;
}

AgString DataTable::encode()
{
    return encode(ClassicEncodingSettingsPtr(new ClassicEncodingSettings(false)));
}

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

AgString DataTable::encode(ClassicEncodingSettingsPtr settings)
{
    boost::shared_ptr<int> formatId;
    AgString res;

    bool needToInsertFormat = settings.get() != NULL && settings->isEncodeFormat();
    if (needToInsertFormat)
    {
        if (getFormat()->getFieldCount() > 0 && settings->getFormatCache().get() != NULL)
        {
            formatId = settings->getFormatCache()->getId(getFormat());

            if (formatId.get() == NULL)
            {
                // need check formatId.get() == NULL
                assert(0);
                formatId = settings->getFormatCache()->add(getFormat());
            }

            if (settings->getKnownFormatCollector().get() != NULL)
            {
                needToInsertFormat = false;

                if (settings->getKnownFormatCollector()->isKnown(*formatId) && settings->getKnownFormatCollector()->isMarked(*formatId))
                {
                    // Format is known - inserting ID only
                    Element el(AgString(ELEMENT_FORMAT_ID), AgString::fromInt(*formatId));
                    res += el.encode(settings->isUseVisibleSeparators());

                    if (Log::PROTOCOL_CACHING.isDebugEnabled())
                    {
                        LOG_PROTOCOL_CACHING_DEBUG("Using cached format #" + AgString::fromInt(*formatId).toUtf8() + ": " + getFormat()->encode(settings).toUtf8());
                    }
                }
                else
                {
                    AgString encodedFormat = getFormat()->encode(settings);

                    // Format is not known - inserting both format and ID
                    Element el(AgString(ELEMENT_FORMAT), encodedFormat);
                    res += el.encode(settings->isUseVisibleSeparators());
                    Element el2(AgString(ELEMENT_FORMAT_ID), AgString::fromInt(*formatId));
                    res += el2.encode(settings->isUseVisibleSeparators());

                    if (Log::PROTOCOL_CACHING.isDebugEnabled())
                    {
                        LOG_PROTOCOL_CACHING_DEBUG("Marking format #" + AgString::fromInt(*formatId).toUtf8() + " as known for caller: " + encodedFormat.toUtf8());
                    }
                }
            }
        }

        if (needToInsertFormat)
        {
            AgString encodedFormat = getFormat()->encode(settings);
            Element el(AgString(ELEMENT_FORMAT), encodedFormat);
            res += el.encode(settings->isUseVisibleSeparators());
        }
    }

    res += getEncodedData(settings);

    if (isInvalid())
    {
        Element el(AgString(ELEMENT_FORMAT), invalidationMessage);
        res += el.encode(settings->isUseVisibleSeparators());
    }

    if (timestamp != 0)
    {

        Element el(AgString(ELEMENT_TIMESTAMP), timestamp->toTimestampString());
        res += el.encode(settings->isUseVisibleSeparators());
    }

    if (quality > -1)
    {
        Element el(AgString(ELEMENT_QUALITY), boost::lexical_cast<std::string>(quality));
        res += el.encode(settings->isUseVisibleSeparators());
    }

    if (formatId.get() != NULL)
    {
        // Marking format as known
        settings->getKnownFormatCollector()->makeKnown(*formatId, true);
    }

    return res;
}

AgString DataTable::toString()
{
    //todo exoressions
    /*if (getNamingExpression().get() != NULL)
    {
        return getDescription();
    }
    else*/
    {
        return toDefaultString();
    }
}

AgString DataTable::getDescription()
{
    ExpressionPtr namingExpression = getNamingExpression();

    if (namingExpression.get() == NULL)
    {
        return toDefaultString();
    }

    EvaluatorPtr evaluator = ensureEvaluator();

    AgObjectPtr name;
    try
    {
        name = evaluator->evaluate(namingExpression);
    }
    catch (AggreGateException &ex)
    {
        UNUSED(ex);
        LOG_CORE_DEBUG("Error evaluating naming expression of table '" + toDefaultString().toUtf8() + "'");
        return toDefaultString();
    }

    return name.get() == NULL ? "" : name->toString();
}

void DataTable::fixRecords()
{
    getFormat()->fixRecords(this);
}

AgString DataTable::dataAsString()
{
    return dataAsString(true, false);
}

AgString DataTable::dataAsString(bool showFieldNames, bool showHiddenFields)
{
    AgString res;
    AgString recordSeparator = getFieldCount() > 1 ? " | " : ", ";

    for (int i = 0; i < getRecordCount(); i++)
    {
        if (i > 0)
        {
            res += recordSeparator;
        }

        DataRecordPtr rec = getRecord(i);
        res += rec->dataAsString(showFieldNames, showHiddenFields);
    }

    return res;
}

bool DataTable::isOneCellTable()
{
    return getFieldCount() == 1 && getRecordCount() == 1;
}

bool DataTable::conform(TableFormatPtr rf)
{
    return !conformMessage(rf).length();
}

AgString DataTable::conformMessage(TableFormatPtr rf)
{
    if (getRecordCount() < rf->getMinRecords())
    {
        return "Number of records too small: need " + AgString::fromInt(rf->getMinRecords()) + " or more, found " + AgString::fromInt(getRecordCount());
    }

    if (getRecordCount() > rf->getMaxRecords())
    {
        return "Number of records too big: need " + AgString::fromInt(rf->getMaxRecords()) + " or less, found " + AgString::fromInt(getRecordCount());
    }

    if (getFormat().get())
        return getFormat()->extendMessage(rf);
    else
        return AgString("");
}

std::list<DataRecordPtr> DataTable::selectAll(DataTableQueryPtr query)
{
    UNUSED(query);
    //not used
    assert(0);
    return std::list<DataRecordPtr>();
}

boost::shared_ptr<int> DataTable::findIndex(DataTableQueryPtr query)
{
    for (int i = 0; i < getRecordCount(); i++)
    {
        bool meet = true;

        DataRecordPtr rec = getRecord(i);

        std::list<QueryConditionPtr> conds = query->getConditions();
        for (std::list<QueryConditionPtr>::iterator cond = conds.begin(); cond != conds.end(); ++cond)
        {
            if (!rec->meetToCondition(*cond))
            {
                meet = false;
            }
        }

        if (meet)
        {
            return boost::shared_ptr<int>(new int(i));
        }
     }

     return boost::shared_ptr<int>(new int(0));;
}

DataRecordPtr DataTable::select(DataTableQueryPtr query)
{
    for (std::vector<DataRecordPtr>::iterator rec = records.begin(); rec != records.end(); ++rec)
    {
        bool meet = true;

        std::list<QueryConditionPtr> conds = query->getConditions();
        for (std::list<QueryConditionPtr>::iterator cond = conds.begin(); cond != conds.end(); ++cond)
        {
            if (!(*rec)->meetToCondition(*cond))
            {
                meet = false;
            }
        }

        if (meet)
        {
            return *rec;
        }
    }

    return DataRecordPtr();
}

DataRecordPtr DataTable::select(const AgString &field, AgObjectPtr value)
{
    QueryConditionPtr cond = QueryConditionPtr(new QueryCondition(field, value));
    return select(DataTableQueryPtr(new DataTableQuery(cond)));
}

boost::shared_ptr<int> DataTable::findIndex(const AgString &field, AgObjectPtr value)
{
    QueryConditionPtr cond = QueryConditionPtr(new QueryCondition(field, value));
    return findIndex(DataTableQueryPtr(new DataTableQuery(cond)));
}

void DataTable::sort(const AgString &/*field*/, bool /*ascending*/)
{
    //todo
    assert(0);
}

void DataTable::sort(DataTableSorterPtr /*sorter*/)
{
    //todo
    assert(0);
}

DataRecordPtr DataTable::rec()
{
    return getRecord(0);
}

AgObjectPtr DataTable::get()
{
    return getRecord(0)->getValue(0);
}

void DataTable::splitFormat()
{
    for (std::vector<DataRecordPtr>::iterator rec = records.begin(); rec != records.end(); ++rec)
    {
        (*rec)->cloneFormatFromTable();
    }
}

void DataTable::joinFormats()
{
    for (std::vector<DataRecordPtr>::iterator rec = records.begin(); rec != records.end(); ++rec)
    {
        (*rec)->setFormat(this->getFormat());
    }
}

std::vector<DataRecordPtr>::iterator DataTable::iteratorBegin()
{
    return records.begin();
}

std::vector<DataRecordPtr>::iterator DataTable::iteratorEnd()
{
    return records.end();
}

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

    cl->id = id;
    if (format.get() != NULL)
    {
        cl->format = TableFormatPtr(format->clone());
    }
    cl->invalidationMessage = invalidationMessage;

    for (std::vector<DataRecordPtr>::iterator it = records.begin(); it != records.end(); ++it)
    {
        DataRecordPtr drp = DataRecordPtr((*it)->clone());
        drp->setFormat(cl->format);
        drp->setTable(cl);
        cl->records.push_back(drp);
    }

    for (std::vector<DataRecordPtr>::iterator it = cl->records.begin(); it != cl->records.end(); ++it)
    {
        (*it)->setTable(cl);
    }

    return cl;
}

int DataTable::compareTo(Comparable *obj)
{
    DataTable *other = dynamic_cast<DataTable *>(obj);
    assert(other);
    AgString str1 = dataAsString();
    AgString str2 = other->dataAsString();
    return str1.compareTo(&str2);
}

std::string DataTable::getClass()
{
    return typeid(DataTable).name();
}

int DataTable::hashCode()
{
    assert(0);
    return 0;
}

void DataTable::append(DataTablePtr src)
{
    std::vector<DataRecordPtr> recs = src->getRecords();
    for (std::vector<DataRecordPtr>::iterator rec = recs.begin(); rec != recs.end(); ++rec)
    {
        this->addRecord(*rec);
    }
}


void DataTable::removeIterator(std::vector<DataRecordPtr>::iterator it)
{
    records.erase(it);
}
