//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / UNI
//
//File: unicoordprotocol.cpp
//
//Version: $Revision: 1.25 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/02/02 08:38:29 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Jari Katajavuori
//
//Description:
//
//
//Copyright:
//
//
//Licence:
//
//
//History: 

#include "unicoordprotocol.h"

#include "pf/frame.h"
#include "pf/conduit.h"
#include "pf/mux.h"
#include "pf/debug.h"

#include "iface/uaalif/uaaldownprimitives.h"

#include "unidefs.h"
#include "unistrings.h"
#include "unicoordstate.h"
#include "uniprimitives.h"
#include "unirsnprotocol.h"
#include "unirrnprotocol.h"
#include "unitransporter.h"
#include "uniexceptions.h"
#include "unierrorinfo.h"

pfConduit uniCoOrdProtocol :: create(pfConduit &mux_)
{
    uniCoOrdProtocol *coord = new uniCoOrdProtocol;

    // Get a pointer to the mux above co-ord.
    uniMuxCatcherTransporter muxCatcher =
        uniMuxCatcherTransporter::create();

    mux_.accept(&muxCatcher);
    coord->_mux = muxCatcher.catchedMux();

    pfConduit newConduit(coord);
    return newConduit;
}

uniCoOrdProtocol :: uniCoOrdProtocol(void)
    : pfProtocol(),
      pfStorage(),
      _mux(0),
      _RSN(),
      _RRN(),
      _queue()
{
    defineTimer(uniT309Str, uniT309timeout::create(), uniT309expire);

    _RSN = uniRSNProtocol::create();
    _RRN = uniRRNProtocol::create();
    
    pfConduit proxy(this);
    _RSN.connectToA(proxy);
    _RRN.connectToA(proxy);

    changeToAALConnectionReleasedState();
    return;
}

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

//
// Method: disconnect
//
// Description:
//      CoOrd contains conduit-object references to RSN and RRN, which
//      must be eliminated explicitly.
//

void uniCoOrdProtocol :: disconnect(void)
{
    _RSN.disconnect();
    _RRN.disconnect();
    pfProtocol::disconnect();
    return;
}

//
// Method: verifyTrafficParameterCombinations
//
// Description:
//      Verifies parameter combinations in the message.
//      Allowable combinations are tabled in Table F-1 of 
//      UNI 3.1 Specification.
//

bool uniCoOrdProtocol :: verifyTrafficParameterCombinations(
    uniSETUPpdu *message_)
{
    bool result = 0;
    if ((message_->isVariableDefined(uniBroadbandBearerCapabilityStr) != 0) &&
        (message_->isVariableDefined(uniATDStr) != 0) &&
        (message_->isVariableDefined(uniQoSParametersStr) != 0))
    {
        // Get information elements
        pfStorage &bearer = message_->getStorage(uniBroadbandBearerCapabilityStr);
        pfStorage &atd = message_->getStorage(uniATDStr);
        pfStorage &qos = message_->getStorage(uniQoSParametersStr);

        // Get bearer class and bitrate parameters
        pfUlong bclass = bearer.getInteger(uniBBC_BearerClassStr);
        pfUlong bitrate = bearer.getInteger(uniBBC_TrafficTypeStr);
 
        // Get combination patterns
        pfUlong fcombination = 
            atd.getInteger(uniATD_ForwardCombinationStr);
        pfUlong bcombination = 
            atd.getInteger(uniATD_BackwardCombinationStr);
        pfUlong fqosclass = qos.getInteger(uniQoS_ForwardStr);
        pfUlong bqosclass = qos.getInteger(uniQoS_BackwardStr);

        // Mask tagging out from these patterns
        pfUlong ftagcomb = fcombination & B0111_1111;
        pfUlong btagcomb = bcombination & B0111_1111;

        // Check if combination is allowed
        switch (bclass)
        {
            case uniPdu::BCOB_A : 
                if (((ftagcomb == uniPdu::TD_PCR_CLP0) ||
                     (fcombination == uniPdu::TD_PCR_CLP1)) &&
                    ((ftagcomb == uniPdu::TD_PCR_CLP0) ||
                     (bcombination == uniPdu::TD_PCR_CLP1)))
                {
                    result = 1;
                }
                break;

            case uniPdu::BCOB_C : 
            case uniPdu::BCOB_X : 
                if ((((ftagcomb == uniPdu::TD_PCR_CLP0) ||
                      (fcombination == uniPdu::TD_PCR_CLP1) ||
                      ((bitrate != uniPdu::CBR) && 
                       ((ftagcomb == uniPdu::TD_SCR_CLP0) ||
                        (fcombination == uniPdu::TD_SCR_CLP1)))) && 
                     ((btagcomb == uniPdu::TD_PCR_CLP0) ||
                      (bcombination == uniPdu::TD_PCR_CLP1) ||
                      ((bitrate != uniPdu::CBR) && 
                       ((btagcomb == uniPdu::TD_SCR_CLP0) ||
                        (bcombination == uniPdu::TD_SCR_CLP1))))) ||
                    ((bitrate != uniPdu::CBR) &&
                     (bcombination == uniPdu::TD_BE) &&
                     (bqosclass == 0) &&
                     (fcombination == uniPdu::TD_BE) &&
                     (fqosclass == 0)))
                {
                    result = 1;
                }
                break;
        }        
    }
    return result;
}

// To SAAL

void uniCoOrdProtocol :: sendESTABLISHreqToAAL(void)
{
    uaalESTABLISHreq *message = new uaalESTABLISHreq;
    toA(message);
    return;
}

void uniCoOrdProtocol :: sendRELEASEreqToAAL(void)
{
    uaalRELEASEreq *message = new uaalRELEASEreq;
    toA(message);
    return;
}

void uniCoOrdProtocol :: sendDATAreqToAAL(pfFrame &frame_)
{
    uaalDATAreq *message = new uaalDATAreq;
    message->setMessageUnit(frame_);
    toA(message);
    return;
}

//
// Method: sendDATAreqToAAL
//
// Description:
//     Sends a DATAreq message to AA Layer. Does NOT destroy
//     the given pdu.
//

void uniCoOrdProtocol :: sendDATAreqToAAL(uniPdu *pdu_)
{
    try
    {
        pfFrame frame = pdu_->encode();
        sendDATAreqToAAL(frame);
    }
    catch (uniEncodeFailedException &e)
    {
        e.printInfo();
    }

    return;
}

void uniCoOrdProtocol :: sendStatus(uniPdu *pdu_, 
                                    uniErrorInfo &errorInfo_)
{
    pfUlong muxReference = pdu_->getInteger(uniMuxReferenceStr);
    if (isMuxKeyAllocated(muxReference) == 0)
    {
        // no UNI instance attached to mux reference
        sendSTATUSreplyToAAL(pdu_, errorInfo_);
    }
    else
    {
        // there is a UNI instance, query it
        sendSTATUSrequestToUNI(pdu_, errorInfo_);
    }
    return;
}

void uniCoOrdProtocol :: sendSTATUSrequestToUNI(uniPdu *message_, 
                                                uniErrorInfo &errorInfo_)
{
    uniSTATUS_ENQUIRYpdu *message = uniSTATUS_ENQUIRYpdu::create();
    message->copyVariable(*message_, uniMuxReferenceStr);
    message->defineStorage(uniCauseStr);
    message->setStorage(uniCauseStr, errorInfo_);
    toB(message);
}

void uniCoOrdProtocol :: sendSTATUSreplyToAAL(uniErrorInfo &errorInfo_)
{
    uniSTATUSpdu *message = uniSTATUSpdu::create();
    message->setInteger(uniCallReferenceFlagStr, 0);
    message->setInteger(uniCallReferenceStr, uniGlobalCallReference);
    pfStorage &cs = message->defineCallState();
    cs.setInteger(uniCallState_StateStr, 0);
    message->defineStorage(uniCauseStr);
    message->setStorage(uniCauseStr, errorInfo_);
    sendDATAreqToAAL(message);
    return;
}

void uniCoOrdProtocol :: sendSTATUSreplyToAAL(uniPdu *pdu_,
                                              pfUlong cause_)
{
    uniErrorInfo errorInfo(cause_);
    sendSTATUSreplyToAAL(pdu_, errorInfo);
    return;
}

void uniCoOrdProtocol :: sendSTATUSreplyToAAL(uniPdu *pdu_,
                                              uniErrorInfo &errorInfo_)
{
    debugUser("sendSTATUS reached");
    uniSTATUSpdu *message =
        uniSTATUSpdu::buildReply(pdu_, errorInfo_);
    sendDATAreqToAAL(message);
    debugUser("deleting..");
    delete message;
    debugUser("deleted.");
    return;
}

void uniCoOrdProtocol :: sendRELEASE_COMPLETEreplyToAAL(
    uniPdu *pdu_, pfUlong cause_)
{
    uniErrorInfo errorInfo(cause_);
    sendRELEASE_COMPLETEreplyToAAL(pdu_, errorInfo);
    return;
}

void uniCoOrdProtocol :: sendRELEASE_COMPLETEreplyToAAL(
    uniPdu *pdu_, uniErrorInfo &errorInfo_)
{
    uniRELEASE_COMPLETEpdu *message =
        uniRELEASE_COMPLETEpdu::buildReply(pdu_, errorInfo_);
    sendDATAreqToAAL(message);
    delete message;
    return;
}

// To UNI

void uniCoOrdProtocol :: sendRESET_ERRORindToUNI(uniRESET_ERRORind *primitive_)
{
    toB(primitive_);
    return;
}

void uniCoOrdProtocol :: sendRESETconfToUNI(uniRESETconf *primitive_)
{
    toB(primitive_);
    return;
}

void uniCoOrdProtocol :: sendRESETindToUNI(uniRESETind *primitive_)
{
    toB(primitive_);
    return;
}

void uniCoOrdProtocol :: sendLINK_ESTABLISHconfToUNI(void)
{
    uniLINK_ESTABLISHconf *message = uniLINK_ESTABLISHconf::create();
    toAllUNIs(message);
    return;
}

void uniCoOrdProtocol :: sendLINK_ESTABLISHindToUNI(void)
{
    uniLINK_ESTABLISHind *message = uniLINK_ESTABLISHind::create();
    toAllUNIs(message);
    return;
}

void uniCoOrdProtocol :: sendLINK_ESTABLISHerrorToUNI(void)
{
    uniLINK_ESTABLISHerror *message = uniLINK_ESTABLISHerror::create();
    toAllUNIs(message);
    return;
}

void uniCoOrdProtocol :: sendLINK_RELEASEconfToUNI(void)
{
    uniLINK_RELEASEconf *message = uniLINK_RELEASEconf::create();
    toAllUNIs(message);
    return;
}

void uniCoOrdProtocol :: sendLINK_RELEASEindToUNI(void)
{
    uniLINK_RELEASEind *message = uniLINK_RELEASEind::create();
    toAllUNIs(message);
    return;
}

void uniCoOrdProtocol :: sendRELEASEpduToUNI(uniPdu *pdu_, pfUlong cause_)
{
    uniRELEASEpdu *message = uniRELEASEpdu::create();
    message->copyVariable(*pdu_, uniMuxReferenceStr);
    message->defineCause();
    message->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause_);
    toB(message);
    return;
}

void uniCoOrdProtocol :: sendRELEASE_COMPLETEpduToUNI(uniPdu *pdu_,
                                                      pfUlong cause_)
{
    uniRELEASE_COMPLETEpdu *message = uniRELEASE_COMPLETEpdu::create();
    message->copyVariable(*pdu_, uniMuxReferenceStr);
    message->defineCause();
    message->getStorage(uniCauseStr).setInteger(uniCause_ValueStr, cause_);
    toB(message);
    return;
}

void uniCoOrdProtocol :: sendRESETreqToRSN(uniRESETreq *primitive_)
{
    uniRESETreq *primitive = uniRESETreq::create();
    primitive->fetch(*primitive_);
    toRSN(primitive);
    return;
}

void uniCoOrdProtocol :: sendRESETrespToRRN(uniRESETresp *primitive_)
{
    uniRESETresp *primitive = uniRESETresp::create();
    primitive->fetch(*primitive_);
    toRRN(primitive);
    return;
}

void uniCoOrdProtocol :: sendRESET_ERRORrespToRRN(uniRESET_ERRORresp *primitive_)
{
    uniRESET_ERRORresp *primitive = uniRESET_ERRORresp::create();
    primitive->fetch(*primitive_);
    toRRN(primitive);
    return;
}

//
//Functions: changeToXState
//
//Description:
//    Changes protocol state to X
//

void uniCoOrdProtocol :: changeToAALConnectionReleasedState(void)
{
    changeState(uniCoOrdAALConnectionReleased::instance());
    return;
}

void uniCoOrdProtocol :: changeToAALAwaitingEstablishState(void)
{
    changeState(uniCoOrdAALAwaitingEstablish::instance());
    return;
}

void uniCoOrdProtocol :: changeToAALAwaitingReleaseState(void)
{
    changeState(uniCoOrdAALAwaitingRelease::instance());
    return;
}

void uniCoOrdProtocol :: changeToAALConnectionEstablishedState(void)
{
    changeState(uniCoOrdAALConnectionEstablished::instance());
    return;
}

//
//Function: saveMessage
//
//Description:
//    Save messengers to buffer (STL queue)
//

void uniCoOrdProtocol :: saveMessage(pfMessenger *messenger_)
{
    messenger_->incRefCount();
    _queue.push(messenger_);
    return;
}

//
//Function: acceptAllSavedMessages
//
//Description:
//    Accept all messengers inside message transporters from buffer.
//

void uniCoOrdProtocol :: acceptSavedMessages(void)
{
    while (_queue.empty() == 0)
    {
        pfMessenger *message = _queue.front();
        message->decRefCount();
        pfMsgTransporter *transporter =
            pfMsgTransporter::createMsgTransporter(message);
        accept(transporter);
        _queue.pop();
    }
    return;    
}

//
//Function: removeMessages
//
//Description:
//    Remove all messengers from buffer (STL queue). 
//

void uniCoOrdProtocol :: removeSavedMessages(void)
{
    while (_queue.empty() == 0)
    {
        pfMessenger *message = _queue.front();
        message->decRefCount();
        if(message->isReference() == false)
        {
            delete message;
        }
        _queue.pop();
    }
    return;    
}

//
//Function: isMuxKeyAllocated
//
//Description:
//
//

bool uniCoOrdProtocol :: isMuxKeyAllocated(const pfKey key_)
{
    bool result = _mux->isKeyInstalled(key_);
    return result;
}

//
//Function: toRSN/RRN
//
//Description:
//

void uniCoOrdProtocol :: toRSN(pfMessenger *message_)
{
    toExternal(_RSN, message_);
    return;
}

void uniCoOrdProtocol :: toRSN(pfMsgTransporter *message_)
{
    toExternal(_RSN, message_);
    return;
}

void uniCoOrdProtocol :: toRRN(pfMessenger *message_)
{
    toExternal(_RRN, message_);
    return;
}

void uniCoOrdProtocol :: toRRN(pfMsgTransporter *message_)
{
    toExternal(_RRN, message_);
    return;
}

void uniCoOrdProtocol :: toAllUNIs(pfMessenger *message_)
{
    pfMsgTransporter *transporter =
        pfMsgTransporter::createMsgTransporter(message_);
    toAllUNIs(transporter);
    return;
}

void uniCoOrdProtocol :: toAllUNIs(pfMsgTransporter *transporter_)
{
    if (_mux == 0)
    {
        delete transporter_;
        throw pfNullPointerException(PF_EX_INFO);
    }
    _mux->toAllB(transporter_);
    return;
}

void uniCoOrdProtocol :: toExternal(pfConduit &conduit_,
                                    pfMessenger *message_)
{
    pfMsgTransporter *transporter =
        pfMsgTransporter::createMsgTransporter(message_);
    toExternal(conduit_, transporter);
    return;
}

void uniCoOrdProtocol :: toExternal(pfConduit &conduit_,
                                    pfMsgTransporter *transporter_)
{
    pfConduit proxy(this);
    transporter_->setSender(proxy);
    //sendingTrace(transporter_);
    conduit_.accept(transporter_);
    return;
}

