#include "communication/AbstractDeviceController.h"
#include "device/DisconnectionException.h"
#include <boost/thread/locks.hpp>
#include "AggreGateException.h"
#include "util/Log.h"

AbstractDeviceController::AbstractDeviceController(int64_t commandTimeout)
{
    this->commandTimeout = commandTimeout;
    this->connecting = false;
    this->connected = false;
    this->loggingIn = false;
    this->loggedIn = false;
}

void AbstractDeviceController::connect()
{
    boost::lock_guard<boost::recursive_mutex> lock(mutex);
    if (isConnected() || connecting) {
        return;
    }

    try
    {
        connecting = true;

        if (connectImpl()) {
            setConnected(true);
        }
    }catch(AggreGateException& ex){
        connecting = false;
        throw ex;
    }
}

void AbstractDeviceController::login()
{
    boost::lock_guard<boost::recursive_mutex> lock(mutex);
    if (isLoggedIn() || loggingIn) {
        return;
    }

    try
    {
        loggingIn = true;
        if (loginImpl()) {
            setLoggedIn(true);
        }
    }catch(AggreGateException ex){
        loggingIn = false;
        throw ex;
    }
}

void AbstractDeviceController::disconnect()
{
    if (processor != NULL)
    {
        processor->finishThread();
    }

    setLoggedIn(false);
    setConnected(false);
}

CommandPtr AbstractDeviceController::sendCommand(CommandPtr cmd)
{
    connect();

    CommandPtr reply;
    try
    {
        reply = processor->sendSyncCommand(cmd);
    }catch (DisconnectionException ex) {
        setLoggedIn(false);
        setConnected(false);
        throw ex;
    }

    //AGDEBUG(DBG_DEBUG, "AbstractDeviceController", "Received reply '" + reply->toString() + "' to command: " + cmd->toString());

    return reply;
}

bool AbstractDeviceController::isActive()
{
    if (connecting || loggingIn) {
        return true;
    }

    if (processor != NULL) {
        return processor->isActive();
    }

    return false;
}

void AbstractDeviceController::processAsyncCommand(CommandPtr cmd)
{
    if(Log::COMMANDS.isInfoEnabled())
    {
        LOG_COMMANDS_INFO("Received async command: " + cmd->toString().toUtf8());
    }
}

void AbstractDeviceController::newDataReceived()
{
    if (resetTimeoutsOnData)
    {
        resetCommandTimeouts();
    }
}

void AbstractDeviceController::resetCommandTimeouts()
{
    processor->resetSentCommandTimeouts();
}

void AbstractDeviceController::startCommandProcessor()
{
    processor = AsyncCommandProcessorPtr(new AsyncCommandProcessor(this));
    processor->start();
}

void AbstractDeviceController::setCommandParser(CommandParserPtr commandParser)
{
    this->commandParser = commandParser;
    this->commandParser->setListener(this);
    startCommandProcessor();
}

bool AbstractDeviceController::isConnected()
{   
    return connected && (processor != NULL);
}

void AbstractDeviceController::setConnected(bool connected)
{
    this->connected = connected;
}

bool AbstractDeviceController::isLoggedIn()
{
    return isConnected() && loggedIn;
}

void AbstractDeviceController::setLoggedIn(bool loggedIn)
{
    this->loggedIn = loggedIn;
}

CommandParserPtr AbstractDeviceController::getCommandParser()
{
    return commandParser;
}

void AbstractDeviceController::setResetTimeoutsOnData(bool resetTimeoutWhenDataReceived)
{
    this->resetTimeoutsOnData = resetTimeoutWhenDataReceived;
}

std::list<ReplyMonitorPtr> AbstractDeviceController::getActiveCommands()
{
    std::list<ReplyMonitorPtr> nullList;
    return processor != NULL ? processor->getActiveCommands() : nullList;
}

int64_t AbstractDeviceController::getCommandTimeout()
{
    return commandTimeout;
}


CommandProcessorStatisticsPtr AbstractDeviceController::getStatistics()
{
    return processor != NULL ? processor->getStatistics() : CommandProcessorStatisticsPtr();
}
