//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / OVOPS++
//
//File: transporter.cpp
//
//Version: $Revision: 1.81 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/08 08:42:34 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Authors:
//      Pasi Nummisalo
//      Timo Pärnänen
//      Jari Katajavuori
//	Timo Kokkonen
//	Vesa-Matti Puro
//
//Description:
//      See corresponding header file.
//
//Copyright:     
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//      
//Licence:
//     
//
//History:
//
//

#include "assert.h"
#include "transp.h"
#include "adapter.h"
#include "factory.h"
#include "mux.h"
#include "debug.h"

pfTransporter :: pfTransporter(void)
    : _key(99),
      _serialNumber(0),
      _sender(SENDER_UNKNOWN),
      _asynchronous(1),
      _saveAtTail(1)
{
    _serialNumber = pfMessenger::getNextSerialNumber();
    return;
}

pfTransporter :: pfTransporter(const pfTransporter &other_)
    : _key(other_._key),
      _serialNumber(0),
      _sender(other_._sender),
      _asynchronous(other_._asynchronous),
      _saveAtTail(other_._saveAtTail)
{
    _serialNumber = pfMessenger::getNextSerialNumber();
    return;
}

pfTransporter :: ~pfTransporter(void)
{
    return;
}

//
//Function: clone
//
//Description:
//    Returns pointer to new transporter created by using copy constructor
//

pfTransporter *pfTransporter :: clone(void)
{
    pfTransporter *newTransporter = new pfTransporter(*this);
    return newTransporter;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfTransporter :: atProtocol(
    pfProtocol *const protocol_,
    pfState *)
{
    if (isFromA() == 0)
    {
        protocol_->toA(this);
    }
    else
    {
        protocol_->toB(this);
    }
    return;
}

void pfTransporter :: atMux(pfMux *const)
{
    return;
}

void pfTransporter :: atAdapter(pfAdapter *const)
{
    return;
}

void pfTransporter :: atFactory(pfFactory *const)
{
    return;
}

//
//Functions: setSender
//
//Description:
//    Set proper sender to transporter.
//

bool pfTransporter :: isSender(pfConduit &candidate_)
{
    bool result = _senderConduit == candidate_ ? 1 : 0;
    return result;
}

void pfTransporter :: setSender(pfConduit &senderConduit_)
{
    _senderConduit = senderConduit_;
    return;
}

void pfTransporter :: setSenderIsA(void)
{
    _sender = FROM_A;
    return;
}

void pfTransporter :: setSenderIsB(void)
{
    _sender = FROM_B;
    return;
}

void pfTransporter :: setSenderIsC(void)
{
    _sender = FROM_C;
    return;
}

//
//Functions: isFromA
//
//Description:
//    Returns 1 if the transporter is coming from A side, 0 otherwise.
//

bool pfTransporter :: isFromA(void) const
{
    bool result = _sender == FROM_A ? 1 : 0;
    return result;
}

//
//Functions: key
//
//Description:
//    Set and get methods for _key attribute
//

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

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

pfKey pfTransporter :: getDispatchKey(string &) const
{
    return _key;
}

bool pfTransporter :: isDispatchKeySet(string &) const
{
    return 0;
}

//
//Function: setAcceptMethod
//
//Description:
//    Set given accept method to transporter to indicate how it should
//    be scheduled
//

void pfTransporter :: setAcceptMethodToAsynchronous(void)
{
    _asynchronous = 1;
    return;
}

void pfTransporter :: setAcceptMethodToSynchronous(void)
{
    _asynchronous = 0;
    return;
}

//
//Function: acceptAsynchronous
//
//Description:
//
//
//

bool pfTransporter :: acceptAsynchronous(void) const
{
    return _asynchronous;
}

//
//Function: setSaveMethod
//
//Description:
//
//
//

void pfTransporter :: setSaveMethodToHead ()
{
    _saveAtTail = 0;
    return;
}

void pfTransporter :: setSaveMethodToTail ()
{
    _saveAtTail = 1;
    return;
}

//
//Function: saveAtTail
//
//Description:
//
//
//

bool pfTransporter :: saveAtTail(void) const
{
    return _saveAtTail;
}

string pfTransporter :: getName(void) const
{
    string result = typeid(*this).name ();
    return result;
}

pfMessenger::SerialNumber pfTransporter :: getSerialNumber(void) const
{
    return _serialNumber;
}

// --------------------------------------------------------------------

pfMsgTransporter :: pfMsgTransporter(void)
    : pfTransporter(),
      _messenger(0)
{
    return;
}

pfMsgTransporter :: pfMsgTransporter(pfMessenger *messenger_)
    : pfTransporter(),
      _messenger(messenger_)
{
    //assert(_messenger != 0);
    if (_messenger != 0)
    {
        _messenger->incRefCount();
    }
    else
    {
        //debugUser("pfMsgTransporter :: constructor with 0 messenger called");
    }
    return;
}

pfMsgTransporter :: pfMsgTransporter(const pfMsgTransporter &other_)
    : pfTransporter(other_),
      _messenger(other_._messenger->clone())  // ++TODO++ is this right?
{
    assert(_messenger != 0);
    _messenger->incRefCount();
    return;
}

pfMsgTransporter :: ~pfMsgTransporter(void)
{
    if (_messenger != 0)
    {
        // Destroy messenger only if reference count allows it
        _messenger->decRefCount();
        if (_messenger->isReference() == false)
        {
            //debugUser("pfMsgTransporter :: destructor destroyed the messenger");
            delete _messenger;
        }
        else
        {
            //debugUser("pfMsgTransporter :: messenger was copied and was not destroyed");
        }
    }
    return;
}

pfMsgTransporter *pfMsgTransporter
    :: createMsgTransporter(pfMessenger *messenger_)
{
    pfMsgTransporter *newTransporter = new pfMsgTransporter(messenger_);
    return newTransporter;
}

//
//Function: clone
//
//Description:
//    Returns pointer to new message transporter created by
//    using copy constructor
//

pfMsgTransporter *pfMsgTransporter :: clone(void)
{
    pfMsgTransporter *newMsgTransporter = new pfMsgTransporter(*this);
    return newMsgTransporter;
}

pfKey pfMsgTransporter :: getDispatchKey(string &keyName_) const
{
    return _messenger->getDispatchKey(keyName_); 
}

bool pfMsgTransporter :: isDispatchKeySet(string &keyName_) const
{
    bool present = _messenger->isValuePresent(keyName_);

    return present;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfMsgTransporter :: atProtocol(pfProtocol *const protocol_,
                                    pfState *state_)
{
    _messenger->apply(state_, protocol_);
    return;
}

void pfMsgTransporter :: atMux(pfMux *const mux_)
{
    if (isFromA() == 0)
    {
        mux_->toA(this);
    }
    else
    {
        mux_->tryToGotoB(this);
    }
    return;
}

void pfMsgTransporter :: atAdapter(pfAdapter *const adapter_)
{
    if (adapter_->getState() != 0)
    {
        atProtocol(adapter_, adapter_->getState());
    }
    else
    {
    }
    return;
}

void pfMsgTransporter :: atFactory(pfFactory *const factory_)
{
    // Make new string of conduits (one or more conduits)
    pfConduit firstConduit;
    pfConduit lastConduit;
    factory_->makeConduit(firstConduit, lastConduit);

    this->setAcceptMethodToAsynchronous();
    pfKey key = getKey();
    if (isFromA() == 1)
    {
        factory_->sendInstallerToA(firstConduit, 1, key);
        factory_->sendInstallerToB(lastConduit, 1, key);
        firstConduit.accept(this);
    }
    else
    {
        factory_->sendInstallerToB(lastConduit, 1, key);
        factory_->sendInstallerToA(firstConduit, 1, key);
        // Send MessageTransporter direct to firstConduit
        lastConduit.accept(this);
    }
    return;
}

string pfMsgTransporter :: getName(void) const
{
    string result;
    if (_messenger != 0)
    {
        result = typeid(*_messenger).name ();
    }
    else
    {
        result = pfTransporter::getName();
    }
    return result;
}

pfMessenger::SerialNumber pfMsgTransporter :: getSerialNumber(void) const
{
    pfMessenger::SerialNumber result;
    if (_messenger != 0)
    {
        result = _messenger->getSerialNumber();
    }
    else
    {
        result = pfTransporter::getSerialNumber();
    }
    return result;
}

//-----------------------------------------------------------------------

pfInstallTransporter :: pfInstallTransporter(pfConduit &conduit_)
    : pfTransporter(),
      _conduit(conduit_),
      _useThisKey(0)
{
    setAcceptMethodToSynchronous();
    return;
}

pfInstallTransporter :: pfInstallTransporter(
    const pfInstallTransporter &other_)
    : pfTransporter(other_),
      _conduit(other_._conduit),
      _useThisKey(other_._useThisKey)    
{
    setAcceptMethodToSynchronous();
    return;
}

pfInstallTransporter :: ~pfInstallTransporter(void)
{
    return;
}

pfInstallTransporter
    pfInstallTransporter :: createInstallTransporter(pfConduit &conduit_)
{
    pfInstallTransporter newTransporter(conduit_);
    return newTransporter;
}

//
//Function: clone
//
//Description:
//    Returns pointer to new transporter created by using copy constructor
//

pfInstallTransporter *pfInstallTransporter :: clone(void)
{
    pfInstallTransporter *newInstallTransporter =
        new pfInstallTransporter(*this);
    return newInstallTransporter;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfInstallTransporter :: atMux(pfMux *const mux_)
{
    //  When _useThisKey is set on mux will allocate new
    //  connection with key in installer otherwise it will allocate
    //  a key itself.
    if (_useThisKey != 0)
    {
        mux_->connectToB(_conduit, _key);
    }
    else        
    {
        setKey(mux_->installOnSideB(_conduit));
    }
    return;
}

//
//Function: useThisKey
//
//Description:
//    This must be set on when new connection is wanted to allocate
//    with key in installer.
//

void pfInstallTransporter :: useThisKey(void)
{
    _useThisKey = 1;
    return;
}

// ---------------------------------------------------------------------

pfUnInstallTransporter :: pfUnInstallTransporter(pfKey removeKey_)
    : pfTransporter()
{
    setAcceptMethodToSynchronous();
    setKey(removeKey_);
    return;
}

pfUnInstallTransporter :: pfUnInstallTransporter(
    const pfUnInstallTransporter &other_)
    : pfTransporter(other_)
{
    setAcceptMethodToSynchronous();
    return;
}

pfUnInstallTransporter :: ~pfUnInstallTransporter(void)
{
    return;
}

pfUnInstallTransporter pfUnInstallTransporter
    :: createUnInstallTransporter(pfKey removeKey_)
{
    pfUnInstallTransporter newTransporter(removeKey_);
    return newTransporter;
}

//
//Function: clone
//
//Description:
//    Returns pointer to new transporter created by using copy constructor
//

pfUnInstallTransporter *pfUnInstallTransporter :: clone(void)
{
    pfUnInstallTransporter *newUnInstallTransporter =
        new pfUnInstallTransporter(*this);
    return newUnInstallTransporter;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfUnInstallTransporter :: atMux(pfMux *const mux_)
{    
    mux_->removeFromSideB(_key);
    return;
}

void pfUnInstallTransporter :: atProtocol(
    pfProtocol *const protocol_,
    pfState *)
{
    if (isFromA() == 1)
    {
        protocol_->toB(this);
    }
    else
    {
        protocol_->toA(this);
    }
    protocol_->disconnect();
    return;
}

// -----------------------------------------------------------------------

pfCrossConnecter :: pfCrossConnecter(void)
    : pfTransporter(),
      _otherSide(),
      _keyName()
{
    return;
}


pfCrossConnecter :: ~pfCrossConnecter(void)
{
    return;
}

pfCrossConnecter
    pfCrossConnecter :: createCrossConnector(void)
{
    pfCrossConnecter newTransporter;
    return newTransporter;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfCrossConnecter :: atMux (pfMux *const mux_)
{   
    // Travelling always from A to B
    mux_->tryToGotoB(this);
    return;
}

void pfCrossConnecter :: atFactory(pfFactory *const factory_)
{
    if (isFromA() == 0)
    {
        // Make new conduit or conduit string (many conduits). 
        pfConduit firstConduit;
        pfConduit lastConduit;
        factory_->makeConduit(firstConduit, lastConduit);

        factory_->sendInstallerToA(firstConduit);
          
        // Return new conduit to sender
        _otherSide = lastConduit;
          
        // Don't send further
    }
    else
    {
        // This must be an error.
        //assert(1 == 0);
    }
} 

bool pfCrossConnecter :: isDispatchKeySet(string &keyName_) const
{
    bool result = 0;
    
    if (_keyName == keyName_)
    {
        result = 1;
    }

    return result;
}

void pfCrossConnecter :: setKeyName(string &keyName_)
{

    _keyName = keyName_;

    return;
}

//
//Function: otherSide
//
//Description:
//    Set and get methods for _otherSide attribute
//

pfConduit pfCrossConnecter :: getOtherSide(void)
{
    return _otherSide;
}

void pfCrossConnecter :: setOtherSide(pfConduit &otherSide_)
{
    _otherSide = otherSide_;
}

// ---------------------------------------------------------------------

pfCloseRequestTransporter :: pfCloseRequestTransporter(void)
    : pfTransporter()
{
    return;
}

pfCloseRequestTransporter :: pfCloseRequestTransporter(
    const pfCloseRequestTransporter &other_)
    : pfTransporter(other_)
{
    return;
}

pfCloseRequestTransporter :: ~pfCloseRequestTransporter(void)
{
    return;
}

pfCloseRequestTransporter *
    pfCloseRequestTransporter :: createCloseRequestTransporter(void)
{
    pfCloseRequestTransporter *newTransporter = new pfCloseRequestTransporter;
    return newTransporter;
}

//
//Function: clone
//
//Description:
//    Returns pointer to new transporter created by using copy constructor
//

pfCloseRequestTransporter *pfCloseRequestTransporter :: clone(void)
{
    pfCloseRequestTransporter *newCloseRequestTransporter =
        new pfCloseRequestTransporter(*this);
    return newCloseRequestTransporter;
}

//
//Functions: atConduit methods
//
//Description:
//    Implements action to be done when transporter arrives to proper
//    conduit.
//

void pfCloseRequestTransporter :: atMux(pfMux *const mux_)
{    
    pfUnInstallTransporter unInstaller = 
        pfUnInstallTransporter::createUnInstallTransporter(_key);
    mux_->toB(&unInstaller);
    mux_->removeFromSideB(_key);
    mux_->deleteTransporter(this);
    return;
}

void pfCloseRequestTransporter :: atProtocol(
    pfProtocol *const protocol_,
    pfState *)
{
    pfCloseRequestTransporter *transporter = this->clone();
    transporter->setKey(protocol_->getKey());
    if (isFromA() == 1)
    {
        protocol_->toB(transporter);
    }
    else
    {
        protocol_->toA(transporter);
    }
    return;
}


// ---------------------------------------------------------------------

pfKeyQueryTransporter :: pfKeyQueryTransporter(void)
    : pfTransporter(),
      _keyAllocatedStatus(0)
{
    return;
}

pfKeyQueryTransporter :: pfKeyQueryTransporter(
    const pfKeyQueryTransporter &other_)
    : pfTransporter(other_),
      _keyAllocatedStatus(other_._keyAllocatedStatus)
{
    return;
}

pfKeyQueryTransporter :: ~pfKeyQueryTransporter(void)
{
    return;
}

pfKeyQueryTransporter
pfKeyQueryTransporter :: createKeyQueryTransporter(pfKey key_)
{
    pfKeyQueryTransporter newTransporter;
    newTransporter._key = key_;
    return newTransporter;
}

//
//Function: clone
//
//Description:
//

pfKeyQueryTransporter *pfKeyQueryTransporter :: clone(void)
{
    pfKeyQueryTransporter *newKeyQueryTransporter = new pfKeyQueryTransporter;
    return newKeyQueryTransporter;
}

//
//Function: atMux
//
//Description:
//     Checks if the key is allocated or not and saves status into
//     a variable
//

void pfKeyQueryTransporter :: atMux(pfMux *const mux_)
{
    _keyAllocatedStatus = mux_->isKeyInstalled(_key);

    return;
}

//
//Function: keyAllocatedStatus
//
//Description:
//     

bool pfKeyQueryTransporter :: keyAllocatedStatus(void) const
{
    return _keyAllocatedStatus;
}

