#ifndef AgentContextH
#define AgentContextH

#include "util/Pointers.h"
#include "util/simpleobject/AgLong.h"
#include "util/simpleobject/AgString.h"
#include "util/simpleobject/AgLong.h"
#include "util/simpleobject/AgDate.h"
#include "protocol/RemoteServer.h"
#include "context/AbstractContext.h"
#include "context/FunctionImplementation.h"
#include "datatable/DataRecord.h"
#include "device/agent/AgentController.h"

#include <boost/function.hpp>
#include <boost/bind.hpp>

class AgentContext : public AbstractContext
{

public:

    static AgString V_DATE() { return "date"; }

    static AgString F_LOGIN() { return "login"; }
    static AgString F_REGISTER() { return "register"; }
    static AgString F_SYNCHRONIZED() { return "synchronized"; }
    static AgString F_CONFIRM_EVENT() { return "confirmEvent"; }
    static AgString F_ACKNOWLEDGE_EVENT() { return "acknowledgeEvent"; }

    static AgString E_EVENT_CONFIRMED() { return "eventConfirmed"; }
    static AgString E_EVENT_ACKNOWLEDGED() { return "eventAcknowledged"; }

    static AgString FIF_LOGIN_CHALLENGE() { return "challenge"; }

    static AgString FIF_CONFIRM_EVENT_ID() { return "id"; }

    static AgString FIF_ACKNOWLEDGE_EVENT_ID() { return "id"; }
    static AgString FIF_ACKNOWLEDGE_EVENT_DATE() { return "date"; }
    static AgString FIF_ACKNOWLEDGE_EVENT_AUTHOR() { return "author"; }
    static AgString FIF_ACKNOWLEDGE_EVENT_ACKNOWLEDGEMENT() { return "acknowledgement"; }
    static AgString FIF_ACKNOWLEDGE_EVENT_EVENT_DATA() { return "eventData"; }

    static AgString FOF_LOGIN_OWNER() { return "owner"; }
    static AgString FOF_LOGIN_NAME() { return "name"; }
    static AgString FOF_LOGIN_RESPONSE() { return "response"; }

    static AgString FOF_REGISTER_PASSWORD() { return "password"; }

    static AgString EF_EVENT_CONFIRMED_ID() { return "id"; }

    static AgString V_ASSETS() { return "assets"; }

    static AgString FIELD_ID() { return "id"; }
    static AgString FIELD_ENABLED() { return "enabled"; }
    static AgString FIELD_CHILDREN() { return "children"; }
    static AgString FIELD_DESCRIPTION() { return "description"; }

    static AgString AGGREGATE() { return "aggregate"; }

    static AgString RECORDS() { return "records"; }

    static AgString PROPERTY_ENABLED() { return "enabled"; }

    static TableFormatPtr FIFT_LOGIN;
    static TableFormatPtr FOFT_LOGIN;
    static TableFormatPtr FOFT_REGISTER;
    static TableFormatPtr FIFT_CONFIRM_EVENT;
    static TableFormatPtr FIFT_ACKNOWLEDGE_EVENT;
    static TableFormatPtr EFT_EVENT_CONFIRMED;
    static TableFormatPtr FOFT_ASSET;

    AgentContext(RemoteServerPtr server, AgString name, bool eventConfirmation);
    virtual ~AgentContext() {}

    virtual void setupMyself();

    void setSynchronized(bool isSynchronized);
    bool isSynchronized();

    RemoteServerPtr getServer();

    void confirmEvent(AgLong id);

    void acknowledgeEvent(AgString id, AgDate date, AgString author, AgString acknowledgement, DataTablePtr data);

private:

    static TableFormatPtr initFIFT_LOGIN();
    static TableFormatPtr initFOFT_LOGIN();
    static TableFormatPtr initFOFT_REGISTER();
    static TableFormatPtr initFIFT_CONFIRM_EVENT();
    static TableFormatPtr initFIFT_ACKNOWLEDGE_EVENT();
    static TableFormatPtr initEFT_EVENT_CONFIRMED();
    static TableFormatPtr initFOFT_ASSET();

    RemoteServerPtr server;

    AgString name;

    bool eventConfirmation;

    bool issynchronized;

    std::list<DeviceAssetDefinitionPtr> assets;
};

class NewFunctionImplementationLogin : public FunctionImplementation
{
private:
    AgString password;
    AgString username;
    AgString name;

public:
    NewFunctionImplementationLogin(AgString password, AgString username, AgString name)
    {
        this->password = password;
        this->username = username;
        this->name = name;
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/,
                                 RequestControllerPtr /*request*/, DataTablePtr parameters)
    {
        std::string challenge = parameters->rec()->getString(AgentContext::FIF_LOGIN_CHALLENGE()).toUtf8().c_str();

        std::string temp;

        temp.append(challenge);
        temp.append(password.toUtf8().c_str());

        std::string response = AgentController::calculatemd5(temp);

        DataRecordPtr dr = DataRecordPtr(new DataRecord(AgentContext::FOFT_LOGIN));

        dr->addString(username);
        dr->addString(name);
        dr->addString(response);

        DataTablePtr dt = DataTablePtr(new DataTable(AgentContext::FOFT_LOGIN));
        dt->addRecord(dr);

        return dt;
    }

    virtual bool equals(FunctionImplementation* /*other*/)
    {
        return false;
    }
};

class NewFunctionImplementationRegister : public FunctionImplementation
{
private:
    AgString password;

public:
    NewFunctionImplementationRegister(AgString password)
    {
        this->password = password;
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/,
                                 RequestControllerPtr /*request*/, DataTablePtr /*parameters*/)
    {
        DataRecordPtr dr = DataRecordPtr(new DataRecord(AgentContext::FOFT_REGISTER));

        dr->addString(password);

        DataTablePtr dt = DataTablePtr(new DataTable(AgentContext::FOFT_REGISTER));
        dt->addRecord(dr);

        return dt;
    }

    virtual bool equals(FunctionImplementation* /*other*/)
    {
        return false;
    }
};

class NewFunctionImplementationSync : public FunctionImplementation
{
private:
    boost::function<void(bool)> setSynchronized;

public:
    NewFunctionImplementationSync(AgentContext* agentContext)
    {
        setSynchronized = boost::bind(&AgentContext::setSynchronized, agentContext, _1);
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/,
                                 RequestControllerPtr /*request*/, DataTablePtr /*parameters*/)
    {
        setSynchronized(true);
        return DataTablePtr();
    }

    virtual bool equals(FunctionImplementation* /*other*/)
    {
        return false;
    }
};

class NewFunctionImplementationConf : public FunctionImplementation
{
private:
    boost::function<void(AgLong)> confirmEvent;

public:
    NewFunctionImplementationConf(AgentContext* agentContext)
    {
        confirmEvent = boost::bind(&AgentContext::confirmEvent, agentContext, _1);
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/,
                                 RequestControllerPtr /*request*/, DataTablePtr parameters)
    {
        confirmEvent(parameters->rec()->getLong(AgentContext::FIF_CONFIRM_EVENT_ID()));
        return DataTablePtr();
    }

    virtual bool equals(FunctionImplementation* /*other*/)
    {
        return false;
    }
};

class NewFunctionImplementationAck : public FunctionImplementation
{
private:
    boost::function<void(AgString, AgDate, AgString, AgString, DataTablePtr)> acknowledgeEvent;

public:
    NewFunctionImplementationAck(AgentContext* agentContext)
    {
        acknowledgeEvent = boost::bind(&AgentContext::acknowledgeEvent, agentContext, _1, _2, _3, _4, _5);
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/,
                                 RequestControllerPtr /*request*/, DataTablePtr parameters)
    {
        AgLong id = parameters->rec()->getLong(AgentContext::FIF_ACKNOWLEDGE_EVENT_ID());
        AgDate date = parameters->rec()->getDate(AgentContext::FIF_ACKNOWLEDGE_EVENT_DATE());
        AgString author = parameters->rec()->getString(AgentContext::FIF_ACKNOWLEDGE_EVENT_AUTHOR());
        AgString acknowledgement = parameters->rec()->getString(AgentContext::FIF_ACKNOWLEDGE_EVENT_ACKNOWLEDGEMENT());
        DataTablePtr data = parameters->rec()->getDataTable(AgentContext::FIF_ACKNOWLEDGE_EVENT_EVENT_DATA());
        acknowledgeEvent(id.toString(), date, author, acknowledgement, data);
        return DataTablePtr();
    }

    virtual bool equals(FunctionImplementation* /*other*/)
    {
        return false;
    }
};

#endif
