//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SSCOP protocol
//
//File: sscopstate7.cpp
//
//Version: $Revision: 1.12 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/11 18:53:56 $
//
//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 "sscopstate7.h"
#include "sscoppdu.h"
#include "sscopprotocol.h"
#include "sscopinternals.h"
#include "iface/aaif/aadownprimitives.h"
#include "pf/frame.h"
#include "pf/timer.h"
#include "pf/error.h"

//
// Variable: sscopOutgoingRecoveryPending :: _only
// Function: sscopOutgoingRecoveryPending :: instance()
//
// Description:
//     Implements the singleton pattern used for actual states
//

sscopOutgoingRecoveryPending *
sscopOutgoingRecoveryPending :: _only = 0;

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


sscopOutgoingRecoveryPending :: sscopOutgoingRecoveryPending(void)
    : sscopState()
{
    return;
}


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


//
// Functions: AA primitive input methods
//
// Description:
//     These methods define actions in SSCOP state 7 for incoming
//     AA-signals from the above layer (UNI SSCF or NNI SSCF for
//     the time being)
//

void sscopOutgoingRecoveryPending :: aaRELEASEreqAct(
    aaRELEASEreq *messenger_,
    pfProtocol *protocol_) const
{
    sscopProtocol *sscop = dynamic_cast<sscopProtocol *>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(sscop);
    sscop->_timerCC.stop();
    sscop->_VT_CC = 1;
    sscop->sendENDpdu(messenger_->getSSCOP_UU(), SSCOP_RELEASE_USER);
    sscop->clearReceiverBuffer();
    sscop->_timerCC.start();
    sscop->changeToOutgoingDisconnectionPendingState();
    return;
}

void sscopOutgoingRecoveryPending :: aaDATAreqAct(
    aaDATAreq *messenger_,
    pfProtocol *protocol_) const
{
    sscopProtocol *sscop = dynamic_cast<sscopProtocol *>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(sscop);
    if (sscop->_clearBuffers == 0)
    {
        sscop->_transmissionQueue.push_back(messenger_->getMessageUnit());
        sscop->createSDpduQueuedUp();
    }
    return;
}

void sscopOutgoingRecoveryPending :: aaRESYNCreqAct(
    aaRESYNCreq *messenger_,
    pfProtocol *protocol_) const
{
    sscopProtocol *sscop = dynamic_cast<sscopProtocol *>(protocol_);
    THROW_IF_DYNAMIC_CAST_FAILED(sscop);
    sscop->_timerCC.stop();
    sscop->_VT_CC = 1;
    sscop->_VT_SQ++;
    sscop->initializeVR_MR();
    sscop->sendRSpdu(messenger_->getSSCOP_UU());
    sscop->clearTransmitter();
    sscop->clearReceiverBuffer();
    sscop->_timerCC.start();
    sscop->changeToOutgoingResynchronizationPendingState();
    return;
}


//
// Functions: SSCOP PDU input methods
//
// Description:
//     Actions in SSCOP state 7 for received PDUs
//

void sscopOutgoingRecoveryPending :: sscopBGNpduAct(
    sscopBGN_PDU *messenger_,
    sscopProtocol *protocol_) const
{
    if (protocol_->detectRetransmission(messenger_->getN_SQ()) != 0)
    {
        protocol_->sendErrorReleaseEND(SSCOP_ERROR_B);
    }
    else
    {
        protocol_->_timerCC.stop();
        protocol_->_VT_MS = messenger_->getN_MR();
        pfFrame sscop_uu;
        protocol_->sendAaRELEASEind(sscop_uu, SSCOP_RELEASE_USER);
        protocol_->sendAaESTABLISHind(messenger_->getSSCOP_UU());
        protocol_->clearReceiverBuffer();
        protocol_->changeToIncomingConnectionPendingState();
    }
    return;
}

void sscopOutgoingRecoveryPending :: sscopBGAKpduAct(
    sscopBGAK_PDU *,
    sscopProtocol *protocol_) const
{
    protocol_->sendErrorReleaseEND(SSCOP_ERROR_C);
    return;
}

void sscopOutgoingRecoveryPending :: sscopBGREJpduAct(
    sscopBGREJ_PDU *,
    sscopProtocol *protocol_) const
{
    protocol_->sendErrorReleaseEND(SSCOP_ERROR_D, 0, 1);
    protocol_->_timerCC.stop();
    protocol_->clearReceiverBuffer();
    protocol_->changeToIdleState();
    return;
}

void sscopOutgoingRecoveryPending :: sscopENDpduAct(
    sscopEND_PDU *messenger_,
    sscopProtocol *protocol_) const
{
    protocol_->_timerCC.stop();
    protocol_->sendENDAKpdu();
    protocol_->sendAaRELEASEind(messenger_->getSSCOP_UU(),
                                SSCOP_RELEASE_USER);
    protocol_->clearReceiverBuffer();
    protocol_->changeToIdleState();
    return;
}

void sscopOutgoingRecoveryPending :: sscopENDAKpduAct(
    sscopENDAK_PDU *,
    sscopProtocol *protocol_) const
{
    protocol_->sendErrorReleaseEND(SSCOP_ERROR_F, 0, 1);
    protocol_->_timerCC.stop();
    protocol_->clearReceiverBuffer();
    protocol_->changeToIdleState();
    return;
}

void sscopOutgoingRecoveryPending :: sscopRSpduAct(
    sscopRS_PDU *messenger_,
    sscopProtocol *protocol_) const
{
    if (protocol_->detectRetransmission(messenger_->getN_SQ()) != 0)
    {
        protocol_->sendErrorReleaseEND(SSCOP_ERROR_J);
    }
    else
    {
        protocol_->_timerCC.stop();
        protocol_->_VT_MS = messenger_->getN_MR();
        protocol_->sendAaRESYNCind(messenger_->getSSCOP_UU());
        protocol_->clearReceiverBuffer();
        protocol_->changeToIncomingResynchronizationPendingState();
    }
    return;
}

void sscopOutgoingRecoveryPending :: sscopRSAKpduAct(
    sscopRSAK_PDU *,
    sscopProtocol *protocol_) const
{
    protocol_->sendErrorReleaseEND(SSCOP_ERROR_K);
    return;
}

void sscopOutgoingRecoveryPending :: sscopERpduAct(
    sscopER_PDU *messenger_,
    sscopProtocol *protocol_) const
{
    if (protocol_->detectRetransmission(messenger_->getN_SQ()) != 0)
    {
        protocol_->sendErrorReleaseEND(SSCOP_ERROR_L);
    }
    else
    {
        protocol_->_timerCC.stop();
        protocol_->_VT_MS = messenger_->getN_MR();
        protocol_->initializeVR_MR();
        protocol_->sendERAKpdu();
        protocol_->deliverData();
        protocol_->sendAaRECOVERind();
        protocol_->changeToRecoveryResponsePendingState();
    }
    return;
}

void sscopOutgoingRecoveryPending :: sscopERAKpduAct(
    sscopERAK_PDU *messenger_,
    sscopProtocol *protocol_) const
{
    protocol_->_timerCC.stop();
    protocol_->_VT_MS = messenger_->getN_MR();
    protocol_->deliverData();
    protocol_->sendAaRECOVERind();
    protocol_->changeToRecoveryResponsePendingState();
    return;
}

//
// Functions: Timeout input methods
//
// Description:
//     Actions in SSCOP state 2 for timer timeouts
//

void sscopOutgoingRecoveryPending :: sscopCCtimeoutAct(
    sscopProtocol *protocol_) const
{
    if (protocol_->_VT_CC >= protocol_->_maxCC)
    {
        protocol_->sendErrorReleaseEND(SSCOP_ERROR_O, 0, 1, 1);
        protocol_->clearReceiverBuffer();
        protocol_->changeToIdleState();
    }
    else
    {
        protocol_->_VT_CC++;
        protocol_->resendLastERpdu();
        protocol_->_timerCC.start();
    }
    return;
}
