#include "datatable/DataTableBindingProvider.h"
#include "datatable/DataTable.h"
#include "datatable/TableFormat.h"
#include "datatable/FieldFormat.h"
#include "datatable/DataRecord.h"
#include "binding/EvaluationOptions.h"
#include "binding/BindingException.h"
#include "binding/ReferenceListener.h"
#include "util/simpleobject/AgBoolean.h"
#include "util/Log.h"
#include "Cres.h"

DataTableBindingProvider::DataTableBindingProvider(DataTablePtr table) : headless(false)
{
    this->table = table;
}

DataTableBindingProvider::DataTableBindingProvider(DataTablePtr table, ErrorCollectorPtr errorCollector)
    : AbstractDataTableBindingProvider(errorCollector)
{
    this->headless = true;
    this->table = table;
}

std::map<BindingPtr, EvaluationOptionsPtr> DataTableBindingProvider::createBindings()
{
    static EvaluationOptionsPtr EVALUATION_OPTIONS;

    if (EVALUATION_OPTIONS.get() == NULL)
    {
        EVALUATION_OPTIONS = EvaluationOptionsPtr(new EvaluationOptions(true, false, 0));
    }

    std::map<BindingPtr, EvaluationOptionsPtr> bm;

    std::list<BindingPtr> &bindings = table->getFormat()->getBindings();
    for (std::list<BindingPtr>::iterator b = bindings.begin();  b != bindings.end(); ++b)
    {
        bm[*b] = EVALUATION_OPTIONS;
    }
    return bm;
}

void DataTableBindingProvider::stop()
{
}

void DataTableBindingProvider::start()
{
    for (int i = 0; i < table->getRecordCount(); i++)
    {
        for (int k = 0; k < table->getFormat()->getFieldCount(); k++)
        {
            processBindings(table->getFormat()->getField(k)->getName(), i, true);
        }
    }
}

void DataTableBindingProvider::writeReference(int method, ReferencePtr ref, ReferencePtr cause, AgObjectPtr value, ChangeCachePtr cache)
{
    UNUSED(method);
    UNUSED(ref);
    UNUSED(cause);
    UNUSED(value);
    UNUSED(cache);
    //todo
    assert(0);
}

void DataTableBindingProvider::callReferenceChanged(ReferencePtr cause, int method, ReferenceListenerPtr listener)
{
    try
    {
        listener->referenceChanged(cause, std::map<AgString, AgObjectPtr>(), ChangeCachePtr());
    }
    catch (BindingException &ex)
    {
        processError(listener->getBinding(), method, cause, ex);
    }
}

ReferenceWriterPtr DataTableBindingProvider::getExternalReferenceWriter()
{
    return ReferenceWriterPtr();
}

void DataTableBindingProvider::setCellValue(AgObjectPtr value, int row, const AgString &field)
{
    table->getRecord(row)->setValue(field, value);
}

void DataTableBindingProvider::setEnabled(AgObjectPtr value, int row, const AgString &field)
{
    UNUSED(value);
    UNUSED(row);
    UNUSED(field);
}

void DataTableBindingProvider::setHidden(AgObjectPtr value, int row, const AgString &field)
{
    if (headless)
    {
        return; // Format will be immutable in headless mode
    }

    AgBoolean *hidden = dynamic_cast<AgBoolean *>(value.get());
    assert(hidden);

    try
    {
        FieldFormat* ff = getFieldFormat(row, field);
        if (ff != NULL)
        {
            ff->setHidden(hidden->getValue());
        }
    }
    catch (BindingException &ex)
    {
        UNUSED(ex);
        LOG_BINDINGS_ERROR("Error hidden status setting field " + field.toUtf8() + " in row " + AgString::fromInt(row).toUtf8() + " to " + hidden->toString().toUtf8() +" " +  ex.getMessage().toUtf8());
    }
}

void DataTableBindingProvider::setOptions(AgObjectPtr value, int row, const AgString &field)
{
    UNUSED(value);
    UNUSED(row);
    UNUSED(field);
}

void DataTableBindingProvider::setSelectionValues(AgObjectPtr value, int row, const AgString &field)
{
    UNUSED(value);
    UNUSED(row);
    UNUSED(field);
}

void DataTableBindingProvider::setEditorEnabled(bool enabled)
{
    UNUSED(enabled);
}

FieldFormat* DataTableBindingProvider::getFieldFormat(int row, const AgString &field)
{
    if (row >= table->getRecordCount())
    {
        throw BindingException(Cres::get()->getString("dtRecordNotAvail") + AgString::fromInt(row));
    }

    FieldFormat* ff = table->getRecord(row)->getFormat()->getField(field);

    if (ff == NULL)
    {
        throw BindingException(Cres::get()->getString("dtFieldNotAvail") + field);
    }

    return ff;
}
