/*****************************************************************************
 *
 * $Id$
 *
 * Copyright (C) 2009 - 2012  Richard Hacker <lerich@gmx.net>
 *                            Florian Pose <fp@igh-essen.com>
 *
 * This file is part of the PdCom library.
 *
 * The PdCom library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * The PdCom library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with the PdCom Library. If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#error No config.h
#endif

#include "pdcom/Process.h"
#include "pdcom/Variable.h"
#include "pdcom/Exception.h"

#include "ProcessStreambuf.h"

#include "msrproto/MsrProtocolHandler.h"

#include <iostream>
#include <sstream>

using namespace PdCom;

/*****************************************************************************/

Process::Process(unsigned int buflen):
    readOnly(false),
    sb(new ProcessStreambuf(this, buflen)), os(new std::ostream(sb))
{
    handler = NULL;
    os_flags = os->flags();
    logLevel = LogInfo;
}

/*****************************************************************************/

Process::~Process()
{
    reset();
    delete os;
    delete sb;
}

/*****************************************************************************/

std::map<std::string,std::string> Process::getProtocolInformation() const
{
    return handler ? handler->getInfo() : std::map<std::string,std::string>();
}

/*****************************************************************************/

void Process::reset()
{
    for (VariableSet::iterator it = variableSet.begin();
            it != variableSet.end(); it++) {
        (*it)->cancelSubscribers();
    }

    delete handler;
    handler = 0;

    os->clear();
    os->flags(os_flags);
    sb->reset();
}

/*****************************************************************************/

bool Process::clientInteraction(
        const std::string&, const std::string&, const std::string&,
        std::list<ClientInteraction>&)
{
    return false;
}

/*****************************************************************************/

Variable* Process::findVariable(const std::string& path) const
{
    if (!isConnected()) {
        protocolLog(LogWarn, "Requesting a variable before protocol "
                "initialisation has completed.");
    }

    for (VariableSet::const_iterator it = variableSet.begin();
            it != variableSet.end(); it++) {
        if ((*it)->path == path)
            return *it;
    }

    std::stringstream err;
    err << "Variable \"" << path << "\" not found.";
    protocolLog(LogWarn, err.str());
    return 0;
}

/*****************************************************************************/

bool Process::isConnected() const
{
    return handler ? handler->connected() : false;
}

/*****************************************************************************/

void Process::newVariable(Variable *pv)
{
    variableSet.insert(pv);
}

/*****************************************************************************/

void Process::rmVariable(Variable *pv)
{
    variableSet.erase(pv);
}

/*****************************************************************************/

void Process::protocolInitialised()
{
    sigConnected();
}

/*****************************************************************************/

size_t Process::newData(const char *buf, size_t len)
{
    if (!handler) {
        if ((handler = MSRProto::ProtocolHandler::tryParse(
                    buf, len, this, os))) {
            protocolLog(LogInfo, "Autodetected MSR Protocol.");
        }
        else {
            throw ProtocolException("Unknown protocol");
        }
    }

    len = handler->parse(buf, len);

    // If data is in the output buffer, call the virtual method
    // sendRequest()
    if (sb->hasData())
        sendRequest();

    return len;
}

/*****************************************************************************/

void Process::sendRequest()
{
    while (writeReady() > 0);
}

/*****************************************************************************/

bool Process::hasData() const
{
    return sb->hasData();
}

/*****************************************************************************/

void Process::sigConnected()
{
    protocolLog(LogDebug, "Protocol initialisation completed.");
}

/*****************************************************************************/

int Process::writeReady()
{
    return sb->writeReady();
}

/*****************************************************************************/

void Process::processMessage(
        const Time& time,
        LogLevel_t level,
        unsigned int messageNo,
        const std::string& message
        ) const
{
    if (level > logLevel)
        return;
    std::cout << "<" << level << "> Message from process at " << time.str()
        << ": (" << messageNo << ") " << message << std::endl;
}

/*****************************************************************************/

void Process::protocolLog(
        LogLevel_t level,
        const std::string& message
        ) const
{
    if (level > logLevel)
        return;
    std::cerr << "<" << level << "> " << message << std::endl;
}

/*****************************************************************************/

void Process::sendBroadcast(
        const std::string &message,
        const std::string &attr
        )
{
    if (!handler) {
        protocolLog(LogError,
                "sendBroadcast(): Protocol handler not ready.");
        return;
    }

    bool hadData = sb->hasData();

    handler->sendBroadcast(message, attr);

    if (!hadData && sb->hasData()) {
        sendRequest();
    }
}

/*****************************************************************************/
