//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / UNI
//
//File: uniprotocol.h
//
//Version: $Revision: 1.35 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/14 06:56:32 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Jari Katajavuori
//
//Description:
//      UNI protocol conduit implementation.
//
//Copyright:
//
//
//Licence:
//
//
//History: 

#include "uniprotocol.h"

#include "pf/mux.h"
#include "pf/factory.h"
#include "pf/debug.h"

#include "iface/sigif/sigupprimitives.h"
#include "iface/sigif/sigdownprimitives.h"

#include "unistrings.h"
#include "unimode.h"
#include "uniprimitives.h"
#include "unimultipointprotocol.h"

#include "unistate.h"
#include "uniprimitives.h"
#include "unipdu.h"

#include "protocol/cc/ccprotocol.h"

uniProtocol *uniProtocol :: create(uniMode *mode_)
{
    uniProtocol *protocol = new uniProtocol;
    
    if (mode_ != 0)
    {
        protocol->setMode(mode_);
    }
    else
    {
        protocol->setUserMode();
    }
    protocol->setDefaultLocation();
    protocol->initTimers();
    protocol->changeToNullState();
    
    return protocol;
}

uniProtocol :: uniProtocol(void)
    : pfProtocol(),
      pfStorage(),
      _multipointSet(),
      _mode(0),
      _cc(0)
{
    defineStorage(uniSavedSETUPStr);
    defineStorage(uniSavedRELEASEStr);
    defineInteger(uniCauseRStr);
    defineBoolean(uniAllowDoubleCausesStr);
    defineInteger(uniCallStateValueStr);
    defineInteger(uniLocationStr);
    defineInteger(uniZeroEndpointReferenceStr);
    defineBoolean(uniMultipointConnectionFlagStr);
    defineBoolean(uniQ2931ConnectionFlagStr);
    defineBoolean(uniAllowFinalClearingStr);
    defineInteger(uniT303TimeoutsStr);
    defineInteger(uniT308TimeoutsStr);
    defineInteger(uniT322TimeoutsStr);
    
    // Set defaults

    // Double cause occurence allowed normally
    setBooleanTrue(uniAllowDoubleCausesStr);

    // Call state set to Null
    setInteger(uniCallStateValueStr, uniState_Null);

    // Not a multipoint connection
    setBooleanFalse(uniMultipointConnectionFlagStr);

    // Not a Q.2931 connection
    setBooleanFalse(uniQ2931ConnectionFlagStr);
    
    // Allow final clearing by default
    setBooleanTrue(uniAllowFinalClearingStr);
    
    return;
}

uniProtocol :: uniProtocol(const uniProtocol &other_)
    : pfProtocol(other_),
      pfStorage(other_),
      _multipointSet(other_._multipointSet),
      _mode(other_._mode),
      _cc(other_._cc)
{
    return;
}

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

pfProtocol *uniProtocol :: cloneImplementation(void) const
{
    uniProtocol *protocol = new uniProtocol(*this);
    protocol->initTimers();
    protocol->changeToNullState();
    return protocol;
}

//
// Method: createPointToPointConduits
//
// Description:
//      Creates a cc protocol above the uni protocol for
//      a point-to-point call. Returns cc conduit.
//

pfConduit uniProtocol :: createPointToPointConduits(void)
{
    if (_cc == 0)
    {
        _cc = ccProtocol::create();
    }
    pfConduit cc(_cc);
    pfConduit proxy(this);
    cc.connectToA(proxy);
    proxy.connectToB(cc);

    // Copy ID (LINK) number
    cc.setId(getId());
    
    return cc;
}

//
// Method: createMultipointConduits
//
// Description:
//      This method creates conduit objects for a multipoint call.
//      If cc is installed already above the uni protocol, it'll be
//      saved as the initial cc; a multipoint multiplexer will be
//      installed between cc and uni. Notice that there is no
//      multipoint protocol between initial cc and uni.
//      Structure:
//                          .___.  
//                          |CC |     initial
//                          `-.-'     connection
//                  .___.   ._|_.  ._|_.
//                  | F |-> |pmp|  |CC |
//                  `-.-'   `-.-'  `-.-'
//                    |   ____|______|___
//                    `---\_____________/
//                               |
//
//       Warning: this method cannot create conduits at terminating
//       user side! Only at root user (originating) and network
//       (terminating).
//

pfConduit uniProtocol :: createMultipointConduits(void)
{
    // Get link id and set it to conduit objects
    pfKey id = getId();
        
    if (_cc == 0)
    {
        _cc = ccProtocol::create();
    }
    pfConduit cc(_cc);
    cc.setId(id);
    
    // Check if already multipoint connection
    if (getBoolean(uniMultipointConnectionFlagStr) == 0)
    {
        pfConduit mux = pfMux::createMux(uniMaxEndpointReference,
                                         uniEndpointReferenceStr);
        mux.setId(id);
        pfConduit protoCC(ccProtocol::create(_cc));
        protoCC.setId(id);
        pfConduit pmp = uniMultipointProtocol::create();
        pmp.setId(id);
        pfConduit factory = pfFactory::createFactory(pmp, protoCC);
        factory.setId(id);
        pfConduit proxy(this);

        // Connect conduits
        
        proxy.connectToB(mux);
        mux.connectToA(proxy);
        mux.connectToB(factory);
        factory.connectToA(mux);
        
        // Install cc for initial multipoint connection
        
        pfInstallTransporter installer =
            pfInstallTransporter::createInstallTransporter(cc);
        if (isNetworkMode() == 0)
        {
            installer.setKey(uniZeroEndpointReferenceAtOSide);
            setInteger(uniZeroEndpointReferenceStr,
                       uniZeroEndpointReferenceAtOSide);
        }
        else
        {
            installer.setKey(uniZeroEndpointReferenceAtTSide);
            setInteger(uniZeroEndpointReferenceStr,
                       uniZeroEndpointReferenceAtTSide);
        }
        installer.useThisKey();
        toB(&installer);
        cc.connectToA(mux);
        
        // Set flag for multipoint connection
        setBooleanTrue(uniMultipointConnectionFlagStr);

        // Clear PMP set
        _multipointSet.clear();
    }
    
    return cc;
}

//
// Method: getSideB
//
// Description:
//      Returns side B conduit.
//

pfConduit uniProtocol :: getSideB(void)
{
    return _sideB;
}

//
// Method: getCC
//
// Description:
//      Returns pointer to cc object connected at side B
//

ccProtocol *uniProtocol :: getCC(void)
{
    ccProtocol *protocol = (ccProtocol*)_sideB.getAddress();
    return protocol;
}

//
// Method: addPMP
//
// Description:
//      Adds the endpoint reference of the message to the set.
//      Called when new PMP is installed.
//

void uniProtocol :: addPMP(pfMessenger *message_)
{
    pfUlong reference = message_->getInteger(uniEndpointReferenceStr);
    _multipointSet.insert(reference);
    debugPfUlong("Insert PMP with ER ", reference);
    return;
}

//
// Method: removePMP
//
// Description:
//

void uniProtocol :: removePMP(pfMessenger *message_)
{
    pfUlong reference = message_->getInteger(uniEndpointReferenceStr);
    _multipointSet.erase(reference);
    // Remove physical PMP
    debugPfUlong("Remove PMP with ER ", reference);

    pfUnInstallTransporter uninstaller =
        pfUnInstallTransporter::createUnInstallTransporter(reference);
    toB(&uninstaller);
    
    if (_multipointSet.empty() != 0)
    {
        triggerContinueClearing();
    }
    return;
}

//
// Method: isExistingPMP
//
// Description:
//      Returns true if given endpoint reference exists.
//

bool uniProtocol :: isExistingPMP(pfUlong endpointReference_)
{    
    bool result = _multipointSet.exists(endpointReference_);
    return result;
}

//
// Method: isPMPSetEmpty
//
// Description:
//      Returns true if PMP set is empty
//

bool uniProtocol :: isPMPSetEmpty(void)
{
    bool result = _multipointSet.empty();
    return result;
}   

//
// Method: saveSetupData
//
// Description:
//      Saves reusable data from SETUP message into storage for use
//      in the ADD PARTY.
//

void uniProtocol :: saveSetupData(uniSETUPpdu *message_)
{
    pfStorage &save = getStorage(uniSavedSETUPStr);
    save.defineStorage(uniATDStr);
    save.setStorage(uniATDStr, message_->getStorage(uniATDStr));
    save.defineStorage(uniBroadbandBearerCapabilityStr);
    save.setStorage(uniBroadbandBearerCapabilityStr,
                    message_->getStorage(uniBroadbandBearerCapabilityStr));
    save.defineStorage(uniQoSParametersStr);
    save.setStorage(uniQoSParametersStr,
                    message_->getStorage(uniQoSParametersStr));
    return;
}

//
// Method: getSavedSetupData
//
// Description:
//      Gets saved SETUP data into given ADD PARTY req
//

void uniProtocol :: putSavedSetupDataInto(uniADD_PARTYreq *message_)
{
    pfStorage &save = getStorage(uniSavedSETUPStr);
    message_->defineStorage(uniATDStr);
    message_->setStorage(uniATDStr, save.getStorage(uniATDStr));
    message_->defineStorage(uniBroadbandBearerCapabilityStr);
    message_->setStorage(uniBroadbandBearerCapabilityStr,
                         save.getStorage(uniBroadbandBearerCapabilityStr));
    message_->defineStorage(uniQoSParametersStr);
    message_->setStorage(uniQoSParametersStr,
                         save.getStorage(uniQoSParametersStr));
    return;
}

//
// Methods: send*ToCC
//
// Description:
//      Sends a message (response) to CC (upper) protocol.
//

void uniProtocol :: sendSETUPindToCC(uniSETUPpdu *pdu_)
{
    sigSETUPind *primitive = new sigSETUPind;

    primitive->extend(*pdu_);
                            
    toB(primitive);
    return;
}

void uniProtocol :: sendSETUPconfToCC(uniCONNECTpdu *pdu_)
{
    sigSETUPconf *primitive = new sigSETUPconf;
    primitive->copyIfPresent(*pdu_, uniAALParametersStr);
    primitive->copyIfPresent(*pdu_, uniBroadbandLowLayerInfoStr);
    primitive->copyIfPresent(*pdu_, uniConnectionIdentifierStr);
    primitive->copyIfPresent(*pdu_, uniEndpointReferenceStr);
    toB(primitive);
    return;
}

void uniProtocol :: sendSETUP_COMPLETEindToCC(uniCONNECT_ACKpdu *pdu_)
{
    sigSETUP_COMPLETEind *primitive = new sigSETUP_COMPLETEind;
    primitive->copyIfPresent(*pdu_, uniEndpointReferenceStr);
    toB(primitive);
    return;
}

void uniProtocol :: sendPROCEEDINGindToCC(uniCALL_PROCEEDINGpdu *pdu_)
{
    sigPROCEEDINGind *primitive = new sigPROCEEDINGind;
    primitive->copyIfPresent(*pdu_, uniConnectionIdentifierStr);
    primitive->copyIfPresent(*pdu_, uniEndpointReferenceStr);
    toB(primitive);
    return;
}

void uniProtocol :: sendALERTINGindToCC(uniALERTINGpdu *)
{
    //++TODO++
    return;
}

void uniProtocol :: sendRELEASEconfToCC(pfUlong cause_)
{
    sigRELEASEconf *primitive = new sigRELEASEconf;
    (*primitive)[sigCauseStr].setInteger(sigCause_ValueStr, cause_);    
    toCC(primitive);
    return;
}

void uniProtocol :: sendRELEASEconfToCC(uniRELEASE_COMPLETEpdu *pdu_)
{
    sigRELEASEconf *primitive = new sigRELEASEconf;
    primitive->copyIfPresent(*pdu_, uniCauseStr);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRELEASEconfToCC(uniRELEASEpdu *pdu_)
{
    sigRELEASEconf *primitive = new sigRELEASEconf;
    primitive->copyIfPresent(*pdu_, uniCauseStr);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRELEASEindToCC(uniRELEASEpdu *pdu_)
{
    sigRELEASEind *primitive = new sigRELEASEind;
    primitive->extend(*pdu_);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRELEASEindToCC(pfUlong cause_)
{
    sigRELEASEind *primitive = new sigRELEASEind;
    (*primitive)[sigCauseStr].setInteger(sigCause_ValueStr, cause_);
    (*primitive)[sigCauseStr].setInteger(sigCause_LocationStr, 0);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRESET_ERRORindToCC(pfStorage &storage_)
{
    sigRESET_ERRORind *primitive = new sigRESET_ERRORind;
    primitive->fetch(storage_);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRESETconfToCC(pfStorage &storage_)
{
    sigRESETconf *primitive = new sigRESETconf;
    primitive->fetch(storage_);
    toCC(primitive);
    return;
}

void uniProtocol :: sendRESETindToCC(pfStorage &storage_)
{
    sigRESETind *primitive = new sigRESETind;
    primitive->fetch(storage_);
    toCC(primitive);
    return;
}

//
// Methods: send*ToPMP
//
// Description:
//      Sends a primitive to PMP (upper) protocol.
//

void uniProtocol :: sendADD_PARTYreqToPMP(uniADD_PARTYpdu *message_)
{
    uniADD_PARTYreq *primitive = uniADD_PARTYreq::create();
    primitive->fetch(*message_);
    // ++REMOVE++ Message type overwritten
    primitive->setInteger(uniMessageTypeStr, 5);

    putSavedSetupDataInto(primitive);
    toB(primitive);
    return;
}

void uniProtocol :: sendADD_PARTYrespToPMP(uniADD_PARTY_ACKpdu *message_)
{
    uniADD_PARTYresp *primitive = uniADD_PARTYresp::create();
    primitive->fetch(*message_);
    toB(primitive);
    return;
}

void uniProtocol :: sendADD_PARTYerrorToPMP(uniADD_PARTY_REJECTpdu *message_)
{
    uniADD_PARTYerror *primitive = uniADD_PARTYerror::create();
    primitive->fetch(*message_);
    toB(primitive);
    return;
}

void uniProtocol :: sendDROP_PARTYreqToPMP(uniDROP_PARTYpdu *message_)
{
    uniDROP_PARTYreq *primitive = uniDROP_PARTYreq::create();
    primitive->fetch(*message_);
    toB(primitive);
    return;
}

void uniProtocol :: sendDROP_PARTYrespToPMP(uniDROP_PARTY_ACKpdu *message_)
{
    uniDROP_PARTYresp *primitive = uniDROP_PARTYresp::create();
    primitive->fetch(*message_);
    toB(primitive);
    return;
}

void uniProtocol :: sendDROP_PARTYreqToAllPMPs(pfMessenger *message_)
{
    uniDROP_PARTYreq *primitive = uniDROP_PARTYreq::create();
    if (message_->isVariableDefined(uniCauseStr) != 0)
    {
        primitive->defineStorage(uniCauseStr);
        primitive->setStorage(uniCauseStr, message_->getStorage(uniCauseStr));
    }
    toAllPMPs(primitive);
    delete primitive;
    return;
}

void uniProtocol :: sendSTATUSreqToPMP(uniSTATUS_ENQUIRYpdu *message_)
{
    uniSTATUSreq *primitive = uniSTATUSreq::create();
    primitive->fetch(*message_);
    toB(primitive);
    return;
}

//
// Methods: send*ToCoOrd
//
// Description:
//      Sends a message (primitive) to coordination (lower) protocol.
//

void uniProtocol :: sendRESETreqToCoOrd(pfStorage &storage_)
{
    uniRESETreq *primitive = uniRESETreq::create();
    primitive->fetch(storage_);
    toA(primitive);
    return;
}

void uniProtocol :: sendRESETrespToCoOrd(pfStorage &storage_)
{
    uniRESETresp *primitive = uniRESETresp::create();
    primitive->fetch(storage_);
    toA(primitive);
    return;
}

void uniProtocol :: sendRESET_ERRORrespToCoOrd(pfStorage &storage_)
{
    uniRESET_ERRORresp *primitive = uniRESET_ERRORresp::create();
    primitive->fetch(storage_);
    toA(primitive);
    return;
}

void uniProtocol :: sendSETUPpduToCoOrd(void)
{
    if (isValuePresent(uniSavedSETUPStr) != 0)
    {
        sendSETUPpduToCoOrd((*this)[uniSavedSETUPStr]);
    }
    return;
}

void uniProtocol :: sendSETUPpduToCoOrd(sigSETUPreq *primitive_)
{
    setStorage(uniSavedSETUPStr, *primitive_);
    sendSETUPpduToCoOrd(*primitive_);
    return;
}

void uniProtocol :: sendSETUPpduToCoOrd(pfStorage &storage_)
{
    uniSETUPpdu *pdu = uniSETUPpdu::create();
    pdu->extend(storage_);

    if (isMultipointConnection() != 0)
    {        
        pdu->setEndpointReference(uniZeroEndpointReferenceAtOSide);
    }
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendCONNECTpduToCoOrd(sigSETUPresp *primitive_)
{
    uniCONNECTpdu *pdu = uniCONNECTpdu::create();
    pdu->extend(*primitive_);

    if (isMultipointConnection() != 0)
    {
        pdu->setEndpointReference(uniZeroEndpointReferenceAtTSide);
    }    
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendCONNECT_ACKpduToCoOrd(void)
{
    uniCONNECT_ACKpdu *pdu = uniCONNECT_ACKpdu::create();

    if (isMultipointConnection() != 0)
    {
        pdu->setEndpointReference(uniZeroEndpointReferenceAtOSide);
    }
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendCALL_PROCEEDINGpduToCoOrd(sigPROCEEDINGreq *primitive_)
{
    uniCALL_PROCEEDINGpdu *pdu = uniCALL_PROCEEDINGpdu::create();
    pdu->extend(*primitive_);
    if (isMultipointConnection() != 0)
    {
        pdu->setEndpointReference(uniZeroEndpointReferenceAtTSide);
    }
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendALERTINGpduToCoOrd(sigALERTINGreq *)
{
    if (getBoolean(uniQ2931ConnectionFlagStr) != 0)
    {
        uniALERTINGpdu *pdu = uniALERTINGpdu::create();
        //pdu->extend(*primitive_);
        pdu->setCallReference(this);
        toA(pdu);
    }
    return;
}

void uniProtocol :: sendRELEASEpduToCoOrd(void)
{
    if (isValuePresent(uniSavedRELEASEStr) != 0)
    {
        sendRELEASEpduToCoOrd((*this)[uniSavedRELEASEStr]);
    }
    return;
}

void uniProtocol :: sendRELEASEpduToCoOrd(sigRELEASEreq *primitive_)
{
    setStorage(uniSavedRELEASEStr, *primitive_);
    sendRELEASEpduToCoOrd(*primitive_);
    return;
}

void uniProtocol :: sendRELEASEpduToCoOrd(pfStorage &storage_)
{
    uniRELEASEpdu *pdu = uniRELEASEpdu::create();
    pdu->extend(storage_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendRELEASEpduToCoOrd(pfUlong cause1_, pfUlong cause2_)
{
    uniRELEASEpdu *pdu = uniRELEASEpdu::create();

    pfUlong location = getInteger(uniLocationStr);
    
    // Set cause (first one)
    pdu->defineCause();
    pdu->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause1_);
    pdu->getStorage(uniCauseStr).setInteger(uniCause_LocationStr, location);

    // If cause2_ is given and double causes are allowed, set it.
    if ((cause2_ != 0) && (getBoolean(uniAllowDoubleCausesStr) != 0))
    {
        pdu->defineCause();
        pdu->getStorage(uniCause2Str).setInteger(uniCause_ValueStr, cause2_);
        pdu->getStorage(uniCause2Str).setInteger(uniCause_LocationStr, location);
    }

    setStorage(uniSavedRELEASEStr, *pdu);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendRELEASE_COMPLETEpduToCoOrd(sigRELEASEresp *primitive_)
{
    uniRELEASE_COMPLETEpdu *pdu = uniRELEASE_COMPLETEpdu::create();
    pdu->extend(*primitive_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendRELEASE_COMPLETEpduToCoOrd(pfUlong cause_)
{
    uniRELEASE_COMPLETEpdu *pdu = uniRELEASE_COMPLETEpdu::create();
    pfUlong location = getInteger(uniLocationStr);
    
    pdu->defineCause();
    pdu->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause_);
    pdu->getStorage(uniCauseStr).setInteger(uniCause_LocationStr, location);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendSTATUSpduToCoOrd(pfUlong cause_)
{
    uniSTATUSpdu *pdu = uniSTATUSpdu::create();
    pdu->defineCause();
    pdu->defineCallState();
    pdu->getStorage(uniCauseStr).setInteger(uniCause_LocationStr,
                                            getInteger(uniLocationStr));
    pdu->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause_);
    pdu->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr, getInteger(uniCallStateValueStr));

    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendSTATUSpduToCoOrd(uniSTATUSresp *message_)
{
    uniSTATUSpdu *pdu = uniSTATUSpdu::create();
    pdu->defineCause();
    pdu->defineCallState();
    pdu->getStorage(uniCauseStr).setInteger(uniCause_LocationStr,
                                            getInteger(uniLocationStr));
    pdu->getStorage(uniCauseStr).setInteger(
        uniCause_ValueStr, uniCauseValue_ResponseToSTATUS_ENQUIRY);
    pdu->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr, getInteger(uniCallStateValueStr));
    pdu->copyVariable(*message_, uniEndpointReferenceStr);
    pdu->copyVariable(*message_, uniEndpointStateStr);

    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendSTATUSpduToCoOrd(uniSTATUS_ENQUIRYpdu *message_)
{
    uniSTATUSpdu *pdu = uniSTATUSpdu::create();

    if (message_->isVariableDefined(uniCauseStr) != 0)
    {
        // In this case cause is defined by coord
        pdu->copyVariable(*message_, uniCauseStr);
        pdu->getStorage(uniCauseStr).setInteger(
            uniCause_LocationStr,
            getInteger(uniLocationStr));
    } 
    else
    {
        // Normal enquiry
        pdu->defineCause();
    
        pdu->getStorage(uniCauseStr).setInteger(
            uniCause_LocationStr,
            getInteger(uniLocationStr));
        pdu->getStorage(uniCauseStr).setInteger(
            uniCause_ValueStr, 
            uniCauseValue_ResponseToSTATUS_ENQUIRY);
    }

    pdu->defineCallState();
    pdu->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr, getInteger(uniCallStateValueStr));

    if (message_->isMultipointMessage() != 0)
    {
        pdu->defineInteger(uniEndpointReferenceStr);
        pdu->defineEndpointState();
        
        pfUlong endpointReference = message_->getInteger(uniEndpointReferenceStr);
        pdu->setInteger(uniEndpointReferenceStr, endpointReference);
        
        pdu->getStorage(uniEndpointStateStr).setInteger(
            uniEndpointState_PartyStateStr, uniPartyState_Null);
    }
    
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendSTATUS_ENQUIRYpduToCoOrd(void)
{
    uniSTATUS_ENQUIRYpdu *pdu = uniSTATUS_ENQUIRYpdu::create();
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendSTATUSpduToCoOrd(uniErrorInfo &errorInfo_,
                                         uniStateValue stateValue_)
{
    uniSTATUSpdu *pdu = uniSTATUSpdu::create();
    pdu->defineCause();
    pdu->setStorage(uniCauseStr, errorInfo_);

    pdu->defineCallState();    
    pdu->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr, stateValue_);

    pdu->setCallReference(this);
    toA(pdu);
    return;
}

//
// Method: verifyMessage
//
// Description:
//      Performs pdu verifying process, returns true if
//      action should be taken.
//

bool uniProtocol :: verifyMessage(uniPdu *pdu_, uniStateValue stateAfter_)
{
    bool result = true;
    uniErrorInfo *errorInfo = pdu_->getErrorInfo();
    if (errorInfo != 0)
    {
        if (errorInfo->isFatalError() == 0)
        {
            // Non-fatal error occured. Report status.
            sendSTATUSpduToCoOrd(*errorInfo, stateAfter_);
        }
        else
        {
            // Fatal error
            result = false;
        }
    }
    return result;
}

void uniProtocol :: sendLINK_ESTABLISHreqToCoOrd(void)
{
    uniLINK_ESTABLISHreq *primitive = uniLINK_ESTABLISHreq::create();
    toA(primitive);
    return;
}

void uniProtocol :: sendLINK_RELEASEreqToCoOrd(void)
{
    uniLINK_RELEASEreq *primitive = uniLINK_RELEASEreq::create();
    toA(primitive);
    return;
}

void uniProtocol :: sendADD_PARTYpduToCoOrd(uniADD_PARTYreq *message_)
{
    uniADD_PARTYpdu *pdu = uniADD_PARTYpdu::create();
    pdu->extend(*message_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendADD_PARTY_ACKpduToCoOrd(uniADD_PARTYresp *message_)
{
    uniADD_PARTY_ACKpdu *pdu = uniADD_PARTY_ACKpdu::create();
    pdu->extend(*message_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendADD_PARTY_REJECTpduToCoOrd(uniADD_PARTYerror *message_)
{
    uniADD_PARTY_REJECTpdu *pdu = uniADD_PARTY_REJECTpdu::create();
    pdu->copyVariable(*message_, uniEndpointReferenceStr);
    pdu->copyVariable(*message_, uniCauseStr);
    if ((*pdu)[uniCauseStr].isVariableDefined(uniCause_LocationStr) == 0)
    {
        pfUlong defaultLocation = getInteger(uniLocationStr);
        (*pdu)[uniCauseStr].defineInteger(uniCause_LocationStr);
        (*pdu)[uniCauseStr].setInteger(uniCause_LocationStr, defaultLocation);
    }
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendDROP_PARTYpduToCoOrd(uniDROP_PARTYreq *message_)
{
    uniDROP_PARTYpdu *pdu = uniDROP_PARTYpdu::create();
    pdu->extend(*message_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

void uniProtocol :: sendDROP_PARTY_ACKpduToCoOrd(uniDROP_PARTYresp *message_)
{
    uniDROP_PARTY_ACKpdu *pdu = uniDROP_PARTY_ACKpdu::create();
    pdu->extend(*message_);
    pdu->setCallReference(this);
    toA(pdu);
    return;
}

//
// Methods: changeTo*State
//
// Description:
//      Makes a state change.
//

void uniProtocol :: changeToNullState(void)
{
    setInteger(uniCallStateValueStr, uniState_Null);
    changeState(_mode->createNullState());
    return;
}

void uniProtocol :: changeToCallInitiatedState(void)
{
    setInteger(uniCallStateValueStr, uniState_CallInitiated);
    changeState(_mode->createCallInitiatedState());
    return;
}

void uniProtocol :: changeToOutgoingCallProceedingState(void)
{
    setInteger(uniCallStateValueStr, uniState_OutgoingCallProceeding);
    changeState(_mode->createOutgoingCallProceedingState());
    return;
}

void uniProtocol :: changeToCallDeliveredState(void)
{
    setInteger(uniCallStateValueStr, uniState_CallDelivered);
    changeState(_mode->createCallDeliveredState());
    return;
}

void uniProtocol :: changeToCallPresentState(void)
{
    setInteger(uniCallStateValueStr, uniState_CallPresent);
    changeState(_mode->createCallPresentState());
    return;
}

void uniProtocol :: changeToCallReceivedState(void)
{
    setInteger(uniCallStateValueStr, uniState_CallReceived);
    changeState(_mode->createCallReceivedState());
    return;
}

void uniProtocol :: changeToConnectRequestState(void)
{
    setInteger(uniCallStateValueStr, uniState_ConnectRequest);
    changeState(_mode->createConnectRequestState());
    return;
}

void uniProtocol :: changeToIncomingCallProceedingState(void)
{
    setInteger(uniCallStateValueStr, uniState_IncomingCallProceeding);
    changeState(_mode->createIncomingCallProceedingState());
    return;
}

void uniProtocol :: changeToActiveState(void)
{
    setInteger(uniCallStateValueStr, uniState_Active);
    changeState(_mode->createActiveState());
    return;
}

void uniProtocol :: changeToReleaseRequestState(void)
{
    setInteger(uniCallStateValueStr, uniState_ReleaseRequest);
    changeState(_mode->createReleaseRequestState());
    return;
}

void uniProtocol :: changeToReleaseIndicationState(void)
{
    setInteger(uniCallStateValueStr, uniState_ReleaseIndication);
    changeState(_mode->createReleaseIndicationState());
    return;
}

//
// Method: statusEnquiry
//
// Description:
//      Performs status enquiry procedure
//

void uniProtocol :: statusEnquiry(void)
{
    if (isTimerActive(uniT322Str) == 0)
    {
        sendSTATUS_ENQUIRYpduToCoOrd();
        setInteger(uniT322TimeoutsStr, 0);
        startTimer(uniT322Str);
    }
    return;
}

//
// Method: isCompatibleState
//
// Description:
//      Return non-zero if given state is compatible with current
//

bool uniProtocol :: isCompatibleState(pfUlong state_)
{
    bool result = (state_ == getInteger(uniCallStateValueStr));
    return result;
}

//
// Method: clearCall
//
// Description:
//      Clears the call.
//

void uniProtocol :: clearCall(void)
{
    stopAllTimers();
    debugUser("Call Cleared.");
    changeToNullState();
    finalClearing();
    return;
}

void uniProtocol :: clearCall(pfUlong cause_)
{
    stopAllTimers();
    debugPfUlong("Clearing call with cause ", cause_);
    sendRELEASEconfToCC(cause_);
    changeToNullState();
    // Wait for response from cc
    return;
}

//
// Method: clearCallIfNullState
//
// Description:
//      Clears the call if call state null 
//

void uniProtocol :: clearCallIfNullState(uniSTATUSpdu *message_)
{
    pfUlong cs =
        message_->getStorage(uniCallStateStr).getInteger(uniCallState_StateStr);
    if (cs == uniState_Null)
    {
        clearCall(uniCauseValue_MessageNotCompatibleWithCallState);
    }
    return;
}

//
// Method: startReleaseProcess
//
// Description:
//      Begins the call release process by sending RELEASE to
//      other side of the call.
//

void uniProtocol :: startReleaseProcess(pfUlong causeUp_, 
                                        pfUlong causeDown_)
{
    if (causeDown_ == 0) 
    {
        causeDown_ = causeUp_;
    }
    debugUser("Starting release process.");
    debugPfUlong("Cause to CC", causeUp_);
    debugPfUlong("Cause to CoOrd", causeDown_);
    stopAllTimers();
    sendRELEASEindToCC(causeUp_);
    sendRELEASEpduToCoOrd(causeDown_);
    setInteger(uniT308TimeoutsStr, 0);
    startTimer(uniT308Str);
    _mode->continueReleaseProcess(this);
    return;
}

//
// Method: stopAllTimers
//
// Description:
//      Stops all timers. Timers are mode dependent so we have to call
//      a mode dependent method.
//

void uniProtocol :: stopAllTimers(void)
{
    stopTimer(uniT301Str);
    stopTimer(uniT303Str);
    stopTimer(uniT308Str);
    stopTimer(uniT310Str);
    stopTimer(uniT313Str);
    stopTimer(uniT322Str);
    stopTimer(uniTREMOVEDStr);
    return;
}

//
// Method: clearPMPs
//
// Description:
//      Disallows final clearing since DROP PARTY is initiated,
//      and starts TREMOVED timer.
//

void uniProtocol :: clearPMPs(pfMessenger *message_)
{
    if (isPMPSetEmpty() == 0)
    {
        setBooleanFalse(uniAllowFinalClearingStr);
        sendDROP_PARTYreqToAllPMPs(message_);
        startTimer(uniTREMOVEDStr);
    }
    return;
}

//
// Method: triggerClearingToContinue
//
// Description:
//

void uniProtocol :: triggerContinueClearing(void)
{
    debugUser("Triggering continue clearing");
    // accept timeout
    return;
}

//
// Method: finalClearing
//
// Description:
//      Removes UNI / CC objects if allowed
//

void uniProtocol :: finalClearing(void)
{
    bool allowed = getBoolean(uniAllowFinalClearingStr);
    if (allowed != 0)
    {
        // Close all
        debugUser("Removing CC / UNI");
        pfKey key = getKey();
        pfUnInstallTransporter uninstaller =
            pfUnInstallTransporter::createUnInstallTransporter(key);
        toA(&uninstaller);
    }
    return;
}

//
// Method: reportIncompatibleMessage
//
// Description:
//      Reports with STATUS about a message received in incompatible
//      state.
//

void uniProtocol :: reportIncompatibleMessage(uniPdu *pdu_)
{
    uniSTATUSpdu *status = uniSTATUSpdu::create();
    status->defineCause();
    status->defineCallState();

    pfStorage &cause = status->getStorage(uniCauseStr);
    cause.setInteger(uniCause_LocationStr, getInteger(uniLocationStr));
    cause.setInteger(uniCause_ValueStr,
                     uniCauseValue_MessageNotCompatibleWithCallState);
    cause.defineInteger(uniCause_MessageTypeStr);
    cause.setInteger(uniCause_MessageTypeStr,
                     pdu_->getInteger(uniMessageTypeStr));

    status->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr,
        getInteger(uniCallStateValueStr));

    status->setCallReference(this);
    toA(status);
    return;
}

//
// Method: setZeroEndpointReference
//
// Description:
//      Sets zero endpoint reference to the primitive 
//

void uniProtocol :: setZeroEndpointReference(pfMessenger *message_)
{
    if (message_->isVariableDefined(uniEndpointReferenceStr) == 0)
    {
        message_->defineInteger(uniEndpointReferenceStr);
    }
    message_->setInteger(uniEndpointReferenceStr,
                         getInteger(uniZeroEndpointReferenceStr));
    return;    
}

//
// Method: setMultipointConnection
//
// Description:
//      Sets multipoint connection flag.
//

void uniProtocol :: setMultipointConnection(void)
{
    setBooleanTrue(uniMultipointConnectionFlagStr);
    return;
}

//
// Method: isMultipointConnection
//
// Description:
//      Returns multipoint connection flag.
//

bool uniProtocol :: isMultipointConnection(void)
{
    bool result = getBoolean(uniMultipointConnectionFlagStr);
    return result;
}

//
// Method: setUserMode
//
// Description:
//      Sets UNI protocol conduit into user mode
//

void uniProtocol :: setUserMode(void)
{
    setMode(uniUserMode::instance());
    debugUser("Now in User mode");
    return;
}

//
// Method: setNetworkMode
//
// Description:
//      Sets UNI protocol conduit into network mode
//

void uniProtocol :: setNetworkMode(void)
{
    setMode(uniNetworkMode::instance());
    debugUser("Now in Network mode");
    return;
}

//
// Method: isNetworkMode
//
// Description:
//      Returns nonzero (true) if this protocol is in network mode.
//

bool uniProtocol :: isNetworkMode(void)
{
    bool result = _mode->isNetworkMode();
    return result;
}

//
// Method: compareCI
//
// Description:
//      Compares CI (vpi/vci) in given pdu to values in saved 
//      SETUP. Returns false if differ.
//

bool uniProtocol :: compareCI(uniPdu *pdu_)
{
    pfBoolean result = true;
    pfStorage &saved = getStorage(uniSavedSETUPStr);
    if ((pdu_->isVariableDefined(uniConnectionIdentifierStr) != 0) &&
        (saved.isVariableDefined(uniConnectionIdentifierStr) != 0))
    {
        pfStorage &pci = pdu_->getStorage(uniConnectionIdentifierStr);
        pfStorage &sci = saved.getStorage(uniConnectionIdentifierStr);
        if ((pci.getInteger(uniConnectionIdentifier_VPCIStr) !=
             sci.getInteger(uniConnectionIdentifier_VPCIStr)) ||
            (pci.getInteger(uniConnectionIdentifier_VCIStr) !=
             sci.getInteger(uniConnectionIdentifier_VCIStr)))
        {
            result = false;
        }
    }        
    return result;
}

//
// Method: setMode
//
// Description:
//      Uni operates in two modes, user and network. Selection must be
//      performed with this method.
//

void uniProtocol :: setMode(uniMode *mode_)
{
    if (mode_ == 0)
    {
        throw pfNullPointerException(PF_EX_INFO);
    }
    _mode = mode_;
    return;
}

//
// Method: setDefaulLocation
//
// Description:
//      Mode dependent default location (at network) will be set
//      with this.
//

void uniProtocol :: setDefaultLocation(void)
{
    setInteger(uniLocationStr, uniLocation_PrivateNWServingLocalUser);
    return;
}

//
// Method: initTimers
//
// Description:
//      Initializes all timers to their default values.
//

void uniProtocol :: initTimers(void)
{
    defineTimer(uniT301Str, uniT301timeout::create(), uniT301expire);
    defineTimer(uniT303Str, uniT303timeout::create(), uniT303expire);
    defineTimer(uniT308Str, uniT308timeout::create(), uniT308expire);
    defineTimer(uniT310Str, uniT310timeout::create(), uniT310expire);
    defineTimer(uniT313Str, uniT313timeout::create(), uniT313expire);
    defineTimer(uniT322Str, uniT322timeout::create(), uniT322expire); 
    defineTimer(uniTREMOVEDStr, uniTREMOVEDtimeout::create(),
                uniTREMOVEDexpire);
    return;
}

//
// Method: toAllPMPs
//
// Description:
//      Sends a copy of the given message to all PMP instances
//

void uniProtocol :: toAllPMPs(pfMessenger *primitive_)
{
    _multipointSet.beginGoThrough();

    while (_multipointSet.isDone() == 0)
    {
        pfMessenger *newMsg = primitive_->clone();

        newMsg->defineInteger(uniEndpointReferenceStr);
        pfUlong endpointReference = _multipointSet.currentItem();
        newMsg->setInteger(uniEndpointReferenceStr, endpointReference);

        toB(newMsg);
        _multipointSet.next();
    }
    return;
}
           
//
// Method: toCC
//
// Description:
//      Sends a message to cc adding zero endpoint reference if
//      necessary.
//

void uniProtocol :: toCC(pfMessenger *primitive_)
{
    if (getBoolean(uniMultipointConnectionFlagStr) != 0)
    {
        setZeroEndpointReference(primitive_);
    }
    toB(primitive_);
    return;
}

