//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / UNI
//
//File: uniprotocol.h
//
//Version: $Revision: 1.38 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/22 14:36:58 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Jari Katajavuori
//      Sami Raatikainen (based on version 1.26)
//
//Description:
//      UNI protocol conduit implementation.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 

#include "uniprotocol.h"

#include "protocol/uni/unistrings.h"
#include "protocol/uni/uniprimitives.h"
#include "protocol/uni/unimultipointprotocol.h"

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

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

#include "unimode.h"
#include "unistate.h"
#include "unipdu.h"

#include "protocol/cc/ccprotocol.h"
#include "protocol/sig/sigtimeouts.h"
#include "ie/cause.h"
#include "ie/callstate.h"

uniProtocol *uniProtocol :: create(bool isUserSide_)
{
    uniProtocol *protocol = new uniProtocol;
    if (isUserSide_ == true)
    {
        protocol->setUserMode();
    }
    else
    {
        protocol->setNetworkMode();
    }
    protocol->changeToNullState();
    protocol->setLocation(sigLocation_PrivateNWServingLocalUser);
    protocol->initTimers();
    return protocol;
}


uniProtocol :: uniProtocol(void)
    : sigProtocol(),
      pfStorage(),
      _multipointSet(),
      _cc(0),
      _uniT303Timeouts(0),
      _uniT308Timeouts(0),
      _uniT322Timeouts(0),
      _causeR(0)
{
    defineStorage(uniSavedSETUPStr);
    defineStorage(uniSavedRELEASEStr);
    defineInteger(uniCauseRStr);
    defineBoolean(uniAllowDoubleCausesStr);

    defineInteger(uniZeroEndpointReferenceStr);
    defineBoolean(uniQ2931ConnectionFlagStr); // in use
    defineBoolean(uniAllowFinalClearingStr);
    
    // Set defaults

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

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

uniProtocol :: uniProtocol(const uniProtocol &other_)
    : sigProtocol(other_),
      pfStorage(other_),
      _multipointSet(other_._multipointSet),
      _cc(other_._cc),
      _uniT303Timeouts(0),
      _uniT308Timeouts(0),
      _uniT322Timeouts(0),
      _causeR(0)
{
    return;
}

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

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


void uniProtocol :: createConduitsConnection(sigSETUPind *primitive_)
{
    if (isNetworkMode() == true)
    {
        if (primitive_->isAvailable(uniEndpointReferenceStr) != 0)
        {
            if (primitive_->getInteger(uniEndpointReferenceStr) !=
                uniZeroEndpointReferenceAtTSide)
            {
                THROW_INVALID_ENDPOINT_REFERENCE;
            }
            createMultipointConduits();
            saveSetupData(primitive_);
        }
        else
        {
            createPointToPointConduits();
        }
    }
    return;
}


//
// 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 (isMultipointConnection() == false)
    {
        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
        setMultipointConnection();

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

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

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

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


//
// 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(sigSETUPind *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*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*ToCC
//
// Description:
//      Those methods that need extra functionality are implemented
//      in this inherited protocol.
//




//
// Methods: send*ToCoOrd
//
// Description:
//      Sends a message (primitive) to coordination (lower) protocol.
//      Note! methods sending RELEASE req has to be rewritten or they
//      would have disappeared from the scope because new method with
//      different agrument was written here comparing to sigProtocol.
//

void uniProtocol :: sendSETUP_COMPLETEreqToDown(void)
{
    if (isNetworkMode() == false)
    {
        sigProtocol::sendSETUP_COMPLETEreqToDown();
    }
    return;
}


//new method
void uniProtocol :: sendALERTINGreqToDown(sigALERTINGreq *primitive_)
{
    if (getBoolean(uniQ2931ConnectionFlagStr) != 0)
    {
        // should primitive be empty (only callReference) ???!!!!
        setCallReference(primitive_);
        toA(primitive_);
    }
    return;
}

//new method
void uniProtocol :: sendRELEASEreqToDown(sigRELEASEreq *primitive_)
{
    _causeR = (primitive_->getIE(sigCauseStr))->clone();
    setCallReference(primitive_);
    saveMessage(primitive_);
    toA(primitive_);
    return;
}

//new method
void uniProtocol :: sendRELEASEreqToDown(pfUlong cause_)
{
    sigRELEASEreq *primitive = new sigRELEASEreq;
    setCause(primitive, cause_);
    sendRELEASEreqToDown(primitive);
    return;
}


// ++TODO++ setting multiple cause values to primitive
void uniProtocol :: sendRELEASEreqToDown(pfUlong cause1_, pfUlong cause2_)
{
    sigRELEASEreq *primitive = new sigRELEASEreq;
    pfUlong location = getLocation();
    
    /*
    // Set cause (first one)
    primitive->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause1_);
    primitive->getStorage(uniCauseStr).setInteger(uniCause_LocationStr, 
                                                  location);
    
    // If cause2_ is given and double causes are allowed, set it.
    if ((cause2_ != 0) && (getBoolean(uniAllowDoubleCausesStr) != 0))
    {
        // cause2 not defined in sigRELEASEreq's constructor
        primitive->defineStorage(uniCause2Str);
        primitive->getStorage(uniCause2Str).defineInteger(
            uniCause_LocationStr);
        primitive->getStorage(uniCause2Str).defineInteger(uniCause_ValueStr);
        primitive->getStorage(uniCause2Str).defineInteger(uniCause_ClassStr);
        primitive->getStorage(uniCause2Str).setInteger(uniCause_ValueStr, 
                                                       cause2_);
        primitive->getStorage(uniCause2Str).setInteger(uniCause_LocationStr, 
                                                       location);
    }

    setStorage(uniSavedRELEASEStr, *pdu);
    pdu->setCallReference(this);
    */
    
    sendRELEASEreqToDown(primitive);
    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();
    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));

    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 :: 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: start/stop*timer
//
// Description:
//       Methods to define the timers to be started and stopped for
//       different situations.
//

void uniProtocol :: startPROCEEDINGtimer(void)
{
    startTimer(uniT303Str);
    return;
}

void uniProtocol :: stopPROCEEDINGtimer(void)
{
    stopTimer(uniT303Str);
    return;
}

void uniProtocol :: startALERTINGtimer(void)
{
    startTimer(uniT310Str);
    return;
}

void uniProtocol :: stopALERTINGtimer(void)
{
    stopTimer(uniT310Str);
    return;
}

void uniProtocol :: startCONNECTtimer(void)
{
    //++TODO++ network always, butuser only if symmetric
    startTimer(uniT301Str);
    return;
}

void uniProtocol :: stopCONNECTtimer(void)
{
    stopTimer(uniT301Str);
    return;
}

void uniProtocol :: startCONNECT_ACKtimer(void)
{
    if (isNetworkMode() == false)
    {
        startTimer(uniT313Str);
    }
    return;
}

void uniProtocol :: stopCONNECT_ACKtimer(void)
{
    stopTimer(uniT313Str);
    return;
}

void uniProtocol :: startRELEASE_COMPLETEtimer(void)
{
    startTimer(uniT308Str);
    return;
}

void uniProtocol :: stopRELEASE_COMPLETEtimer(void)
{
    stopTimer(uniT308Str);
    return;
}


//
// Methods: *timeoutAct
//
// Description:
//       Methods to define the actions for different timeouts.
//       Some of the timers are supported on the other side only if
//       it supports symmetric operation, but checking that is not
//       necessary since timer may not be activated if it is not supported.
//

// T303
void uniProtocol :: sigAwaitPROCEEDINGtimeoutAct(void)
{
    if (_uniT303Timeouts++ == 0)
    {
        startPROCEEDINGtimer();
        pfMessenger *primitive = getSavedMessage();
        // need to check it is SETUP??
        toA(primitive);
    }
    else
    {
        sendRELEASEconfToCC(sigCauseValue_NoUserResponding);
        finalClearing();
    }
    return;
}

// T310
void uniProtocol :: sigAwaitALERTINGtimeoutAct(void)
{
    if (isNetworkMode() == true)
    {
        startReleaseProcess(sigCauseValue_NoUserResponding);
    }
    else
    {
        startReleaseProcess(sigCauseValue_RecoveryOnTimerExpiry);
    }
    return;
}

// T301
void uniProtocol :: sigAwaitCONNECTtimeoutAct(void)
{
    startReleaseProcess(sigCauseValue_NoAnswerFromUserUserAlerted);
    return;
}

// T313
void uniProtocol :: sigAwaitCONNECT_ACKtimeoutAct(void)
{
    startReleaseProcess(sigCauseValue_RecoveryOnTimerExpiry);
    return;
}

// T308
void uniProtocol :: sigAwaitRELEASE_COMPLETEtimeoutAct(void)
{
    if (_uniT308Timeouts == 0)
    {
        //++TODO++ double cause
        pfUlong causeR = getInteger(uniCauseRStr);
        sendRELEASEreqToDown(causeR, sigCauseValue_RecoveryOnTimerExpiry);
        startRELEASE_COMPLETEtimer();
        _uniT308Timeouts++;
    }
    else
    {
        clearCall(sigCauseValue_NormalUnspecified);
    }
    return;
}


void uniProtocol :: incomingResourcesRejectedAct(sigRELEASEresp *primitive_)
{
    sendRELEASErespToDown(primitive_);
    finalClearing();
    return;
}

void uniProtocol :: releaseCollisionAct(sigRELEASEind *primitive_)
{
    stopRELEASE_COMPLETEtimer();
    if (isMultipointConnection() == true && isNetworkMode() == true)
    {
        clearPMPs(primitive_);
    }
    pfIE *ieRef = primitive_->getIE(sigCauseStr);
    ieCause *ie = dynamic_cast<ieCause *>(ieRef);
    THROW_IF_DYNAMIC_CAST_FAILED(ie);
    sendRELEASEconfToCC(ie->getCauseValue());
    changeToNullState();
    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: clearCallIfNullState
//
// Description:
//      Clears the call if call state null 
//

void uniProtocol :: clearCallIfNullState(uniSTATUSpdu *message_)
{
    pfUlong cs =
        message_->getStorage(uniCallStateStr).getInteger(uniCallState_StateStr);
    if (cs == ieCallState::callState_Null)
    {
        clearCall(sigCauseValue_MessageNotCompatibleWithCallState);
    }
    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)
    {
        sigProtocol::finalClearing();
    }
    return;
}


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

bool uniProtocol :: verifyMessage(
    pfMessenger *msg_, 
    uniStateValue stateAfter_)
{
    pfBoolean result = 1;
    /*
    uniErrorInfo *errorInfo = pdu_->getErrorInfo();
    if (errorInfo != 0)
    {
    // Non-fatal error occured. Report status.
        sendSTATUSpduToCoOrd(*errorInfo, stateAfter_);
    }
    */
    return result;
}

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

//new method
void uniProtocol :: reportIncompatibleMessage(pfMessenger *message_)
{
    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,
                     message_->getInteger(uniMessageTypeStr));
    
    status->getStorage(uniCallStateStr).setInteger(
        uniCallState_StateStr,
        getInteger(uniCallStateValueStr));
    
    status->setCallReference(this);
    toA(status);
    return;
}


//
// Method: setCallReference
// 
// Description:
//     Sets a Call Reference variable getting the value from
//     the key given to the protocol_ instance.
//

void uniProtocol :: setCallReference(pfMessenger *message_)
{
    pfKey key = getKey();
    // The flag is the MSb of the key
    pfUlong flag = key >> uniCallReferenceFlagBitPosition;
    // Reference does not contain the flag -> filter it out
    pfUlong reference = key & (~uniCallReferenceFlagBitMask); 
    // Originating end sets flag to zero, but terminating conduit
    // have an id with flag set to one. We are to take a complement.
    pfUlong muxReference = key ^ uniCallReferenceFlagBitMask;
    message_->setInteger(uniCallReferenceFlagStr, flag);
    message_->setInteger(uniCallReferenceStr, reference);
    message_->setInteger(uniMuxReferenceStr, muxReference);
    return;
}


//
// Method: setXSideEndpointReference
//
// Description:
//      Sets endpoint reference value to messages going down.
//

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

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

//
// Method: setZeroEndpointReference
//
// Description:
//      Sets zero endpoint reference to the primitive. This is usually 
//      used when primitive is going to CC.
//

void uniProtocol :: setZeroEndpointReference(pfMessenger *message_)
{
    _mode->setZeroEndpointReference(message_, this);
    return;    
}


//
// Method: initTimers
//
// Description:
//      Initializes all timers to their default values. Most of the timers
//      used are from sigTimeouts.
//

void uniProtocol :: initTimers(void)
{
    defineTimer(
        uniT301Str, sigAwaitCONNECTtimeout::create(), uniT301expire);
    defineTimer(
        uniT303Str, sigAwaitPROCEEDINGtimeout::create(), uniT303expire);
    defineTimer(
        uniT308Str, sigAwaitRELEASE_COMPLETEtimeout::create(), uniT308expire);
    defineTimer(
        uniT310Str, sigAwaitALERTINGtimeout::create(), uniT310expire);
    defineTimer(
        uniT313Str, sigAwaitCONNECT_ACKtimeout::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;
}
