//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project/CC
//
//File: ccostate_selectroute.cpp
//
//Version: $Revision: 1.26 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/12/16 16:50:58 $
//
//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 "pf/debug.h"
#include "ccprotocol.h"
#include "ccostate_authcallsetup.h"
#include "ccodp_routeselectfailure.h"
#include "iface/sigif/sigdownprimitives.h"
#include "ccostate_selectroute.h"
#include "cccrossconnectormux.h"
#include "protocol/uni/unidefs.h"
#include "pf/error.h"
#include "ie/connectionidentifier.h"

ccOstateSelectRoute *ccOstateSelectRoute :: _only = 0;

ccOstateSelectRoute :: ccOstateSelectRoute(void)
    : ccOstate()
{
    return;
}

ccOstateSelectRoute :: ~ccOstateSelectRoute(void)
{
    _only = 0;
    return;
}

ccOstateSelectRoute *ccOstateSelectRoute :: instance(void)
{
    if(_only == 0)
    {
        _only = new ccOstateSelectRoute;
        assert(_only != 0);
    }
    return _only;
}

//
//Function: proceed
//
//Description:
//     Availability of routing address
//
//

void ccOstateSelectRoute :: proceed(ccProtocol *protocol_)
{     
    string number = protocol_->getCalledPartyNumber();
   
#if CC_DEBUG
    string mes("Select Route (B-number: ");
    mes = mes + number + ")"; 
traceState(ccCC,(char*) mes.c_str());
#endif

    protocol_->connect();
    // Waiting response from FCF

    return;
}


//
//Function: SendNegativeResponseWithCause
//
//Description:
//   Negative response for indication of outgoing call.
//
//

void ccOstateSelectRoute :: sendNegativeResponseWithCause(
    ccProtocol *protocol_, pfUlong cause_)
{
#if CC_DEBUG
traceSend(ccCC,ccDSS2,"SelectRoute","RELEASEresp");
#endif      

    pfStorage storage;
    pfStorage ieStorage;

    ieStorage.defineInteger(sigCause_ValueStr);
    ieStorage.setInteger(sigCause_ValueStr, cause_);
    storage.defineStorage(sigCauseStr);
    storage.setStorage(sigCauseStr, ieStorage);

    protocol_->setData("release", storage);

    exitAction(protocol_, ccOdpRouteSelectFailure::instance()); 
   
    return;
}

//
//Function: swSuccessMessageAct
//
//Description:
//   Must be implemented in state select Route, this
//   method presents an error case.

void ccOstateSelectRoute :: swSuccessMessageAct(swSuccessMessage *messenger_, 
                                                pfProtocol *protocol_)
{
#if CC_DEBUG
traceInput(ccVE,ccCC,"Select Route","swSuccessMessage");
#endif

    ccProtocol *ccprotocol = dynamic_cast<ccProtocol*>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(ccprotocol)


    pfIE *inIE = messenger_->adoptIE("inputInfo");
    pfIE *outIE = messenger_->adoptIE("outputInfo");

    ieConnectionInfo *inConnection = 
        dynamic_cast<ieConnectionInfo*>(inIE);
    THROW_IF_DYNAMIC_CAST_FAILED(inConnection)
    ieConnectionInfo *outConnection = 
        dynamic_cast<ieConnectionInfo*>(outIE);
    THROW_IF_DYNAMIC_CAST_FAILED(outConnection)

    // Save connection data for later use (e.g. disconnect) 
    ccprotocol->setConnections(inConnection, outConnection);

    pfUlong outPort = outConnection->getLinkIdentifier();

    // Send CrossConnector to side T 
    bool status = makeConnectionToSideT(ccprotocol, outPort);
    
    // If T side was found
    if (status == true)
    {
        // Send Call Proceeding
        sendPositiveResponse(ccprotocol, inIE);
    }
    else // We have wrong routing info 
    {
        pfUlong cause = uniCauseValue_TemporaryFailure;
        // Send Release
        sendNegativeResponseWithCause(ccprotocol, cause);       
    }
    return;
}

//
//Function: makeConnectionToSideT 
//
//Description:
//    Creates side-T protocol and connects it to O-side.
//      

bool ccOstateSelectRoute :: makeConnectionToSideT(ccProtocol *protocol_,
                                                  pfUlong outPort_)
{
    bool status = 0;
    
    pfCrossConnecter crossConnecter = 
       pfCrossConnecter::createCrossConnector();

    pfConduit ccOprotocol(protocol_);
    crossConnecter.setOtherSide(ccOprotocol);

    string keyName = ccCrossConnectorMux::getLinkIdName();
    crossConnecter.setKeyName(keyName);
   
    crossConnecter.setKey(outPort_);
    crossConnecter.setSenderIsA();
   
    protocol_->toCrossConnector(&crossConnecter);
    
    // Cross Connecter returns the "Other Side"
    pfConduit otherSide = crossConnecter.getOtherSide();
    const pfConduit nullConduit(0);

    if (otherSide != nullConduit)
    {
        // Create new T side
        ccProtocol *ccTprotocolPtr = ccProtocol::create();
        pfConduit ccTprotocol(ccTprotocolPtr);
        
        ccOprotocol.connectToB(ccTprotocol);
        ccTprotocol.connectToB(ccOprotocol);
        
        otherSide.connectToB(ccTprotocol);
        ccTprotocol.connectToA(otherSide);
        status = 1;
    }
    else
    {
#if CC_DEBUG
        debugUser("OtherSide = nullConduit, connection failed");
#endif         
        status = 0;
    }
    
    return status;
}

//
//Function: sendPositiveResponse
//
//Description:
//   Positive response for indication of outgoing call.
//

void ccOstateSelectRoute :: sendPositiveResponse(ccProtocol *protocol_, 
                                                 pfIE *ie_)
{
    ieConnectionIdentifier *atmInConnection = 
        dynamic_cast<ieConnectionIdentifier*>(ie_);
    THROW_IF_DYNAMIC_CAST_FAILED(atmInConnection)

    sigPROCEEDINGreq *proceeding = new sigPROCEEDINGreq;

    pfUlong inVpci = atmInConnection->getVPCI();
    pfUlong inVci = atmInConnection->getVCI();

   (*proceeding)[sigConnectionIdentifierStr].setInteger(sigCI_VPCIStr, inVpci);
   (*proceeding)[sigConnectionIdentifierStr].setInteger(sigCI_VCIStr, inVci);

#if CC_DEBUG
    char str[30];
    sprintf(str, "(VPI=%d,VCI=%d)", (int) inVpci,(int) inVci);
    string text("PROCEEDINGreq ");
    text+=str;    
traceSend(ccCC,ccDSS2,"SelectRoute", (char*) text.c_str());
#endif

    protocol_->toDown(proceeding);

    exitAction(protocol_, ccOstateAuthorizeCallSetup::instance()); 
    
    return;    
}

//
//Function: swFailureMessageAct
//
//Description:
//    Error in routing or fabric control
//

void ccOstateSelectRoute :: swFailureMessageAct(swFailureMessage *messenger_, 
                                                pfProtocol *protocol_)
{
#if CC_DEBUG
traceInput(ccVE,ccCC,"SelectRoute","swFailureMessage");
#endif
    
    ccProtocol *ccprotocol = dynamic_cast<ccProtocol*>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(ccprotocol)

    // ++TODO++ Change get to adopt
    pfIE *ie = messenger_->getIE("cause");
    sendNegativeResponse(ccprotocol, ie);

    return;
}

//
//Function: SendNegativeResponse
//
//Description:
//   Negative response for indication of outgoing call.
//
//

void ccOstateSelectRoute :: sendNegativeResponse(
    ccProtocol *protocol_, pfIE *ie_)
{
#if CC_DEBUG
traceSend(ccCC,ccDSS2,"SelectRoute","RELEASEresp");
#endif      

    ieCause *causeIe = dynamic_cast<ieCause*>(ie_);
    THROW_IF_DYNAMIC_CAST_FAILED(causeIe)

    pfUlong cause = causeIe->getCauseValue();

    pfStorage storage;
    pfStorage ieStorage;

    ieStorage.defineInteger(sigCause_ValueStr);
    ieStorage.setInteger(sigCause_ValueStr, cause);
    storage.defineStorage(sigCauseStr);
    storage.setStorage(sigCauseStr, ieStorage);

    protocol_->setData("release", storage);

    exitAction(protocol_, ccOdpRouteSelectFailure::instance()); 
   
    return;
} 


