#ifndef ProxyContextListenersH
#define ProxyContextListenersH

#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/enable_shared_from_this.hpp>

class VisibleChildAddedListener : public DefaultContextEventListener
{
    ProxyContext &ownerContext;

public:
    VisibleChildAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        AgString path = event->getData()->rec()->getString(ServerContextConstants::EF_VISIBLE_CHILD_ADDED_PATH);
        ownerContext.addVisibleChild(path);
    }
};

class VisibleChildRemovedListener : public DefaultContextEventListener
{
    ProxyContext &ownerContext;

public:
    VisibleChildRemovedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        AgString path = event->getData()->rec()->getString(ServerContextConstants::EF_VISIBLE_CHILD_REMOVED_PATH);
        ownerContext.removeVisibleChild(path);
    }
};

class ContextStatusChangedListener : public DefaultContextEventListener
{
    ProxyContext &ownerContext;

public:
    ContextStatusChangedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)// throws EventHandlingException
    {
        DataRecordPtr statusRec = event->getData()->rec();
        ownerContext.setStatus(statusRec->getInt(ServerContextConstants::VF_CONTEXT_STATUS_STATUS),
                             statusRec->getString(ServerContextConstants::VF_CONTEXT_STATUS_COMMENT));
    }
};

class AcceptContextVisitor : public DefaultContextVisitor
{
    ProxyContext& ownerContext;

public:
    AcceptContextVisitor(ProxyContext& owner) :
        DefaultContextVisitor(), ownerContext(owner) {}

    void visit(Context* event)
    {
        UNUSED(event);
        ownerContext.initializedInfo = false;
        ownerContext.initializingInfo = false;

        ownerContext.initializedChildren = false;
        ownerContext.initializingChildren = false;

        ownerContext.initializedVariables = false;
        ownerContext.initializingVariables = false;

        ownerContext.initializedFunctions = false;
        ownerContext.initializingFunctions = false;

        ownerContext.initializedEvents = false;
        ownerContext.initializingEvents = false;

        ownerContext.initializedActions = false;
        ownerContext.initializingActions = false;

        ownerContext.initializedStatus = false;
        ownerContext.initializingStatus = false;

        ownerContext.initializingVisibleChildren = false;
        ownerContext.visibleChildren.clear();
    }
};


class ChildAddedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    ChildAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        AgString child = event->getData()->rec()->getString(AbstractContextStrings::EF_CHILD_ADDED_CHILD);
        if (ownerContext.AbstractContext::getChild(child) == NULL)
        {
            ownerContext.addChild(ownerContext.createChildContextProxy(child));
        }
    }
};

class ChildRemoveListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    ChildRemoveListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        AgString child = event->getData()->rec()->getString(AbstractContextStrings::EF_CHILD_REMOVED_CHILD);
        if (!child.empty()){
            ownerContext.removeChild(child);
        }
    }
};

class VariableAddedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    VariableAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        VariableDefinitionPtr def = ownerContext.varDefFromDataRecord(event->getData()->rec());
        if (ownerContext.getVariableDefinition(def->getName()) == NULL) {
            ownerContext.addVariableDefinition(def);
        }
    }
};

class VariableRemovedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    VariableRemovedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        ownerContext.removeVariableDefinition(event->getData()->rec()->getString(AbstractContextStrings::EF_VARIABLE_REMOVED_NAME));
    }
};

class FunctionAddedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    FunctionAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        FunctionDefinitionPtr def = ownerContext.funcDefFromDataRecord(event->getData()->rec());
        if (ownerContext.getFunctionDefinition(def->getName()) == NULL) {
            ownerContext.addFunctionDefinition(def);
        }
    }
};

class FunctionRemovedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    FunctionRemovedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        ownerContext.removeFunctionDefinition(event->getData()->rec()->getString(AbstractContextStrings::EF_FUNCTION_REMOVED_NAME));
    }
};

class EventAddedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    EventAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        EventDefinitionPtr def = ownerContext.evtDefFromDataRecord(event->getData()->rec());
        if (ownerContext.getEventDefinition(def->getName()) == NULL) {
            ownerContext.addEventDefinition(def);
        }
    }
};

class EventRemovedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    EventRemovedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        ownerContext.removeEventDefinition(event->getData()->rec()->getString(AbstractContextStrings::EF_EVENT_REMOVED_NAME));
    }
};

class ActionAddedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    ActionAddedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        //FIXME: memory leak when copying field
       /* ActionDefinitionPtr def = ownerContext.actDefFromDataRecord(event->getData()->rec());
        if (ownerContext.getActionDefinition(def->getName()) == NULL) {
            ownerContext.addActionDefinition(def);
        }*/
    }
};

class ActionRemovedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    ActionRemovedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        ownerContext.removeActionDefinition(event->getData()->rec()->getString(AbstractContextStrings::EF_ACTION_REMOVED_NAME));
    }
};

class ActionStateChangeListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    ActionStateChangeListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        /*ActionDefinitionPtr def = ownerContext.actDefFromDataRecord(event->getData()->rec());
        ownerContext.removeActionDefinition(def->getName());
        ownerContext.addActionDefinition(def);*/
    }
};

class InfoChangedListener : public DefaultContextEventListener
{
    ProxyContext& ownerContext;

public:
    InfoChangedListener(ProxyContext& owner) :
        DefaultContextEventListener(), ownerContext(owner) {}

    void handle(EventPtr event)
    {
        ownerContext.initInfoImpl(event->getData());
    }
};


#endif
