//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SCCP protocol
//
//File: sccpstate_scrc_idle.cpp
//
//Version: $Revision: 1.15 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/11/09 09:45:57 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Timo Pärnänen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//
//
//Licence:
//
//
//History: 

#include "pf/storage.h"
#include "pf/debug.h"
#include "common/ss7defs.h"
#include "sccpdefs.h"
#include "iface/mtpif/mtpdownprimitives.h"
#include "sccpblock_scrc.h"
#include "sccpstate_scrc_idle.h"

// Implementation of the singleton pattern

sccp_SCRC_Idle *sccp_SCRC_Idle::_only = 0;

sccp_SCRC_Idle *sccp_SCRC_Idle :: instance(void)
{
    if (_only == 0)
    {
        _only = new sccp_SCRC_Idle;
    }
    return _only;
}

sccp_SCRC_Idle :: sccp_SCRC_Idle(void)
{
    return;
}

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

//
//Functions: State input methods 
//
//Description:
//    PDU inputs from SCLC and primitives from MTP3
//

void sccp_SCRC_Idle ::  sccpUDTpduAct(sccpUDTpdu *messenger_,
                                      pfProtocol *protocol_)
{
    sccpBlockSCRC *block = (sccpBlockSCRC*) protocol_;

    pfStorage calledAddress = messenger_->getStorage("calledAddress");
    pfUlong DPC = 0;
    if (calledAddressIncludesDPC(calledAddress, DPC) == 0)
    {
        continueAtTranslation(block, messenger_);
    }
    else
    {
        continueAtDPCchecking(block, messenger_, DPC);
    }
}

void sccp_SCRC_Idle :: mtpTRANSFERindAct(mtpTRANSFERind *messenger_,
                                         pfProtocol *protocol_)
{
    sccpBlockSCRC *block = dynamic_cast<sccpBlockSCRC *>(protocol_);
    pfFrame frame = messenger_->getSignallingInformation();
    sccpPdu *pdu = sccpPdu :: createPdu(messenger_);
    if (pdu != 0)
    {
        try
        {
            if (pdu->isValuePresent("calledAddress") == 0)
            {
                debugUser("--connection oriented message: discarded");
            }
            else if (pdu->routeOnGT() == 0)
            {
                continueAtSubSystemChecking(block, pdu);
            }
            else
            {
                continueAtTranslation(block, pdu);
            }
        }
        catch (pfException &exception)
        {
            // Message is not passed to the sub system or
            // MTP3 layer, maybe insufficient or invalid
            // compination of information.
            
            debugUser("Message discarded (mtpTRANSFERindAct)");
        }
        
        // Pdu is transfered synchronously (locally inside SCCP)
        // It is not destroyed automatically.
        delete pdu;
    }
    // pdu == 0 indicates decoding error
    // and message is discarded
    return;
}

//
//Functions: Assistance methods for state machine action
//
//Description:
//    These methods makes quite complex state machine clearer.
//    Most of methods are named due to SDL diagram actions.
//

bool sccp_SCRC_Idle :: calledAddressIncludesDPC(
    pfStorage &calledAddress_, pfUlong &DPC_) const
{
    bool result = false;
    if (calledAddress_.isValuePresent("pointCode") != 0)
    {
        DPC_ = calledAddress_.getInteger("pointCode");
        result = true;
    }
    return result;
}

void sccp_SCRC_Idle :: continueAtSubSystemChecking(sccpBlockSCRC *block_,
                                                   sccpPdu *pdu_)
{
    pfStorage calledAddress = pdu_->getStorage("calledAddress");
    pfUlong SSN = calledAddress.getInteger("subSystemNumber");
    if (block_->isSubSystemAvailable(SSN) != 0)
    {
        // Only connectionless messages are supported
        block_->toSCLC(pdu_);
    }
    else
    {
        block_->sendRoutingFailure();
    }
    return;
}

void sccp_SCRC_Idle :: continueAtTranslation(sccpBlockSCRC *block_,
                                             sccpPdu *pdu_)
{
    pfUlong DPC = 0;
    if (block_->translateAddress(pdu_, DPC) == 0)
    {
        block_->sendRoutingFailure();
    }
    else
    {
        continueAtDPCchecking(block_, pdu_, DPC);  
    }
}

void sccp_SCRC_Idle :: continueAtDPCchecking(sccpBlockSCRC *block_,
                                             sccpPdu *pdu_,
                                             pfUlong DPC_)
{
    if (block_->isDPCthisNode(DPC_) != 0) 
    {
        continueAtSubSystemChecking(block_, pdu_);
    }
    else if (block_->isDPCaccessible(DPC_) != 0)
    {
        mtpTRANSFERreq *messenger = new mtpTRANSFERreq;
        messenger->setDestinationPointCode(DPC_);
        messenger->setOriginatingPointCode(block_->getPointCode());
        messenger->setSignallingLinkSelection(
            pdu_->getInteger("signallingLinkSelection"));
        // Sub service field is not supported in
        // service information octet (in this version)
        messenger->setServiceInformationOctet(SCCP);

        pfFrame frame = pdu_->encode();
        messenger->setSignallingInformation(frame);
        block_->toMTP(messenger);
    }
    else
    {
        block_->sendRoutingFailure();
    }
}

