/*!
    \author Vitaly Gribko (vitaliy.gribko@tibbo.com)
*/

#ifndef DemoAgentH
#define DemoAgentH

#include "GlobalVars.h"

#include "util/Pointers.h"
#include "util/simpleobject/AgInteger.h"
#include "util/simpleobject/AgDouble.h"
#include "protocol/RemoteServer.h"
#include "protocol/RemoteServerController.h"
#include "context/ContextUtils.h"
#include "context/VariableDefinition.h"
#include "context/VariableSetter.h"
#include "context/VariableGetter.h"
#include "context/FunctionDefinition.h"
#include "context/FunctionImplementation.h"
#include "event/EventLevel.h"
#include "datatable/FieldFormat.h"
#include "datatable/TableFormat.h"
#include "agent/Agent.h"
#include "agent/AgentContext.h"

#include <boost/thread/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/random.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>

class DemoAgent
{

public:

    DemoAgent();

    virtual ~DemoAgent() {}

    void setPeriod(long period);

    long getPeriod();

    // DataTablePtr getAsset();

private:

    void InitializeAgentContext(Context *context);

    void AgentThread();

    void EventThread(AgentPtr context, TableFormatPtr tf);

    void AsyncUpdateThread(AgentPtr context);

    RemoteServerPtr rls;

    AgentContext *agentContext;

    AgentPtr agent;

    AgString eventName;

    long eventPeriod;

    // DataTablePtr asset;
};

class VariableGetterTest : public VariableGetter
{

private:

    TableFormatPtr tf;
    boost::function<long()> getPeriod;

public:

    VariableGetterTest(DemoAgent *agent, TableFormatPtr tf)
    {
        getPeriod = boost::bind(&DemoAgent::getPeriod, agent);
        this->tf = tf;
    }

    virtual  DataTablePtr get(Context* /*con*/, VariableDefinitionPtr /*def*/, CallerControllerPtr /*caller*/, RequestControllerPtr /*request*/)
    {
        DataRecordPtr dr = DataRecordPtr(new DataRecord(tf));

        dr->addLong(getPeriod());

        DataTablePtr dt = DataTablePtr(new DataTable(tf));
        dt->addRecord(dr);

        return dt;
    }
};

class VariableSetterTest : public VariableSetter
{

private:

    boost::function<void(long)> setPeriod;

public:

    VariableSetterTest(DemoAgent *agent)
    {
        setPeriod = boost::bind(&DemoAgent::setPeriod, agent, _1);
    }

    virtual  bool set(Context* /*con*/, VariableDefinitionPtr /*def*/, CallerControllerPtr /*caller*/, RequestControllerPtr /*request*/, DataTablePtr value)
    {
        setPeriod((long)value->rec()->getLong(0));

        return true;
    }
};

class FunctionImplementationTest : public FunctionImplementation
{

private:

    TableFormatPtr tf;

public:

    FunctionImplementationTest(TableFormatPtr tf)
    {
        this->tf = tf;
    }

    virtual DataTablePtr execute(Context* /*con*/, FunctionDefinitionPtr /*def*/, CallerControllerPtr /*caller*/, RequestControllerPtr /*request*/, DataTablePtr parameters)
    {
        double res = 0, val = 0;
        val = parameters->rec()->getDouble(0);

        if (val > 0)
            res = sqrt(val);
        else
            res = -1;

        DataRecordPtr dr = DataRecordPtr(new DataRecord(tf));

        dr->addDouble(res);

        DataTablePtr dt = DataTablePtr(new DataTable(tf));
        dt->addRecord(dr);

        return dt;
    }

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

class DefaultContextEventListenerConf : public DefaultContextEventListener
{
    virtual void handle(EventPtr event)
    {
        std::string res;

        res.append("Server has confirmed event with ID: ");
        res.append(event->getData()->rec()->getString(0).toUtf8().c_str());

        printf("%s\n", res.c_str());
    }
};

class DefaultContextEventListenerAck : public DefaultContextEventListener
{
    virtual void handle(EventPtr event)
    {
        std::string res;

        res.append("User has confirmed event with data: ");
        res.append(event->getData()->toString().toUtf8().c_str());

        printf("%s\n", res.c_str());
    }
};

// class VariableGetterAsset : public VariableGetter
// {
//
// private:
//
//     TableFormatPtr tf;
//     boost::function<DataTablePtr()> getAsset;
//
// public:
//
//     VariableGetterAsset(DemoAgent *agent, TableFormatPtr tf)
//     {
//         getAsset = boost::bind(&DemoAgent::getAsset, agent);
//         this->tf = tf;
//     }
//
//     virtual  DataTablePtr get(Context* /*con*/, VariableDefinitionPtr /*def*/, CallerControllerPtr /*caller*/, RequestControllerPtr /*request*/)
//     {
//         return getAsset();
//     }
// };

#endif
