#include "protocol/RemoteServerController.h"

#include "protocol/RemoteServer.h"
#include "protocol/AggreGateCommandParser.h"
#include "datatable/DataTable.h"
#include "device/RemoteDeviceErrorException.h"
#include "server/RootContextConstants.h"
#include "server/CommonServerFormats.h"
#include "context/Contexts.h"
#include "util/Log.h"
#include "util/MessageFormat.h"
#include "util/BlockingChannel.h"
#include "IOException.h"


RemoteServerController::RemoteServerController(RemoteServer* device, bool async)
    : AbstractAggreGateDeviceController(device)
{
    AbstractAggreGateDeviceController::setContextManager(new RemoteContextManager(this, device, async));
}

RemoteServerController::RemoteServerController(RemoteServer* device, bool async, bool useContextManager)
    : AbstractAggreGateDeviceController(device)
{
    if(useContextManager) {
        AbstractAggreGateDeviceController::setContextManager(new RemoteContextManager(this, device, async));
    }
}

bool RemoteServerController::connectImpl()
{
    RemoteServer* remoteServer = static_cast<RemoteServer*>(getDevice());
    try
    {
        if (dataChannel == NULL && !remoteServer->getAddress().empty())
        {
           LOG_PROTOCOL_DEBUG("Connecting to remote server (" + remoteServer->toString().toUtf8() + ")");

            if (remoteServer->getSsl())
            {
                dataChannel = BlockingChannelPtr(new BlockingChannel(remoteServer->getConnectionTimeout()));
            }
            else
            {
                dataChannel = BlockingChannelPtr(new BlockingChannel(false));
            }

            dataChannel->connect(remoteServer->getAddress(), remoteServer->getPort());

            AgString msg = getConnectionErrorMessage();
        }

        if (dataChannel != NULL) {
            setCommandParser(CommandParserPtr(new AggreGateCommandParser(dataChannel, AggreGateCommand::START_CHAR,
                                                                         AggreGateCommand::END_CHAR)));
        }

         LOG_PROTOCOL_DEBUG("Connection with remote server established");
    }catch (IOException ex) {
        throw RemoteDeviceErrorException(MessageFormat::format(Cres::get()->getString("devErrConnecting"),
                                                               getDevice()->getDescription() + " (" + getDevice()->getInfo() + ")") + ex.getMessage());
    }

    AbstractAggreGateDeviceController::connectImpl();

    if (getContextManager() != NULL)
    {
        ProxyContext* proxyContext = new ProxyContext(Contexts::CTX_ROOT, this);
        DefaultContextManager* contextManager = static_cast<DefaultContextManager *>(getContextManager());
        contextManager->setRoot(proxyContext);
        contextManager->restart();
    }

    return true;
}


AgString RemoteServerController::getConnectionErrorMessage()
{
    return "";
}

bool RemoteServerController::loginImpl()
{
    if (getContextManager() != NULL) {
        getContextManager()->restart();
    }


    RemoteServer* remoteDevice = static_cast<RemoteServer *>(getDevice());
    std::list<AgObjectPtr> lsParam;
    lsParam.push_back( remoteDevice->getUsername().toObjectPtr() );
    lsParam.push_back( remoteDevice->getPassword().toObjectPtr() );
    DataTablePtr loginInput = DataTablePtr(new DataTable(CommonServerFormats::getInstance().getFIFT_LOGIN(), lsParam) );

    callRemoteFunction(Contexts::CTX_ROOT, RootContextConstants::F_LOGIN, TableFormatPtr(), loginInput);

    if (getContextManager() != NULL) {
        static_cast<ProxyContext *>(getContextManager()->getRoot())->reinitialize(); // Resets local cache, because root context was already initialized, but its visible entities changed after login
    }

    return true;
}

void RemoteServerController::start()
{
}

void RemoteServerController::disconnectImpl()
{
    if (dataChannel != NULL && dataChannel->isOpen()) {
        try {
            dataChannel->close();
        }catch (IOException ex) {
            LOG_PROTOCOL_DEBUG("Error closing socket: "+ ex.getMessage().toUtf8());
        }
    }

    dataChannel = BlockingChannelPtr();

    AbstractAggreGateDeviceController::disconnectImpl();
}

void RemoteServerController::send(CommandPtr cmd)
{
    cmd->send(dataChannel);
}

void RemoteServerController::setDataChannel(BlockingChannelPtr socketChannel)
{
    this->dataChannel = socketChannel;
}

BlockingChannelPtr RemoteServerController::getDataChannel()
{
    return dataChannel;
}

bool RemoteServerController::isConnected()
{    
    return AbstractDeviceController::isConnected() && dataChannel != 0 && dataChannel->isOpen();
}

// Not  usage
AgString RemoteServerController::getAddress()
{
    assert(0);
    return "";
}
