//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / NNI SSCF protocol
//
//File: nsscfprotocol.cpp
//
//Version: $Revision: 1.19 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/10/20 13:06:14 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications and Multimedia
//
//Author:
//      Juhana Räsänen
//
//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 "nsscfprotocol.h"
#include "nsscftimeouts.h"

#include "nsscfstate1_1_1.h"
#include "nsscfstate1_4_1.h"
#include "nsscfstate2_10_3.h"
#include "nsscfstate2_10_4.h"
#include "nsscfstate2_1_2.h"
#include "nsscfstate2_2_2.h"
#include "nsscfstate2_4_2.h"
#include "nsscfstate3_10_5.h"

#include "protocol/saal/saalnnilink.h"
#include "iface/naalif/naalupprimitives.h"
#include "iface/aaif/aadownprimitives.h"
#include "pf/frame.h"
#include "pf/timer.h"


nsscfProtocol :: nsscfProtocol(saalNNIlink *manager_)
    : pfProtocol(),
      _timerT1(),
      _timerT2(),
      _timerT3(),
      _INS(false),
      _LPO(false),
      _UPS(NSSCF_PROVING_NORMAL),
      _MPS(NSSCF_PROVING_NEUTRAL),
      _n1(NSSCF_PROVING_PDUS),
      _C1(0),
      _BSNT(NSSCF_NO_BSNT_AVAILABLE),
      _manager(manager_)
{
    pfTimerMessenger *messenger = 0;
    
    _timerT1.setHost(this);
    setT1timeout(NSSCF_T1_DEFAULT_TIMEOUT);
    messenger = new nsscfT1timeout;
    assert(messenger != 0);
    _timerT1.setMessenger(messenger);

    _timerT2.setHost(this);
    setT2timeout(NSSCF_T2_DEFAULT_TIMEOUT);
    messenger = new nsscfT2timeout;
    assert(messenger != 0);
    _timerT2.setMessenger(messenger);

    _timerT3.setHost(this);
    setT3timeout(NSSCF_T3_DEFAULT_TIMEOUT);
    messenger = new nsscfT3timeout;
    assert(messenger != 0);
    _timerT3.setMessenger(messenger);

    changeState(nsscfOutOfService_Idle::instance());
    return;
}


nsscfProtocol :: nsscfProtocol(const nsscfProtocol &other_, saalNNIlink *manager_)
    : pfProtocol(other_),
      _timerT1(),
      _timerT2(),
      _timerT3(),
      _INS(other_._INS),
      _LPO(other_._LPO),
      _UPS(other_._UPS),
      _MPS(other_._MPS),
      _n1(other_._n1),
      _C1(other_._C1),
      _BSNT(other_._BSNT),
      _manager(manager_)
{
    pfTimerMessenger *messenger = 0;
    
    _timerT1.setHost(this);
    setT1timeout(NSSCF_T1_DEFAULT_TIMEOUT);
    messenger = new nsscfT1timeout;
    assert(messenger != 0);
    _timerT1.setMessenger(messenger);

    _timerT2.setHost(this);
    setT2timeout(NSSCF_T2_DEFAULT_TIMEOUT);
    messenger = new nsscfT2timeout;
    assert(messenger != 0);
    _timerT2.setMessenger(messenger);

    _timerT3.setHost(this);
    setT3timeout(NSSCF_T3_DEFAULT_TIMEOUT);
    messenger = new nsscfT3timeout;
    assert(messenger != 0);
    _timerT3.setMessenger(messenger);

    return;
}


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

//
// nsscfProtocol :: isInService()
//                  setInService()
//                  setNotInService()
//
// Get/set methods for "IN Service" local variable
//

bool nsscfProtocol :: isInService(void) const
{
    return _INS;
}


void nsscfProtocol :: setInService(void)
{
    _INS = true;
    return;
}


void nsscfProtocol :: setNotInService(void)
{
    _INS = false;
    return;
}


//
// nsscfProtocol :: isLPO()
//                  setIsLPO()
//                  setNoLPO()
//
// Get/set methods for "Local Processor Outage" local variable
//

bool nsscfProtocol :: isLPO(void) const
{
    return _LPO;
}


void nsscfProtocol :: setIsLPO(void)
{
    _LPO = 1;
    return;
}


void nsscfProtocol :: setNoLPO(void)
{
    _LPO = 0;
    return;
}


//
// nsscfProtocol :: getUPS()
//                  setUPS()
//
// Get/set methods for "User Proving Status" local variable
//

nsscfProvingStatus nsscfProtocol :: getUPS(void) const
{
    return _UPS;
}


void nsscfProtocol :: setUPSnormal(void)
{
    _UPS = NSSCF_PROVING_NORMAL;
    return;
}


void nsscfProtocol :: setUPSemergency(void)
{
    _UPS = NSSCF_PROVING_EMERGENCY;
    return;
}


//
// nsscfProtocol :: getMPS()
//                  setMPS()
//
// Get/set methods for "Management Proving Status" local variable
//

nsscfProvingStatus nsscfProtocol :: getMPS(void) const
{
    return _MPS;
}


void nsscfProtocol :: setMPSneutral(void)
{
    _MPS = NSSCF_PROVING_NEUTRAL;
    return;
}


void nsscfProtocol :: setMPSnormal(void)
{
    _MPS = NSSCF_PROVING_NORMAL;
    return;
}


void nsscfProtocol :: setMPSemergency(void)
{
    _MPS = NSSCF_PROVING_EMERGENCY;
    return;
}


//
// nsscfProtocol :: getN1()
//                  setN1()
//
// Get/set methods for "n1" local variable
//

pfUlong nsscfProtocol :: getN1(void) const
{
    return _n1;
}


void nsscfProtocol :: setN1(pfUlong n1_)
{
    _n1 = n1_;
    return;
}


//
// nsscfProtocol :: getC1()
//                  setC1()
//                  decreaseC1()
//
// Get/set methods for "C1" local variable (count of proving PDUs)
// and a method to decrease the count by 1
//

pfUlong nsscfProtocol :: getC1(void) const
{
    return _C1;
}


void nsscfProtocol :: setC1(nsscfPDU pdu_)
{
// Following ugly condition implements the decision table for the
// number of proving PDUs as defined in Q.2140 page 31. Result is N1
// PDUs if MPS is NORMAL or if MPS is NEUTRAL and UPS and received pdu
// Proving Status are both NORMAL, otherwise no proving PDUs will be sent

    if ((_MPS == NSSCF_PROVING_NORMAL) ||
            ((_MPS == NSSCF_PROVING_NEUTRAL) &&
             (_UPS == NSSCF_PROVING_NORMAL) &&
             (pdu_ == nsscfNMpdu)))
    {
        _C1 = _n1;
    }
    else
    {
        _C1 = 0;
    }
    return;
}


void nsscfProtocol :: decreaseC1(void)
{
    _C1--;
    return;
}


//
// nsscfProtocol :: getBSNT()
//                  setBSNT()
//
// Get/set methods for "BSNT" local variable
//

pfUlong nsscfProtocol :: getBSNT(void) const
{
    return _BSNT;
}


void nsscfProtocol :: setBSNT(pfUlong bsnt_)
{
    _BSNT = bsnt_;
    return;
}


//
// nsscfProtocol :: timerT1Start()
//                  timerT1Stop()
//                  timerT2Start()
//                  timerT2Stop()
//                  timerT3Start()
//                  timerT3Stop()
//
// Methods to start and stop NSSCF timers
//

void nsscfProtocol :: setT1timeout(pfUlong msec_)
{
    _timerT1.setTimeout(msec_);
    return;
}


void nsscfProtocol :: timerT1start(void)
{
    _timerT1.start();
    return;
}


void nsscfProtocol :: timerT1stop(void)
{
    _timerT1.stop();
    return;
}


void nsscfProtocol :: setT2timeout(pfUlong msec_)
{
    _timerT2.setTimeout(msec_);
    return;
}


void nsscfProtocol :: timerT2start(void)
{
    _timerT2.start();
    return;
}


void nsscfProtocol :: timerT2stop(void)
{
    _timerT2.stop();
    return;
}


void nsscfProtocol :: setT3timeout(pfUlong msec_)
{
    _timerT3.setTimeout(msec_);
    return;
}


void nsscfProtocol :: timerT3start(void)
{
    _timerT3.start();
    return;
}


void nsscfProtocol :: timerT3stop(void)
{
    _timerT3.stop();
    return;
}


//
// nsscfProtocol :: sendAaESTABLISHreq()
//                  sendAaESTABLISHresp()
//                  sendAaDATAreq()
//                  sendAaRELEASEreq()
//                  sendAaRETRIEVEreq()
//                  sendAaRECOVERresp()
//
// Send methods for AA primitives. Create a new instance of messenger
// to be sent, set its parametes and send away to SSCOP
//

void nsscfProtocol :: sendAaESTABLISHreq(void)
{
    aaESTABLISHreq *messenger = new aaESTABLISHreq;
    assert(messenger != 0);

    // If management proving status is NEUTRAL (ie. don't care), send
    // user proving status in SSCOP_UU parameter, else send MPS
    nsscfProvingStatus currentPS;
    if (_MPS == NSSCF_PROVING_NEUTRAL)
    {
        currentPS = _UPS;
    }
    else
    {
        currentPS = _MPS;
    }
    if (currentPS == NSSCF_PROVING_NORMAL)
    {
        messenger->setSSCOP_UU(encodePDU(nsscfNMpdu));
    }
    else
    {
        messenger->setSSCOP_UU(encodePDU(nsscfEMpdu));
    }

    // Buffer Release parameter is always NO in NNI side
    messenger->setBufferRelease(0);
    toA(messenger);
    return;
}


void nsscfProtocol :: sendAaESTABLISHresp(void)
{
    aaESTABLISHresp *messenger = new aaESTABLISHresp;
    assert(messenger != 0);

    // If management proving status is NEUTRAL (ie. don't care), send
    // user proving status in SSCOP_UU parameter, else send MPS
    nsscfProvingStatus currentPS;
    if (_MPS == NSSCF_PROVING_NEUTRAL)
    {
        currentPS = _UPS;
    }
    else
    {
        currentPS = _MPS;
    }
    if (currentPS == NSSCF_PROVING_NORMAL)
    {
        messenger->setSSCOP_UU(encodePDU(nsscfNMpdu));
    }
    else
    {
        messenger->setSSCOP_UU(encodePDU(nsscfEMpdu));
    }

    // Buffer Release parameter is always NO in NNI side
    messenger->setBufferRelease(0);
    toA(messenger);
    return;
}


void nsscfProtocol :: sendAaDATAreq(const pfFrame &frame_)
{
    aaDATAreq *messenger = new aaDATAreq;
    assert(messenger != 0);
    messenger->setMessageUnit(frame_);
    toA(messenger);
    return;
}


void nsscfProtocol :: sendAaRELEASEreq(nsscfPDU pdu_)
{
    aaRELEASEreq *messenger = new aaRELEASEreq;
    assert(messenger != 0);
    messenger->setSSCOP_UU(encodePDU(pdu_));
    toA(messenger);
    return;
}


void nsscfProtocol :: sendAaRETRIEVEreq(pfUlong retrievalNumber_)
{
    aaRETRIEVEreq *messenger = new aaRETRIEVEreq;
    assert(messenger != 0);
    messenger->setRetrievalNumber(retrievalNumber_);
    toA(messenger);
    return;
}


void nsscfProtocol :: sendAaRECOVERresp(void)
{
    aaRECOVERresp *messenger = new aaRECOVERresp;
    assert(messenger != 0);
    toA(messenger);
    return;
}


//
// nsscfProtocol :: sendNaalRECEIVED_MESSAGEind()
//                  sendNaalLINK_CONGESTEDind()
//                  sendNaalLINK_CONGESTION_CEASEDind()
//                  sendNaalIN_SERVICEind()
//                  sendNaalOUT_OF_SERVICEind()
//                  sendNaalRETRIEVED_MESSAGESind()
//                  sendNaalRETRIEVAL_COMPLETEind()
//                  sendNaalBSNTconf()
//                  sendNaalBSNT_NOT_RETRIEVABLEconf()
//
// Send methods for NAAL primitives. Create a new instance of messenger
// to be sent, set its parametes and send away to MTP-3
//

void nsscfProtocol :: sendNaalRECEIVED_MESSAGEind(const pfFrame &frame_)
{
    naalRECEIVED_MESSAGEind *messenger = new naalRECEIVED_MESSAGEind;
    assert(messenger != 0);
    messenger->setMessageUnit(frame_);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalLINK_CONGESTEDind(pfUlong congestion_)
{
    naalLINK_CONGESTEDind *messenger = new naalLINK_CONGESTEDind;
    assert(messenger != 0);
    messenger->setCongestion(congestion_);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalLINK_CONGESTION_CEASEDind(void)
{
    naalLINK_CONGESTION_CEASEDind *messenger =
        new naalLINK_CONGESTION_CEASEDind;
    assert(messenger != 0);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalIN_SERVICEind(void)
{
    naalIN_SERVICEind *messenger = new naalIN_SERVICEind;
    assert(messenger != 0);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalOUT_OF_SERVICEind(void)
{
    naalOUT_OF_SERVICEind *messenger = new naalOUT_OF_SERVICEind;
    assert(messenger != 0);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalRETRIEVED_MESSAGESind(const pfFrame &frame_)
{
    naalRETRIEVED_MESSAGESind *messenger = new naalRETRIEVED_MESSAGESind;
    assert(messenger != 0);
    messenger->setMessageUnit(frame_);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalRETRIEVAL_COMPLETEind(void)
{
    naalRETRIEVAL_COMPLETEind *messenger = new naalRETRIEVAL_COMPLETEind;
    assert(messenger != 0);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalBSNTconf(pfUlong bsnt_)
{
    naalBSNTconf *messenger = new naalBSNTconf;
    assert(messenger != 0);
    messenger->setBSNT(bsnt_);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


void nsscfProtocol :: sendNaalBSNT_NOT_RETRIEVABLEconf(void)
{
    naalBSNT_NOT_RETRIEVABLEconf *messenger =
        new naalBSNT_NOT_RETRIEVABLEconf;
    assert(messenger != 0);
    messenger->setAALidentifier(getAALid());
    toB(messenger);
    return;
}


//
// nsscfProtocol :: sendMaalPROVINGind()
//                  sendMaalSTOP_PROVINGind()
//                  sendMaalREPORTind()
//
// Sends MAAL primitives to the SAAL layer management object.
//

void nsscfProtocol :: sendMaalPROVINGind(void)
{
    _manager->maalPROVINGindAct();
    return;
}


void nsscfProtocol :: sendMaalSTOP_PROVINGind(void)
{
    _manager->maalSTOP_PROVINGindAct();
    return;
}


void nsscfProtocol :: sendMaalREPORTind(nsscfLBC lbc_,
                                        nsscfUBC ubc_,
                                        nsscfReason reason_,
                                        nsscfPDU sscop_uu_)
{
    _manager->maalREPORTindAct(lbc_, ubc_, reason_, sscop_uu_);
    return;
}


// Methods to change state
void nsscfProtocol :: toNsscfOutOfService_Idle(void)
{
    changeState(nsscfOutOfService_Idle::instance());
    return;
}


void nsscfProtocol :: toNsscfOutOfService_OutgoingDisconnectionPending(void)
{
    changeState(nsscfOutOfService_OutgoingDisconnectionPending::instance());
    return;
}


void nsscfProtocol :: toNsscfProving_DataTransferReady(void)
{
    changeState(nsscfProving_DataTransferReady::instance());
    return;
}


void nsscfProtocol :: toNsscfAlignedReady_DataTransferReady(void)
{
    changeState(nsscfAlignedReady_DataTransferReady::instance());
    return;
}


void nsscfProtocol :: toNsscfAlignment_Idle(void)
{
    changeState(nsscfAlignment_Idle::instance());
    return;
}


void nsscfProtocol :: toNsscfAlignment_OutgoingConnectionPending(void)
{
    changeState(nsscfAlignment_OutgoingConnectionPending::instance());
    return;
}


void nsscfProtocol :: toNsscfAlignment_OutgoingDisconnectionPending(void)
{
    changeState(nsscfAlignment_OutgoingDisconnectionPending::instance());
    return;
}


void nsscfProtocol :: toNsscfInService_DataTransferReady(void)
{
    changeState(nsscfInService_DataTransferReady::instance());
    return;
}


//
// nsscfProtocol :: encodePDU()
//
// Creates a frame containing a 4-octet NNI-SSCF PDU
//

pfFrame nsscfProtocol :: encodePDU(nsscfPDU pdu_) const
{
    pfFrame frame;
    frame.putLast(0);
    frame.putLast(0);
    frame.putLast(0);
    frame.putLast((pfByte) pdu_);
    return frame;
}


//
// nsscfProtocol :: decodePDU()
//
// Returns NNI-SSCF PDU type of a 4-octet frame, nsscfILLpdu if
// frame is not 4 octets long
//

nsscfPDU nsscfProtocol :: decodePDU(const pfFrame &frame_) const
{
    nsscfPDU pdu;
    if (frame_.length() == NSSCF_PDU_LENGTH)
    {
        switch (frame_.read(NSSCF_PDU_OCTET_INDEX))
        {
            case nsscfOOSpdu:
                pdu = nsscfOOSpdu;
                break;
            case nsscfPOpdu:
                pdu = nsscfPOpdu;
                break;
            case nsscfINSpdu:
                pdu = nsscfINSpdu;
                break;
            case nsscfNMpdu:
                pdu = nsscfNMpdu;
                break;
            case nsscfEMpdu:
                pdu = nsscfEMpdu;
                break;
            case nsscfANSpdu:
                pdu = nsscfANSpdu;
                break;
            case nsscfMIpdu:
                pdu = nsscfMIpdu;
                break;
            case nsscfPEpdu:
                pdu = nsscfPEpdu;
                break;
            case nsscfPNSpdu:
                pdu = nsscfPNSpdu;
                break;
            case nsscfINVALIDpdu:
            default:
                pdu = nsscfINVALIDpdu;
                break;
        }
    }
    return pdu;
}

//
// nsscfProtocol :: getAALid()
//
// Returns AALid based on id which identifies the link.
// Id is returned using pfProtocol::getId().
// The implementation of the id can be changed easily.
//

pfId nsscfProtocol :: getAALid(void) const
{
    pfId result = getId();
    return result;
}

