//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SIG
//
//File: sigprotocol.h
//
//Version: $Revision: 1.3 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/11/11 13:28:46 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Sami Raatikainen
//
//Description:
//      SIG protocol conduit implementation.
//
//Copyright:
//
//
//Licence:
//
//
//History: 

#include "sigprotocol.h"

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

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

#include "protocol/cc/ccprotocol.h"
#include "protocol/uni/unistrings.h"
#include "sigstrings.h"


sigProtocol :: sigProtocol(void)
    : pfProtocol(),
      _mode(0),
      _multipoint(false),
      _location(sigLocation_User),
      _savedMsg(0)
//      _savedMsgType(0)
{
    _mode = sigMode::instance();
    return;
}

sigProtocol :: sigProtocol(const sigProtocol &other_)
    : pfProtocol(other_),
      _mode(other_._mode),
      _multipoint(other_._multipoint),
      _location(other_._location),
      _savedMsg(other_._savedMsg)
//      _savedMsgType(other_._savedMsgType)
{
    return;
}

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


void sigProtocol :: createConduits(sigSETUPind *)
{
    createPointToPointConduits();
    return;
}

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

pfConduit sigProtocol :: createPointToPointConduits(void)
{
    pfConduit cc(ccProtocol::create());
    pfConduit proxy(this);
    cc.connectToA(proxy);
    proxy.connectToB(cc);
    
    // Copy ID (LINK) number
    cc.setId(getId());
    return cc;
}

//
// Method: createMultipointConduits
//
// Description:
//       At simpliest a signalling protocol doesn't support multipoint.
//

pfConduit sigProtocol :: createMultipointConduits(void)
{
    debugUser("This implementation does not support multipoint");
    throw pfException(PF_EX_INFO);
    pfConduit conduit;
    return conduit;
}


//
// Method: setMode
//
// Description:
//      Signalling protocols usually operate in two modes, user and network, 
//      or defined as outgoing and incoming modes. Selection must be
//      performed with this method.
//

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


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

void sigProtocol :: sendSETUPindToCC(sigSETUPind *primitive_)
{
    toB(primitive_);
    return;
}

void sigProtocol :: sendSETUPconfToCC(sigSETUPconf *primitive_)
{
    toB(primitive_);
    return;
}

void sigProtocol :: sendSETUP_COMPLETEindToCC(sigSETUP_COMPLETEind *primitive_)
{
    toB(primitive_);
    return;
}

void sigProtocol :: sendPROCEEDINGindToCC(sigPROCEEDINGind *primitive_)
{
    toB(primitive_);
    return;
}

void sigProtocol :: sendALERTINGindToCC(sigALERTINGind *)
{
    //++TODO++
    return;
}

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

void sigProtocol :: sendRELEASEconfToCC(sigRELEASEconf *primitive_)
{
    toCC(primitive_);
    return;
}

void sigProtocol :: sendRELEASEconfToCC(sigRELEASEind *primitive_)
{
    sigRELEASEconf *primitive = new sigRELEASEconf;
    primitive->copyIfAvailable(*primitive_, uniCauseStr);
    // delete primitive_ ???
    toCC(primitive);
    return;
}

void sigProtocol :: sendRELEASEindToCC(sigRELEASEind *primitive_)
{
    toCC(primitive_);
    return;
}

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

void sigProtocol :: sendRESET_ERRORindToCC(sigRESET_ERRORind *primitive_)
{
    toCC(primitive_);
    return;
}

void sigProtocol :: sendRESETconfToCC(sigRESETconf *primitive_)
{
    toCC(primitive_);
    return;
}

void sigProtocol :: sendRESETindToCC(sigRESETind *primitive_)
{
    toCC(primitive_);
    return;
}

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

void sigProtocol :: sendRESETreqToDown(sigRESETreq *primitive_)
{
    toA(primitive_);
    return;
}

void sigProtocol :: sendRESETrespToDown(sigRESETresp *primitive_)
{
    toA(primitive_);
    return;
}

void sigProtocol :: sendRESET_ERRORrespToDown(sigRESET_ERRORresp *primitive_)
{
    toA(primitive_);
    return;
}

void sigProtocol :: sendSETUPreqToDown(sigSETUPreq *primitive_)
{
    if (isMultipointConnection() != 0)
    {
        setOSideEndpointReference(primitive_);
    }
    setCallReference(primitive_);
    saveMessage(primitive_);
    toA(primitive_);
    return;
}

void sigProtocol :: sendSETUPrespToDown(sigSETUPresp *primitive_)
{
    if (isMultipointConnection() != 0)
    {
        setTSideEndpointReference(primitive_);
    }
    setCallReference(primitive_);
    toA(primitive_);
    return;
}

void sigProtocol :: sendSETUP_COMPLETEreqToDown(void)
{
    sigSETUP_COMPLETEreq *primitive = new sigSETUP_COMPLETEreq;
    if (isMultipointConnection() != 0)
    {
        setOSideEndpointReference(primitive);
    }
    setCallReference(primitive);
    toA(primitive);
    return;
}

void sigProtocol :: sendPROCEEDINGreqToDown(sigPROCEEDINGreq *primitive_)
{
    if (isMultipointConnection() != 0)
    {
        setTSideEndpointReference(primitive_);
    }
    setCallReference(primitive_);
    toA(primitive_);
    return;
}

void sigProtocol :: sendALERTINGreqToDown(sigALERTINGreq *primitive_)
{
    // should primitive be empty (only callReference)?
    setCallReference(primitive_);
    toA(primitive_);
    return;
}


void sigProtocol :: sendRELEASEreqToDown(sigRELEASEreq *primitive_)
{
    setCallReference(primitive_);
    saveMessage(primitive_);
    toA(primitive_);
    return;
}

void sigProtocol :: sendRELEASErespToDown(sigRELEASEresp *primitive_)
{
    setCallReference(primitive_);
    toA(primitive_);
    return;
}


//
// Methods: changeTo*State
//
// Description:
//      Makes a state change using sigMode which is protocol dependent.
//

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

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

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

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

void sigProtocol :: changeToCallPresentState(void)
{
    //setInteger(uniCallStateValueStr, uniState_CallPresent);
    changeState(_mode->createCallPresentState());
    return;
}
void sigProtocol :: changeToCallReceivedState(void)
{
    //setInteger(uniCallStateValueStr, uniState_CallReceived);
    changeState(_mode->createCallReceivedState());
    return;
}

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

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

void sigProtocol :: changeToActiveState(void)
{
    //setInteger(uniCallStateValueStr, uniState_Active);
    if (isMultipointConnection() == false)
    {
        changeState(_mode->createActiveState());
    }
    else
    {
        //changeState(_mode->createActiveMultipointState());
    }
    return;
}

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

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

void sigProtocol :: changeToReleaseCollisionState(void)
{
    //setInteger(uniCallStateValueStr, uniState_ReleaseCollision);
    changeState(_mode->createReleaseCollisionState());
    
    return;
}


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

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

// ++TODO++ what are we waiting for?!!
void sigProtocol :: clearCall(pfUlong cause_)
{
    stopAllTimers();
    debugPfUlong("Clearing call with cause ", cause_);
    sendRELEASEconfToCC(cause_);
    changeToNullState();
    // Wait for response from cc
    return;
}


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

void sigProtocol :: startReleaseProcess(pfUlong cause_)
{
    /*
    debugPfUlong("Starting release process with cause ", cause_);
    stopAllTimers();
    sendRELEASEindToCC(cause_);
    sendRELEASEreqToDown(cause_);
    startTimer(uniT308Str);
    _mode->continueReleaseProcess(this);
    */
    return;
}


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

// TODO uniAllowFinalClearingStr, is it common for other signalling protcols
void sigProtocol :: finalClearing(void)
{
    // Close all
    debugUser("Removing CC / SIGNALLING");
    pfKey key = getKey();
    pfUnInstallTransporter uninstaller =
        pfUnInstallTransporter::createUnInstallTransporter(key);
    toA(&uninstaller);
    return;
}


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

void sigProtocol :: setOSideEndpointReference(pfMessenger *)
{
    debugUser("This implementation does not support multipoint");
    throw pfException(PF_EX_INFO);
    return;
}

void sigProtocol :: setTSideEndpointReference(pfMessenger *)
{
    debugUser("This implementation does not support multipoint");
    throw pfException(PF_EX_INFO);
    return;
}


//
// Methods: setMultipointConnection
//
// Description:
//      Methods to set the multipoint connection flag, and to return it.
//

void sigProtocol :: setMultipointConnection(void)
{
    _multipoint = true;
    return;
}

bool sigProtocol :: isMultipointConnection(void)
{
    return _multipoint;
}


//
// Methods: setLocation / getLocation
//
// Description:
//      Methods to set the location (2.2.3./Q.850), and to return it.
//

void sigProtocol :: setLocation(sigLocation location_)
{
    _location = location_;
    return;
}

sigProtocol::sigLocation sigProtocol :: getLocation(void)
{
    return _location;
}



//
// Methods: saveMessage / sendSavedMessage
//
// Description:
//       Enables possible resending of messages.
//

void sigProtocol :: saveMessage(pfMessenger *msg_)
{
    if (_savedMsg != 0)
    {
        _savedMsg->incRefCount();
        if (_savedMsg->isReference() == false)
        {
            delete _savedMsg;
        }
    }
    _savedMsg = msg_;
    return;
}

pfMessenger *sigProtocol :: getSavedMessage(void)
{
    // ++TODO++ counter, is this correct ?
    if (_savedMsg != 0)
    {
        _savedMsg->decRefCount();        
    }
    return _savedMsg;
}

           
//
// Method: toCC
//
// Description:
//      Sends a message to cc adding zero endpoint reference if
//      necessary.
//

void sigProtocol :: toCC(pfMessenger *primitive_)
{
    if (_multipoint == true)
    {
        //setZeroEndpointReference(primitive_);
    }
    toB(primitive_);
    return;
}



