#ifndef DefaultClientControllerH
#define DefaultClientControllerH

#include "protocol/AbstractClientController.h"
#include "context/Context.h"
#include "context/ContextManager.h"
#include "context/DefaultContextEventListener.h"
#include "event/ContextEventListener.h"
#include "protocol/IncomingAggreGateCommand.h"
#include "protocol/OutgoingAggreGateCommand.h"
#include "util/Pointers.h"
#include "util/ThreadPoolCachedEx.h"
#include "AggreGateException.h"
#include <queue>

class ForwardingEventListener;
class ProcessCommandTask;

class DefaultClientController : public AbstractClientController
{
private:

    BlockingChannelPtr dataChannel;
	CommandParserPtr commandParser;
	bool startMessageReceived;
	bool shutDown;
	ContextEventListenerPtr defaultEventListener;

    boost::shared_ptr<ThreadPoolCachedEx> commandExecutionService;

    boost::mutex pendingCommandsQueueMutex;
    std::queue<OutgoingAggreGateCommandPtr> pendingCommandsQueue;


	void processPendingEvents();	
    virtual bool controllerShouldHandle(EventPtr /*ev*/, ContextEventListener* /*listener*/) { return false; }

protected:
    Context* getContext(const AgString &path);
    virtual void processMessageOperation(IncomingAggreGateCommandPtr cmd, OutgoingAggreGateCommandPtr ans) ;
    void addCustomListeners(Context* con);
	void processMessage(IncomingAggreGateCommandPtr cmd, OutgoingAggreGateCommandPtr ans);
    AgString getErrorDetails(AggreGateException/*Throwable*/ error);

public:
    ContextEventListenerPtr getDefaultEventListener();

    void processOperationGetVar(const AgString & id, Context* con, const AgString & name, OutgoingAggreGateCommandPtr ans) ;
    DataTablePtr getVariable(Context* con, const AgString & name) ;
    void processOperationSetVar(const AgString & id, Context* con, const AgString & name, const AgString & encodedValue, OutgoingAggreGateCommandPtr ans) ;
    VariableDefinitionPtr getVariableDefinition(Context* con, const AgString & name);
    void setVariable(Context* con, const AgString & name, DataTablePtr value) ;
    void processOperationCallFunction(const AgString & id, Context* con, const AgString & name, const AgString & encodedParameters, OutgoingAggreGateCommandPtr ans) ;
    FunctionDefinitionPtr getFunctionDefinition(Context* con, const AgString & name);
    DataTablePtr callFunction(Context* con, const AgString & name, DataTablePtr parameters) ;

	bool addNormalListener(const AgString &context,const AgString &name, ContextEventListenerPtr cel);

	void processOperationAddEventListener(const AgString & id, const AgString & context, const AgString & name, int  listener, const AgString & filter, OutgoingAggreGateCommandPtr ans) ;
	void processOperationRemoveEventListener(const AgString & id, const AgString & context, const AgString & name, int  listenerHashCode, const AgString & filter, OutgoingAggreGateCommandPtr ans) ;
    void processMessageStart(IncomingAggreGateCommandPtr cmd, OutgoingAggreGateCommandPtr ans);

	void shutdown();
	bool run();
	void runImpl() ;

	OutgoingAggreGateCommandPtr processCommand(IncomingAggreGateCommandPtr cmd) ;
    virtual ActionManagerPtr getActionManager();

	void sendCommand(OutgoingAggreGateCommandPtr cmd) ;
    ContextEventListenerPtr createListener(int  listenerHashCode, ExpressionPtr filter);
    virtual ClassicEncodingSettingsPtr createClassicEncodingSettings(bool useFormatCache);
    OutgoingAggreGateCommandPtr constructEventCommand(EventPtr event, int  listenerCode);
    bool isConnected();
	AgString getAddress();


    DefaultClientController(BlockingChannelPtr dataChannel, ContextManager* contextManager,/*ExecutorService* */AgObjectPtr commandExecutionService, int maxEventQueueLength);

    friend class ForwardingEventListener;
    friend class ProcessCommandTask;
    friend class PendingEventProcessingTask;
};



class ForwardingEventListener : public DefaultContextEventListener
{
private:
    DefaultClientController* owningClientController;
    DefaultClientController* getOwningController();

public:
    ForwardingEventListener(int listenerCode, ExpressionPtr filter, DefaultClientController* owningClientController);

    bool shouldHandle(EventPtr event);
    void handle(EventPtr event);
    AgString toString();
    int hashCode();
    bool equals(AgObject* obj);

    CallerControllerPtr getCallerController();
};


class ProcessCommandTask : public Callable<int>
{
private:
    IncomingAggreGateCommandPtr cmd;
    DefaultClientController *owningClientController;

public:
   ProcessCommandTask(IncomingAggreGateCommandPtr cmd, DefaultClientController *owningClientController);
   virtual ~ProcessCommandTask() {}

   virtual int call();
};


class PendingEventProcessingTask : public Runnable<int>
{
private:
     DefaultClientController *owningClientController;

public:
    PendingEventProcessingTask(DefaultClientController *owningClientController);
    virtual ~PendingEventProcessingTask() {}

    virtual void run();
};

#endif
