//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / OVOPS++
//
//File: protocol.cpp
//
//Version: $Revision: 1.44 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/09 10:14:52 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Authors:
//      Pasi Nummisalo
//      Timo Pärnänen
//      Juhana Räsänen
//      Timo Kokkonen
//
//Description:
//      See corresponding header file.
//
//Copyright:     
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//      
//Licence:
//     
//
//History:
//
//

#include <autoptr.h>
#include "protocol.h"
#include "debug.h"
#include "system.h"

#include "tools.h"

//
//Functions: constructors and destructor
//
//Description:
//    Functions implements different constructors and destructor
//

pfProtocol :: pfProtocol(void)
    : sfTask(),
      pfTimers(this),
      _sideA(),
      _sideB(),
      _key(0),
      _traceOn(0),       
      _id(0),
      _refCount(0),
      _state(this),
      _messageQueue()
{
    pfSystem::instance()->registerConduit(this);
    return;
}

pfProtocol :: pfProtocol(
    const pfProtocol &other_)
    : sfTask(),
      pfTimers(this),
      _sideA(other_._sideA),
      _sideB(other_._sideB),
      _key(other_._key),
      _traceOn(other_._traceOn),      
      _id(other_._id),
      _refCount(0),
      _state(other_._state),
      _messageQueue()
{
// TODO ei kopioitavan protokollan sivuja voi kytkea tahan!! ??
    pfSystem::instance()->registerConduit(this);
    return;
} 

pfProtocol :: ~pfProtocol(void)
{
    transporterListIterator i;
    for (i = _messageQueue.begin(); i != _messageQueue.end(); i++)
    {
        delete (*i);
    }
    return;
} 

pfConduit pfProtocol :: createProtocol(void)
{
    pfConduit newProtocol(new pfProtocol);
    return newProtocol;
}

//
//Function: cloneImplementation
//
//Description:
//    Implements clone method which use copy constructor to return
//    a pointer of type pfProtocol to new object
//

pfProtocol *pfProtocol :: cloneImplementation(void) const
{
    pfProtocol *protocol = new pfProtocol(*this);
    return protocol;
}

//
//Function: set and get methods
//
//Description:
//    Implements set and get methods for _id and _key attributes
//

void pfProtocol :: setId(pfId id_)
{
    _id = id_;
    return;
}

pfId pfProtocol :: getId(void) const
{
    return _id;
}

void pfProtocol :: setKey(pfKey key_)
{
    _key = key_;
    return;
}

pfKey pfProtocol :: getKey(void) const
{
    return _key;
}

//
//Function: changeState
//
//Description:
//    Set state attribute.
//

void pfProtocol :: changeState(pfState *state_)
{
    debugState(_state, state_);
    _state = state_;
    return;
}

//
//Function: getState
//
//Description:
//    Returns state attribute
//

pfState *pfProtocol :: getState(void) const
{
    return _state;
}

//
//Function: accept methods
//
//Description:
//    Accept method takes a transporter (visitor) as an argument.
//    If accept method is synchronous the operation is performed at
//    specific conduit right a way. In other case the transporter
//    is saved to schedulers message queue.   
//

void pfProtocol :: accept(pfTransporter *transporter_)
    throw(pfNullPointerException)
{
    if (transporter_ == 0)
    {
        throw pfNullPointerException(PF_EX_INFO);
    }

    debugReceive(this, transporter_);

    if (transporter_->isSender(_sideA))
    {
        transporter_->setSenderIsA();
    }
    else
    {
        transporter_->setSenderIsB();
    }

    if (transporter_->acceptAsynchronous() == 0)
    {
        acceptSynchronous(transporter_);
    }
    else 
    {
        acceptAsynchronous(transporter_);
    }
    return;
}

void pfProtocol :: acceptSynchronous(pfTransporter *transporter_)
{
    transporter_->atProtocol(this, getState());
    return;
}

void pfProtocol :: acceptAsynchronous(pfTransporter *transporter_)
{
// TODO jonon hannille aina, eri metodi jos halutaan eteen
    if (transporter_->saveAtTail() == 0)
    {
        // Save at head of message queue
        _messageQueue.push_front(transporter_);
    }
    else
    {
        // Save at tail of message queue
        _messageQueue.push_back(transporter_);
    }
    requestCPU();
    return;
}

//
//Functions: toX
//
//Description:
//    Send transporter to sideX, in other words call sideX accept
//    method the transporter as parameter. ToX methods a messenger as
//    parameter create transporter and call transporter version of method.
//    

void pfProtocol :: toA(pfTransporter *transporter_)
{
    debugSendToA(transporter_);
    pfConduit proxy(this);
    transporter_->setSender(proxy);
    _sideA.accept(transporter_);  
    return;
}

void pfProtocol :: toB(pfTransporter *transporter_)
{
    debugSendToB(transporter_);
    pfConduit proxy(this);
    transporter_->setSender(proxy);
    _sideB.accept(transporter_);
    return;
}

void pfProtocol :: toA(pfMessenger *messenger_)
{
    pfMsgTransporter *message =
        pfMsgTransporter::createMsgTransporter(messenger_);
    toA(message);
    return;
}

void pfProtocol :: toB(pfMessenger *messenger_)
{
    pfMsgTransporter *message =
        pfMsgTransporter::createMsgTransporter(messenger_);
    toB(message);
    return;
}

//
//Functions: connect methods
//
//Description:
//    Connect given conduit to proper side 
//
//

void pfProtocol :: connectToA(pfConduit &conduit_)
{
    _sideA = conduit_;
    return;
}

void pfProtocol :: connectToB(pfConduit &conduit_)
{   
    _sideB = conduit_;
    return;
}

//
//Function: disconnect
//
//Description:
//    Replace sideA and sideB conduits with nilConduit
//

void pfProtocol :: disconnect(void)
{
    pfConduit nilConduit;
    _sideA = nilConduit;
    _sideB = nilConduit;
    return;
}

//
//Function: runCallback
//
//Description:
//    Executes action to given transporter
//

void pfProtocol :: runCallback(void)
{
    try
    {
        if (_messageQueue.empty() == 0)
        {
            auto_ptr<pfTransporter> message(_messageQueue.front());
            debugStartRun(this, message.get());
            _messageQueue.pop_front();
            acceptSynchronous(message.get());
        }
    }
    catch (pfException &exception)
    {
        error(exception.getCause());
    }
    catch (...)
    {
        error(41);
    }
    debugEndRun();
    return;
}

//
//Function: error
//
//Description:
//    Hook method for handling errors in runCallback 
//

void pfProtocol :: error(pfUlong cause_)
{
    debugExceptionCatched(cause_);
// ++TODO++ will use trace output when available
    return;
}

//
//Functions: methods for reference counter
//
//Description:
//    Implements methods to increment and decrement the value of
//    reference counter
//

void pfProtocol :: incRefCount(void)
{
    _refCount++;
    return;
}

void pfProtocol :: decRefCount(void)
{
    _refCount--;
    return;
}

//
//Function: noReference
//
//Description:
//    Tell is there any reference to implementation
//

int pfProtocol :: noReference(void) const
{
    int reference = 0;
    if (_refCount == 0)
    {
        reference = 1;
    }
    else
    {
        reference = 0;
    }
    return reference;
}

//
//Function: setTraceOn
//
//Description:
//    Set trace on and register object to trace
//

void pfProtocol :: setTraceOn(void)
{
    _traceOn = 1;
    return;
}

