#ifndef ProxyContextH
#define ProxyContextH


#include "context/AbstractContext.h"
#include "context/DefaultContextVisitor.h"
#include "context/DefaultContextEventListener.h"
#include "context/VariableDefinition.h"
#include "context/FunctionDefinition.h"
#include "context/EventDefinition.h"

#include "action/ActionDefinition.h"
#include "datatable/DataRecord.h"
#include "protocol/CachedVariableValue.h"
#include "server/ServerContextConstants.h"
#include <boost/weak_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/enable_shared_from_this.hpp>

class ProxyContext: public AbstractContext
{
 private:
    static int64_t METADATA_READ_TIMEOUT() { return 20000; }
    static int64_t LISTENER_OPERATIONS_TIMEOUT() { return 120000; }
    AbstractAggreGateDeviceController* controller;
	bool notManageRemoteListeners;
	bool localInitComplete;
	bool initializingInfo;
	bool initializedInfo;
    boost::recursive_mutex initializingInfoLock;
	bool initializingChildren;
	bool initializedChildren;
    boost::recursive_mutex initializingChildrenLock;
	bool initializingVariables;
	bool initializedVariables;
    boost::recursive_mutex initializingVariablesLock;
	bool initializingFunctions;
	bool initializedFunctions;
    boost::recursive_mutex initializingFunctionsLock;
	bool initializingEvents;
	bool initializedEvents;
    boost::recursive_mutex initializingEventsLock;
	bool initializingActions;
	bool initializedActions;
    boost::recursive_mutex initializingActionsLock;
	bool initializingStatus;
	bool initializedStatus;
    boost::recursive_mutex initializingStatusLock;
	bool initializingVisibleChildren;
    boost::recursive_mutex initializingVisibleChildrenLock;
    std::list<AgString> visibleChildren;
    AgString localRoot;
    AgString remoteRoot;
    AgString remotePath;
    AgString remotePrimaryRoot;
	bool mapped;

    std::map< AgString, boost::shared_ptr<CachedVariableValue> > variableCache;
    boost::recursive_mutex variableCacheLock;

    std::list< AgString >  AUTO_LISTENED_EVENTS;
    std::list<AgString> initFunc_AUTO_LISTENED_EVENTS();

	void initInfo();
	void initChildren();
	void initVariables();
	void initFunctions();
	void initEvents();
	void initActions() ;
	void initVisibleChildren();
	void initStatus();
	void initInfoImpl(DataTablePtr info);

	void initChildrenImpl(DataTablePtr children);
	void initVisibleChildrenImpl();
    void initVariablesImpl(DataTablePtr variables);
	void initFunctionsImpl(DataTablePtr functions);
	void initEventsImpl(DataTablePtr events);
	void initActionsImpl(DataTablePtr actions);
	void initStatusImpl();

    void initVariablesLoggingErrors();
	void initFunctionsLoggingErrors();
	void initEventsLoggingErrors();
	void initActionsLoggingErrors();
	void initStatusLoggingErrors();
    IncomingAggreGateCommandPtr sendGetVariable(const AgString & name, int64_t  timeout);
	DataTablePtr getRemoteVariable(VariableDefinitionPtr def);
    void addRemoteListener(const AgString & ename, ContextEventListenerPtr contextEventListener);
	void restoreEventListeners();
    void cacheVariableValue(const AgString & variable, DataTablePtr value);
    AgString getRemotePrimaryPath(const AgString & remoteFullPath);

	ContextEventListenerPtr visibleChildAddedListener;
	ContextEventListenerPtr visibleChildRemovedListener;
	ContextEventListenerPtr contextStatusChangedListener;

	void init();
	EventPtr fireEvent(EventPtr event);

public:
    static AgString F_LOCAL_REINITIALIZE() { return "localReinitialize"; }
    virtual void setupMyself();
	void addLocalFunctionDefinitions();
    virtual TableFormatPtr decodeFormat(const AgString & source, CallerControllerPtr caller);
	void clear();

    AgString convertRemoteDescription(const AgString & remoteDescription);
    ProxyContext* createChildContextProxy(const AgString & name);

    virtual AgString getDescription();
    virtual AgString getType();
    AgString getLocalRoot();
    AgString getRemoteRoot();
    bool isMapped();
    Context* get(const AgString & contextPath, CallerControllerPtr caller);
    AgString getIconId();
    Context* getChild(const AgString & name, CallerControllerPtr callerController);
    VariableDefinitionPtr getVariableDefinition(const AgString & name);
    FunctionDefinitionPtr getFunctionDefinition(const AgString & name);
    EventDataPtr getEventData(const AgString & name);
    ActionDefinitionPtr getActionDefinition(const AgString & name);
	std::list<VariableDefinitionPtr>  getVariableDefinitions(CallerControllerPtr caller, bool hidden);
	std::list<FunctionDefinitionPtr>  getFunctionDefinitions(CallerControllerPtr caller, bool hidden);	
	std::list<ActionDefinitionPtr>  getActionDefinitions(CallerControllerPtr caller, bool hidden);
    std::list<EventDefinitionPtr>  getEventDefinitions(CallerControllerPtr caller, bool hidden);
    ContextStatusPtr getStatus();

    DataTablePtr getRemoteVariable(TableFormatPtr format, const AgString & name, int64_t  timeout) ;

    AbstractAggreGateDeviceController* getController();
	void setupVariables();
	DataTablePtr getVariableImpl(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request);


	bool setVariableImpl(VariableDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr value);
	void setupFunctions();
	DataTablePtr callFunctionImpl(FunctionDefinitionPtr def, CallerControllerPtr caller, RequestControllerPtr request, DataTablePtr parameters);
    DataTablePtr callRemoteFunction(const AgString & name, TableFormatPtr outputFormat, DataTablePtr parameters);

    bool addEventListener(const AgString & name, ContextEventListenerPtr contextEventListener, bool weak);
    bool addEventListener(const AgString & name, ContextEventListenerPtr contextEventListener, bool weak, bool sendRemoteCommand);
    bool removeEventListener(const AgString & name, ContextEventListenerPtr contextEventListener);
    bool removeEventListener(const AgString & name, ContextEventListenerPtr listener, bool sendRemoteCommand);


    std::list<Context*>  getChildren(CallerControllerPtr caller);
    std::list<Context*>  getVisibleChildren(CallerControllerPtr caller);
    void addVisibleChild(const AgString & localVisiblePath);
    void removeVisibleChild(const AgString & localVisiblePath);


	void reinitialize();
    EventPtr fireEvent(EventDefinitionPtr ed, DataTablePtr data, int level, int64_t  id, AgDatePtr creationtime,
                       int  listener, CallerControllerPtr caller, FireEventRequestControllerPtr request, PermissionsPtr permissions);
    AgString getPathDescription();


	bool isProxy();
    bool isDistributed();
    AgString getRemotePath();
    virtual AgString getRemotePrimaryRoot();
    AgString getPeerPath();
    AgString getLocalPath(const AgString & remoteFullPath);
    AgString getLocalVisiblePath(const AgString & remoteFullPath);
    void setRemotePath(const AgString & remotePath);

    virtual bool isInitializedInfo();
    virtual bool isInitializedChildren();
    virtual bool isInitializedVariables();
    virtual bool isInitializedFunctions();
    virtual bool isInitializedEvents();
	bool isNotManageRemoteListeners();
    void setNotManageRemoteListeners(bool notManageRemoteListeners);

    EventPtr fireEvent(const AgString & name, DataTablePtr data, int level, int64_t  id, AgDatePtr creationtime,
                       int  listener, CallerControllerPtr caller, FireEventRequestControllerPtr request);

    ProxyContext(AgString name, AbstractAggreGateDeviceController* controller);

    friend class VisibleChildAddedListener;
    friend class VisibleChildRemovedListener;
    friend class ContextStatusChangedListener;
    friend class AcceptContextVisitor;

    friend class ChildAddedListener;
    friend class ChildRemoveListener;
    friend class VariableAddedListener;
    friend class VariableRemovedListener;
    friend class FunctionAddedListener;
    friend class FunctionRemovedListener;
    friend class EventAddedListener;
    friend class EventRemovedListener;
    friend class ActionAddedListener;
    friend class ActionRemovedListener;
    friend class ActionStateChangeListener;
    friend class InfoChangedListener;
};

#endif
