//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project/CC
//
//File: ccdpstate.cpp
//
//Version: $Revision: 1.47 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/16 16:50:57 $
//
//Organisation:
//      University of Technology
// 
//Author:
//      Pasi Nummisalo
//
//Description:
//      See header.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//      
//Licence:
//     
//
//History:
//

#include "ccostate_null.h"
#include "cctstate_null.h"
#include "ccprotocol.h"
#include "ccdpstate.h"
#include "trigger_digitstring.h"
#include "pf/error.h"
#include "cctstate_exception.h"
#include "ccodp_abandon.h"

ccDpState :: ccDpState(void)
    : pfState()
{
    return;
}

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

//
//Function: exitAction
//
//Description:
//        Exit action for DP -> PIC
//

void ccDpState :: exitAction(ccProtocol *protocol_, ccState *state_)
{
    if (protocol_->isTimerActive(ccTimerSSFtimeout::timerName) == true)
    {
        protocol_->stopTimer(ccTimerSSFtimeout::timerName);
    }
    protocol_->changeState(state_);
    state_->proceed(protocol_);
    return;
}

//
//Function: exitAction
//
//Description:
//        Exit action for DP -> DP
//

void ccDpState :: exitAction(ccProtocol *protocol_, ccDpState *state_)
{
    if (protocol_->isTimerActive(ccTimerSSFtimeout::timerName) != 0)
    {
        protocol_->stopTimer(ccTimerSSFtimeout::timerName);
    }    
    protocol_->changeState(state_);
    state_->proceed(protocol_);
    return;
}

//
//Functions: getInapType 
//
//Description:
//      Type for INAP message.  
//

/*
Declared pure virtual
   
toveinap_EventTypeBCSMType ccDpState :: getInapType(void) const
{
    return toveinap_oAbandon;
}
*/

//
//Functions: getType 
//
//Description:
//      String presentation for the INAP type.
//

string ccDpState :: getType(void) const
{
    string result("NA");
    return result;
}

ccIf_TriggersType ccDpState :: allowedTriggers(void) const
{
    ccIf_TriggersType triggers; 
    triggers.length(1);

    ccIf_TriggerType trigger;
    ccTriggerDigitString::getInfo(trigger);
    
    triggers[0] = trigger;
    return triggers;
}

//
//Function: process
//
//Description:
//    Start of DP processing with DP data object. 
//    Desing pattern: An hook operation for Template Method.       
//

void ccDpState :: process(ccDp &dp_, ccProtocol *protocol_)
{
    
    if (dp_.isArmed() != 0)
    {
        // Processing of notifications (EDP-N and TDP-N) has
        // higher priority that processing of request 
        // and if a DP is both armed as EDP and TDP, then the
        // EDP processing has higher priority (4.2.10.).
        processEDP_N(dp_, protocol_);
    }
    else
    {
        // To next state, this state "Idle"
        dp_.resetTriggerCount();
        // Dp knows it's next state in basic call progress
        toNextState(dp_, protocol_);
    }
}

//
//Function: processEDP_N
//
//Description:
//    When a SSF/CCF detects an armed EDP, it reports to the
//    SCF in the Event Report BCSM information flow or one
//    of the family of DP-specific information flows (both
//    refered to as "report information flows". For an
//    EDP-Notifications, the SSF/CCF sends the appropriate
//    report information flow, then continues call processing
//    without waiting for additional instructions (10.3.1.1.).
//

void ccDpState :: processEDP_N(ccDp &dp_, ccProtocol *protocol_)
{
    // EDP-N and existing relatioship = Control or Monitor (4.2.10.)
    if ( (dp_.isEDP_N()== true) && (protocol_->isRelationship() == true) )
    {
        // if an armed EDP is met, then it is disarmed (4.2.7.) 
        dp_.setMonitorMode(toveinap_transparent);
        protocol_->decreaseArmedEDPcount();
        
        toveinap_EventReportBCSMArgType eventReportBCSMArg;
     
        eventReportBCSMArg.miscCallInfo.messageType
            = toveinap_notification;

        protocol_->sendEventReportBCSM(dp_, eventReportBCSMArg);
        
    }
    // DP may be armed as EDP and TDP (4.2.10.)
    processTDP_N(dp_, protocol_);
}

//
//Function: processTDP_N
//
//Description:
//     When a trigger detection point (TDP) is detected leads that
//     to sending an Initial DP information flow or one of the
//     family of DP-specific initial information flows (both
//     refered as "initial infromation flows") to SCF. For a
//     TDP-Notification, these information flows do not establish
//     a control relationship (10.3.1.1.).
//

void ccDpState :: processTDP_N(ccDp &dp_, ccProtocol *protocol_)
{
    // TDP-N armed and existing relationship = Don't care (4.2.10.)
    // This implementation allows only one dialog to be open
    // at given time to SCP.
    if ((dp_.isTDP_N() == true) && (protocol_->isDialogOpen() == false))
    {
        // One or More DP criteria may be applicable at a given DP (4.2.8)
        while (dp_.anyTriggersLeft() == true)
        {
            // TDP-N criteria may be processed whether or not there
            // is an existing control relationship for the same portion
            // of the call (4.2.10.)
            
            if (dp_.triggerCriteriaMet(protocol_) == true)
            {   
                toveinap_InitialDPArgType initialDParg;
                
                string inapTargetApplicationContext;
                string inapTargetAddress;
        
                dp_.fillTDPrequest(protocol_, initialDParg,
                                   inapTargetApplicationContext, 
                                   inapTargetAddress);
            
                protocol_->openDialog(inapTargetAddress, 
                                      inapTargetApplicationContext);
                protocol_->sendInitialDP(dp_, initialDParg);
                protocol_->endDialog();
            }
            dp_.nextTrigger();    
        }
        dp_.resetTriggerCount();
    }
    processEDP_R(dp_, protocol_);

    return;
}

//
//Function: processEDP_R
//
//Description:
//    When a SSF/CCF detects an armed EDP, it reports to the
//    SCF in the Event Report BCSM information flow or one
//    of the family of DP-specific information flows (both
//    refered to as "report information flows". For an
//    EDP-Request, the SSF/CCF sends the appropriate
//    report information flow, then waits for additional instructions
//    from the SCF (10.3.1.1.).
//
        
void ccDpState :: processEDP_R(ccDp &dp_, ccProtocol *protocol_)
{
    // EDP-R armed and control relationship
    if ( (dp_.isEDP_R() == true) &&
         (protocol_->isControlRelationship() == true) )
    {
         // if an armed EDP is met, then it is disarmed (4.2.7.) 
        dp_.setMonitorMode(toveinap_transparent);
        protocol_->decreaseArmedEDPcount();

        // Make an event report BCSM information flow to notify
        // the SCF that an armed EDP was encountered and
        // wait for answer.

        toveinap_EventReportBCSMArgType eventReportBCSMArg;
     
        eventReportBCSMArg.miscCallInfo.messageType
            = toveinap_request;
        
        protocol_->sendEventReportBCSM(dp_, eventReportBCSMArg);
        protocol_->startTimer(ccTimerSSFtimeout::timerName);

        // Back to same state = Waiting
    }
    else
    {
        processTDP_R(dp_, protocol_);
    }

    return;
}
        
//
//Function: processTDP_R
//
//Description:
//     When a trigger detection point (TDP) is detected leads that
//     to sending an Initial DP information flow or one of the
//     family of DP-specific initial information flows (both
//     refered as "initial infromation flows") to SCF. For a
//     TDP-Request, these information flows establish
//     a control relationship (10.3.1.1.).
//

void ccDpState :: processTDP_R(ccDp &dp_, ccProtocol *protocol_)
{
    // TDP-R armed and no relationship and anytriggers left and
    // trigger criteria met.
    if ( (dp_.isTDP_R() == true) &&
         (protocol_->isRelationship() == false) &&
         (dp_.anyTriggersLeft() == true) &&
         (dp_.triggerCriteriaMet(protocol_) == true) && 
         (protocol_->isDialogOpen() == false)
        )
    {
        toveinap_InitialDPArgType initialDParg;
        string inapTargetApplicationContext;
        string inapTargetAddress;
        
        dp_.fillTDPrequest(protocol_, initialDParg,
                           inapTargetApplicationContext, inapTargetAddress);


        protocol_->openDialog(inapTargetAddress, inapTargetApplicationContext);
        protocol_->sendInitialDP(dp_, initialDParg);        
        protocol_->startTimer(ccTimerSSFtimeout::timerName);

        // At next round proceeding trigger is handled
        // (after SCF has answered to this message)
        dp_.nextTrigger();
        // Back to same state = "Waiting"
    }
    else
    {
        dp_.resetTriggerCount();
        // DP knows it's next state
        toNextState(dp_, protocol_);        
    }
}



// ***********************************************************************
//
//Functions: common Input methods for INAP interface (from SCF)
//

//
//Function: binapCONTINUEpduAct
//
//Description:
//   This function is initiated by binapCONTINUEpdu messenger. This
//   information flow requests the SSF to proceed with call processing
//   at the DP at which it previously suspended to wait SCF instructions.
//   SSF continues without substituting new data from the SCF. (11.4.3.24.)
//
//

void ccDpState :: binapCONTINUEpduAct(
    binapCONTINUEpdu *,
    ccProtocol *protocol_)
{
    // Is there more TDP-Rs?
    processTDP_R(protocol_->getDp(getInapType()), protocol_);
    return;
}

//
//Function: binapREQUEST_REPORT_BCSM_EVENTpduAct
//
//Description:
//   This function is called when CC receives Request Report
//   BCSM Event message from SCF. This IF is used to request
//   the SSF to monitor for a call-related event(s), then
//   send a notification back to the SCF when the event is
//   detected (Event Report BCSM) (11.4.3.57.).
//
//

void ccDpState :: binapREQUEST_REPORT_BCSM_EVENTpduAct(
    binapREQUEST_REPORT_BCSM_EVENTpdu *messenger_,
    ccProtocol *protocol_)
{
#if CC_DEBUG
traceInput(ccINGW,ccCC,"-Base-","REQUEST_REPORT_BCSM_EVENT");
#endif
   // Arm DPs 
   messenger_->setDPValues(protocol_);
   
   // State = "waiting for instructions".
   // Reset the SSF timer to a shorter value.
   protocol_->stopTimer(ccTimerSSFtimeout::timerName);
   protocol_->startTimer(ccTimerSSFtimeout::timerName);
   
   /*
   Why getTimer is private? Can't set new timeout value!
   pfTimer timer = protocol_->getTimer(ccTimerSSFtimeout::timerName);
   timer.stop();
   timer.setTimeout(ccTimerSSFtimeout::value2);
   timer.start();
   */
   
   // Continue to wait instructions. 
   return;
}

//
//Function: binapRELEASE_CALLpduAct
//
//Description:
//   This function is called when SCF sends a Release Call message.
//   Release Call message is used to kill an existing call at any
//   phase of the call. 
//
//

void ccDpState :: binapRELEASE_CALLpduAct(
    binapRELEASE_CALLpdu *,
    ccProtocol *protocol_)
{
    protocol_->releaseCall();
    // The call segment has been cleared
    return;
}

/*
//
//Function: binapCONNECTpduAct
//
//Description:
//   This function is called when SCF sends a connect message.
//   The connect IF is used to create a call to a defined destination,
//   in the case of existing call in the set up phase, or to forward
//   a call to another destination.
//
//

void ccDpState :: binapCONNECTpduAct(
    binapCONNECTpdu *messenger_,
    ccProtocol *protocol_)
{
    return;
}

*/

// ***********************************************************************
//
//Functions: originating input methods for binap interface (from SCF)
//   These functions are for error case. Implement functions for
//   expected inputs at current states.
//

//
//Function: binapAUTHORIZE_ORIGINATIONpduAct
//
//Description:
//   Response from service logic for current DP.
//   This function is called when CC receives Authorize
//   Origination information flow from SCF requesting
//   to perform the originating basic call processing
//   actions to autohorize a calling party in Authorize
//   Origination Attempt PIC. The DP state is changed from
//   Waiting for instruction to Idle.
//
//

void ccDpState :: binapAUTHORIZE_ORIGINATIONpduAct(
    binapAUTHORIZE_ORIGINATIONpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

//
//Function: binapCOLLECT_INFORMATIONpduAct
//
//Description:
//   This function is called when the SCF requests the SSF to perform
//   the originating basic call processing actions to prompt
//   a calling party for destination information, then collect destination
//   information from the callong party according to a specified
//   Numbering Plan Indicator.
//
//

void ccDpState :: binapCOLLECT_INFORMATIONpduAct(
    binapCOLLECT_INFORMATIONpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

//
//Function: binapANALYSE_INFORMATIONpduAct
//
//Description:
//   This function is called when the SCF requests the SSF to perform
//   the originating basic call processing actions to analyze
//   destination information that either is collected from calling
//   party, or provided by the SCF (e.g. Number traslation).
//
//

void ccDpState :: binapANALYSE_INFORMATIONpduAct(
    binapANALYSE_INFORMATIONpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;    
}

//
//Function: binapSELECT_ROUTEpduAct
//
//Description:
//   This function is called when the SCF requests the SSF to perform
//   the originating basic call processing actions to determine
//   routing information and select a route for a call, based on call
//   information or provided by the SCF (e.g. for alternate routing).
//   This includes actions to select a primary route for the call,
//   and if the route is busy, to select an alternate route.
//   
//
//

void ccDpState :: binapSELECT_ROUTEpduAct(
    binapSELECT_ROUTEpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

// ***********************************************************************
//
//Functions: terminating input methods for binap interface (from SCF)
//   These functions are for error case. Implement functions for
//   expected inputs at current states.
//

//
//Function: binapAUTHORIZE_TERMINATIONpduAct
//
//Description:
//   Response from service logic for current DP.
//   This function is called when CC receives Authorize
//   Termination information flow from SCF requesting
//   to perform the terminating basic call processing
//   actions to autohorize a calling party in Authorize
//   Termination Attempt PIC.
//
//

void ccDpState :: binapAUTHORIZE_TERMINATIONpduAct(
            binapAUTHORIZE_TERMINATIONpdu *,
            ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

//
//Function: binapSELECT_FACILITYpduAct
//
//Description:
//   Select facility IF flow requests the SSF to perform the
//   terminating basic call processing actions to select the
//   terminating line if it is idle.
//
//

void ccDpState :: binapSELECT_FACILITYpduAct(
    binapSELECT_FACILITYpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

//
//Function: binapPRESENT_CALLpduAct
//
//Description:
//   Present call IF flow requests the SSF to perform the
//   terminating basic call processing actions to present the
//   call to selected called party.
//

void ccDpState :: binapPRESENT_CALLpduAct(
    binapPRESENT_CALLpdu *,
    ccProtocol *protocol_)
{
    unexpectedINAPmessage(protocol_);
    return;
}

void ccDpState :: unexpectedINAPmessage(ccProtocol *)
{
#if CC_DEBUG
    cout << "Unexpected INAP message, ignoring" << endl;
#endif
}

// **********************************************************************
//
//Function: ccTimerSSFtimeoutAct 
//
//Description: SSF->SCF interaction (waiting for instructions)
//     has taken too long. 
//
//

void ccDpState :: ccTimerSSFtimeoutAct(pfProtocol *protocol_)
{
    ccProtocol *ccprotocol = dynamic_cast<ccProtocol*>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(ccprotocol)
     
    ccprotocol->endDialog(); 

    // Try to continue call with current information.
    toNextState(ccprotocol->getDp(getInapType()), ccprotocol);
    return;
}

//************************************************************************
//
//Functions: Input methods for signalling interface (from down)
//
//Description:
//    If call is in DP, signalling events must be saved and
//    processed later.
//

void ccDpState :: sigSETUPindAct(sigSETUPind *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}
               
void ccDpState :: sigPROCEEDINGindAct(sigPROCEEDINGind *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}

void ccDpState :: sigSETUPconfAct(sigSETUPconf *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}


void ccDpState :: sigSETUP_COMPLETEindAct(
    sigSETUP_COMPLETEind *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}

void ccDpState :: sigRELEASEindAct(sigRELEASEind *, pfProtocol *protocol_)
{
    ccProtocol *ccprotocol = dynamic_cast<ccProtocol*>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(ccprotocol)
        
    if (ccprotocol->isSideO() != 0)
    {
        ccprotocol->changeState(ccOdpAbandon::instance());
    }
    else
    {
        ccprotocol->changeState(ccTstateException::instance());
    }   
    return;
}
         
void ccDpState :: sigRELEASEconfAct(sigRELEASEconf *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}
                
void ccDpState :: sigRESETindAct(sigRESETind *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}
        
void ccDpState :: sigRESETconfAct(sigRESETconf *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}
        
void ccDpState :: sigRESET_ERRORindAct(sigRESET_ERRORind *, pfProtocol *protocol_)
{
    unexpectedSignallingMessage(protocol_);
    return;
}


//----------------- Additional --------------------------------

/*
void ccDpState :: sigMORE_INFOindAct(sigMORE_INFOind *, pfProtocol *)
{
    return;
}

void ccDpState :: sigINFOindAct(sigINFOind *, pfProtocol *)
{
    return;
}
        
void ccDpState :: sigPROGRESSindAct(sigPROGRESSind *, pfProtocol *)
{
    return;
}
        
void ccDpState :: sigNOTIFYindAct(sigNOTIFYind *, pfProtocol *)
{
    return;
}
        
void ccDpState :: sigALERTINGindAct(sigALERTINGind *, pfProtocol *)
{
    return;
}

*/


//
//Function: unexpectedSignallingMessage
//
//Description: 
//     
//

void ccDpState :: unexpectedSignallingMessage(pfProtocol *)
{
    THROW_PROTOCOL_ERROR_UNSPECIFIED
    return;
}

//
//Function: ccRELEASEpduAct
//
//Description: common RELEASE handler
//    This function is called when a disconnect indication is
//    received from the terminating party (called party).
//

void ccDpState :: ccRELEASEpduAct(ccRELEASEpdu *,
                                 ccProtocol *)
{

    return;
}

//
//Function: ccRELEASE_ACTIVEpduAct
//
//Description: 
//    Unexpected RELEASE_ACTIVE message form T side.
//    Release connection.
//

void ccDpState :: ccRELEASE_ACTIVEpduAct(ccRELEASE_ACTIVEpdu *,
                                        ccProtocol *)
{

    return;
}

//
//Function: ccSETUPpduAct
//
//Description: 
//    Unexpected SETUP message form T side.
//    Release connection.
//

void ccDpState :: ccSETUPpduAct(ccSETUPpdu *,
                               ccProtocol *)
{
    return;
}

//
//Function: ccALERTINGpduAct
//
//Description: 
//    Unexpected ALERTING message form T side.
//    Release connection.

void ccDpState :: ccALERTINGpduAct(ccALERTINGpdu *,
                                  ccProtocol *)
{

    return;
}


