#include "Agent.h"
#include "AgentContextManager.h"

#include "communication/SocketException.h"

#include "context/EventDefinition.h"

#include <limits>

Agent::Agent(RemoteServerPtr server, AgString name, bool eventConfirmation)
{
    this->server = server;
    this->context = new AgentContext(server, name, eventConfirmation);
    this->contextManager.reset(new AgentContextManager(context, false, this));
}

Agent::Agent(AgentContext *context)
{
    this->server = context->getServer();
    this->context = context;
    this->contextManager.reset(new AgentContextManager(context, false, this));
}

ContextEventListenerPtr Agent::getContextEventListener()
{
    return controller->getDefaultEventListener();
}

void Agent::connect()
{
    try
    {
        maxEventQueueLength = INT_MAX;

        context->setSynchronized(false);

        dataChannel.reset(new BlockingChannel());
        dataChannel->connect(server->getAddress(), server->getPort());

        contextManager->start();

        controller.reset(new AgentImplementationController(dataChannel, contextManager.get(), AgObjectPtr(), maxEventQueueLength));

        typedef boost::shared_ptr<AgentContextVisitor> AgentContextVisitorPtr;
        contextManager->getRoot()->accept(AgentContextVisitorPtr(new AgentContextVisitor(this)));
    }
    catch(SocketException)
    {
        throw SocketException("Can not connect to host!");
    }
}

void Agent::disconnect()
{
    dataChannel->disconnect();

    if (controller)
    {
        controller->shutdown();
    }

    if (contextManager)
    {
        contextManager->stop();
    }

    context->setSynchronized(false);
}

void Agent::reconnect()
{
    if (isConnected())
        dataChannel->disconnect();

    dataChannel->connect(server->getAddress(), server->getPort());
}

void Agent::run()
{
    try
    {
        controller->runImpl();
    }
    catch (DisconnectionException ex)
    {
        throw DisconnectionException(ex);
    }
}

RemoteServerPtr Agent::getServer()
{
    return server;
}

AgentContext* Agent::getContext()
{
    return context;
}

int Agent::getMaxEventQueueLength()
{
    return maxEventQueueLength;
}

void Agent::setMaxEventQueueLength(int maxEventQueueLength)
{
    this->maxEventQueueLength = maxEventQueueLength;
}

bool Agent::isConnected()
{
    return controller->isConnected();
}
