#include "MessageDim.h"

#include "tools.h"
#include "Time.h"

using namespace std;

// **************************************************************************
/** @class MessageDimTX

@brief Based on MessageImp, redirects log-output to a Dim service MESSAGE

This is a special DimService which offers SERVER/MESSAGE to the DimNetwork
and redirects output issued via its base-class MessageImp to the Dim
service. The severity of the message is send as qualiy of service of
the service message.

@section Examples

 - A simple and usefull example can be found in \ref log.cc and \ref logtime.cc

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

// --------------------------------------------------------------------------
//
//! Constructs a DimService with the name SERVER/MESSAGE. And passes the
//! given ostream down to the MessageImp base.
//!
//! @param name
//!    Name of the message server to which we want to subscribe, e.g. DRIVE
//!
//! @param out
//!    ostream passed to MessageImp. It is used to redirect the output to.
//
MessageDimTX::MessageDimTX(const std::string &name, std::ostream &out)
    : DimDescribedService(name + "/MESSAGE", const_cast<char*>(""),
                          "A general logging service providing a quality of service (severity)"
                          "|Message[string]:The message"),
MessageImp(out), fDebug(false)
{
    // This is a message which will never arrive because
    // the time to establish a client-sever connection is
    // too short.
    Message("MessageDimTX started.");
}

// --------------------------------------------------------------------------
//
//!
//
MessageDimTX::~MessageDimTX()
{
    Message("MessageDimTX shutting down..");
}

// --------------------------------------------------------------------------
//
//! First calls MessageImp::Write to output the message tobe transmitted
//! also to a local logging stream. Then the Dim service is updated.
//! If sending of the message failed a message is written to the
//! logging stream stored in MessageImp. It is intentionally not
//! output through Update to make it look different than usual
//! transmitted messages.
//
int MessageDimTX::Write(const Time &t, const string &txt, int qos)
{
    MessageImp::Write(t, txt, qos);


    // We cannot use our own mutex here because it can create dead-locks
    // in a hand-shake with the global dim-mutex if a service is
    // updated from within a dimHandler (dim-mutex already locked)
    // and at the same time another thread tries to lock the mutex.
    //
    // Thread 1: Lock global Dim mutex and call infoHandler
    //
    // Thread 2: CALL Write
    // Thread 2: LOCK Write-mutex
    // Thread 2: setQuality will try to lock global Dim mutex
    //           (since Thread1!=Thread2 this results in a wait)
    //
    // Thread 1: CALL Write from within the infoHandler
    // Thread 1: LOCK Write-mutex
    //           (since Thread2 now waits for the infoHandler to finish
    //            and the infoHandler has to wait for the Write-mutex
    //            we have a dead-lock)
    //
    dim_lock();

    // We have to use setData to make sure the DimService will
    // hold a local copy of the data.
    setQuality(qos);

    const int rc = DimDescribedService::Update(txt);

    dim_unlock();

    if (rc==0 && fDebug)
        Out() << " !! " << t.GetAsStr() << " - Sending failed!" << endl;

    return rc;
}

// **************************************************************************
/** @class MessageDimRX

@brief Based on MessageImp, subscribes to a MESSAGE service in the Dim network

This is a special DimInfoHandler. It subscribes to a service SERVER/MESSAGE
on the DimNetwork and redirects all received output to its base class
MessageImp view MessageImp::Write. the quality of service received with
each service update is passed as severity.

@section Examples

 - A simple and usefull example can be found in \ref log.cc and \ref logtime.cc

 @todo Maybe it is not a good idea that MessageImp is a base class,
 maybe it should be a reference given in the constructor

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

// --------------------------------------------------------------------------
//
//! Setup a DimStamedInfo service subscription for SERVER/MESSAGE
//!
//! @param name
//!    the name of the SERVER
//!
//! @param imp
//!    A reference to MessageImo to which messages will be redirected
//
MessageDimRX::MessageDimRX(const std::string &name, MessageImp &imp)
: fMinLogLevel(0), fConnected(false), fMsg(imp),
fDimMessage((name+"/MESSAGE").c_str(), (void*)NULL, 0, this)
{
}

// --------------------------------------------------------------------------
//
//! If the server has been disconnected write a simple log-message.
//! Skip all received messages which have a severity smaller than
//! fMinLogLevel. Write any other message with MessageImp::Write.
//
void MessageDimRX::infoHandler()
{
    if (getInfo()!=&fDimMessage)
        return;

    const string name   = fDimMessage.getName();
    const string server = name.substr(0, name.find_first_of('/'));

    fConnected = fDimMessage.getSize()!=0;

    // The server is diconnected. Do nothing
    if (!fConnected)
    {
        // We cannot print this message because it is produced by
        // every server which doesn't have the MESSAGE service, too.
        //fMsg.Message(server+": Disconnected.");
        return;
    }

    // skip all messages with a severity smaller than the minimum log level
    if (fDimMessage.getQuality()<fMinLogLevel)
        return;

    const string msg = server+": "+fDimMessage.getString();

    // Make sure getTimestamp is called _before_ getTimestampMillisecs
    // Must be in exactly this order!
    const int tsec = fDimMessage.getTimestamp();
    const int tms  = fDimMessage.getTimestampMillisecs();

    // Write the received message to the output
    fMsg.Write(Time(tsec, tms*1000), msg, fDimMessage.getQuality());
}
