#include "AgentContext.h"
#include "Cres.h"
#include "server/DeviceContextConstants.h"
#include "context/Contexts.h"
#include "context/FunctionDefinition.h"
#include "context/EventDefinition.h"
#include "datatable/DataTable.h"

TableFormatPtr AgentContext::initFIFT_LOGIN()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + FIF_LOGIN_CHALLENGE() + "><S>");

    return t;
}

TableFormatPtr AgentContext::initFOFT_LOGIN()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + FOF_LOGIN_OWNER() + "><S>");
    t->addField("<" + FOF_LOGIN_NAME() + "><S>");
    t->addField("<" + FOF_LOGIN_RESPONSE() + "><S>");

    return t;
}

TableFormatPtr AgentContext::initFOFT_REGISTER()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + FOF_REGISTER_PASSWORD() + "><S>");

    return t;
}

TableFormatPtr AgentContext::initFIFT_CONFIRM_EVENT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + FIF_CONFIRM_EVENT_ID() + "><L>");

    return t;
}

TableFormatPtr AgentContext::initFIFT_ACKNOWLEDGE_EVENT()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + FIF_ACKNOWLEDGE_EVENT_ID() + "><L><F=N>");
    t->addField("<" + FIF_ACKNOWLEDGE_EVENT_DATE() + "><D>");
    t->addField("<" + FIF_ACKNOWLEDGE_EVENT_AUTHOR() + "><S><F=N>");
    t->addField("<" + FIF_ACKNOWLEDGE_EVENT_ACKNOWLEDGEMENT() + "><S>");
    t->addField("<" + FIF_ACKNOWLEDGE_EVENT_EVENT_DATA() + "><T>");

    return t;
}

TableFormatPtr AgentContext::initEFT_EVENT_CONFIRMED()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->setMaxRecords(1);
    t->setMinRecords(1);

    t->addField("<" + EF_EVENT_CONFIRMED_ID() + "><L>");

    return t;
}

TableFormatPtr AgentContext::initFOFT_ASSET()
{
    TableFormatPtr t = TableFormatPtr(new TableFormat());

    t->addField("<" + FIELD_ID() + "><S><F=HRK>");
    t->addField("<" + FIELD_DESCRIPTION() + "><S><F=R><D=" + Cres::get()->getString("description") + ">");
    t->addField("<" + FIELD_ENABLED() + "><B><A=1><D=" + Cres::get()->getString("enabled") + ">");

    t->addField("<" + FIELD_CHILDREN() + "><T><F=N><D=" + Cres::get()->getString("devNestedAssets") + ">");

    t->setNamingExpression(AGGREGATE() + "({}, \"{env/previous} + ({" + FIELD_ENABLED() + "} ? 1 : 0)\", 0) + '/' + {#" + RECORDS() + "}");

    AgString reff = FIELD_CHILDREN() + "#" + PROPERTY_ENABLED();
    AgString exp = "{" + FIELD_ENABLED() + "}";

    t->addBinding(reff, exp);

    return t;
}

AgentContext::AgentContext(RemoteServerPtr server, AgString name, bool eventConfirmation) :
    AbstractContext(Contexts::CTX_ROOT)
{
    this->server = server;
    this->name = name;
    this->eventConfirmation = eventConfirmation;
}

void AgentContext::setupMyself()
{
    typedef boost::shared_ptr<NewFunctionImplementationLogin> NewFunctionImplementationLoginPtr;
    NewFunctionImplementationLoginPtr loginImpl =
            NewFunctionImplementationLoginPtr(new NewFunctionImplementationLogin(server->getPassword(), server->getUsername(), name));

    typedef boost::shared_ptr<NewFunctionImplementationRegister> NewFunctionImplementationRegisterPtr;
    NewFunctionImplementationRegisterPtr registerImpl =
            NewFunctionImplementationRegisterPtr(new NewFunctionImplementationRegister(server->getPassword()));

    typedef boost::shared_ptr<NewFunctionImplementationSync> NewFunctionImplementationSyncPtr;
    NewFunctionImplementationSyncPtr synchronizedImpl = NewFunctionImplementationSyncPtr(new NewFunctionImplementationSync(this));

    typedef boost::shared_ptr<NewFunctionImplementationConf> NewFunctionImplementationConfPtr;
    NewFunctionImplementationConfPtr confirmEventImpl = NewFunctionImplementationConfPtr(new NewFunctionImplementationConf(this));

    typedef boost::shared_ptr<NewFunctionImplementationAck> NewFunctionImplementationAckPtr;
    NewFunctionImplementationAckPtr acknowledgeEventImpl = NewFunctionImplementationAckPtr(new NewFunctionImplementationAck(this));

    AbstractContext::setupMyself();

    TableFormatPtr EMPTY_FORMAT = TableFormatPtr(new TableFormat(0, 0));

    if (eventConfirmation)
    {
        EventDefinitionPtr edCon = EventDefinitionPtr(new EventDefinition(E_EVENT_CONFIRMED(), EFT_EVENT_CONFIRMED));
        addEventDefinition(edCon);
    }

    EventDefinitionPtr edAck = EventDefinitionPtr(new EventDefinition(E_EVENT_ACKNOWLEDGED(), FIFT_ACKNOWLEDGE_EVENT));
    addEventDefinition(edAck);

    FunctionDefinitionPtr fdLogin = FunctionDefinitionPtr(new FunctionDefinition(F_LOGIN(), FIFT_LOGIN, FOFT_LOGIN, Cres::get()->getString("login")));
    fdLogin->setImplementation(loginImpl);
    addFunctionDefinition(fdLogin);

    FunctionDefinitionPtr fdRegister =
            FunctionDefinitionPtr(new FunctionDefinition(F_REGISTER(), EMPTY_FORMAT, FOFT_REGISTER, Cres::get()->getString("register")));
    fdRegister->setImplementation(registerImpl);
    addFunctionDefinition(fdRegister);

    FunctionDefinitionPtr fdSync = FunctionDefinitionPtr(new FunctionDefinition(F_SYNCHRONIZED(), EMPTY_FORMAT, EMPTY_FORMAT));
    fdSync->setImplementation(synchronizedImpl);
    addFunctionDefinition(fdSync);

    FunctionDefinitionPtr fdConf = FunctionDefinitionPtr(new FunctionDefinition(F_CONFIRM_EVENT(), FIFT_CONFIRM_EVENT, EMPTY_FORMAT));
    fdConf->setImplementation(confirmEventImpl);
    addFunctionDefinition(fdConf);

    FunctionDefinitionPtr fdAck = FunctionDefinitionPtr(new FunctionDefinition(F_ACKNOWLEDGE_EVENT(), FIFT_ACKNOWLEDGE_EVENT, EMPTY_FORMAT));
    fdAck->setImplementation(acknowledgeEventImpl);
    addFunctionDefinition(fdAck);
}

void AgentContext::setSynchronized(bool isSynchronized)
{
    issynchronized = isSynchronized;
}

bool AgentContext::isSynchronized()
{
    return issynchronized;
}

RemoteServerPtr AgentContext::getServer()
{
    return server;
}

void AgentContext::confirmEvent(AgLong id)
{
    if (getEventDefinition(E_EVENT_CONFIRMED()))
    {
        fireEvent(E_EVENT_CONFIRMED(), id.toString());
    }
}

void AgentContext::acknowledgeEvent(AgString id, AgDate date, AgString author, AgString acknowledgement, DataTablePtr data)
{
    if (getEventDefinition(E_EVENT_ACKNOWLEDGED()))
    {
        std::list<AgObjectPtr> rowData;
        rowData.push_back(AgObjectPtr(new AgString(id)));
        rowData.push_back(AgObjectPtr(new AgDate(date)));
        rowData.push_back(AgObjectPtr(new AgString(author)));
        rowData.push_back(AgObjectPtr(new AgString(acknowledgement)));
        rowData.push_back(data);

        fireEvent(E_EVENT_ACKNOWLEDGED(), AbstractContext::DEFAULT_EVENT_LEVEL,
                  DataTablePtr(new DataTable(getEventDefinition(E_EVENT_ACKNOWLEDGED())->getFormat(), rowData)));
    }
}

TableFormatPtr AgentContext::FIFT_LOGIN = AgentContext::initFIFT_LOGIN();
TableFormatPtr AgentContext::FOFT_LOGIN = AgentContext::initFOFT_LOGIN();
TableFormatPtr AgentContext::FOFT_REGISTER = AgentContext::initFOFT_REGISTER();
TableFormatPtr AgentContext::FIFT_CONFIRM_EVENT = AgentContext::initFIFT_CONFIRM_EVENT();
TableFormatPtr AgentContext::FIFT_ACKNOWLEDGE_EVENT = AgentContext::initFIFT_ACKNOWLEDGE_EVENT();
TableFormatPtr AgentContext::EFT_EVENT_CONFIRMED = AgentContext::initEFT_EVENT_CONFIRMED();
TableFormatPtr AgentContext::FOFT_ASSET = AgentContext::initFOFT_ASSET();
