//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SSCOP protocol
//
//File: sscoppdu.cpp
//
//Version: $Revision: 1.16 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/11 18:53:55 $
//
//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 "sscoppdu.h"
#include "sscopstate.h"
#include "sscopprotocol.h"
#include "pf/bytes.h"
#include "pf/debug.h"
#include "pf/error.h"

//
// Function: sscopPDUmessenger :: create()   (static)
//
// Description:
//     This static method accepts a frame and tries to decode it into
//     a SSCOP PDU messenger. It tries to identify its type, create a
//     new nessenger of that type and decode the messenger. If decoding
//     fails, an instance of sscopINVALIDpdu is returned.
//

sscopPDUmessenger *sscopPDUmessenger :: create(const pfFrame &frame_,
                                               pfUlong maxSDUsize_,
                                               pfUlong maxUUsize_)
{
    sscopPDUmessenger *pdu = 0;
    pfUlong error = 0;
    sscopPDU pduType = sscopINVALIDpdu;

    // PDU must be at least SSCOP_MIN_PDU_LENGTH (8 octets) long and
    // aligned to 4 octets; if it is, read its type from the frame
    if ((frame_.length() < SSCOP_MIN_PDU_LENGTH) ||
        ((frame_.length() % 4) != 0))
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        pduType = (sscopPDU) (frame_.read(frame_.length() - 4) & B0000_1111);
        switch (pduType)
        {
            case sscopBGNpdu:
                if (frame_.length() > (sscopBGNpdu_MINLEN + maxUUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopBGN_PDU;
                }
                break;
            case sscopBGAKpdu:
                if (frame_.length() > (sscopBGAKpdu_MINLEN + maxUUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopBGAK_PDU;
                }
                break;
            case sscopBGREJpdu:
                if (frame_.length() > (sscopBGREJpdu_MINLEN + maxUUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopBGREJ_PDU;
                }
                break;
            case sscopENDpdu:
                if (frame_.length() > (sscopENDpdu_MINLEN + maxUUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopEND_PDU;
                }
                break;
            case sscopENDAKpdu:
                pdu = new sscopENDAK_PDU;
                break;
            case sscopRSpdu:
                if (frame_.length() > (sscopRSpdu_MINLEN + maxUUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopRS_PDU;
                }
                break;
            case sscopRSAKpdu:
                pdu = new sscopRSAK_PDU;
                break;
            case sscopERpdu:
                pdu = new sscopER_PDU;
                break;
            case sscopERAKpdu:
                pdu = new sscopERAK_PDU;
                break;
            case sscopSDpdu:
                if (frame_.length() > (sscopSDpdu_MINLEN + maxSDUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopSD_PDU;
                }
                break;
            case sscopPOLLpdu:
                pdu = new sscopPOLL_PDU;
                break;
            case sscopSTATpdu:
                pdu = new sscopSTAT_PDU;
                break;
            case sscopUSTATpdu:
                pdu = new sscopUSTAT_PDU;
                break;
            case sscopUDpdu:
                if (frame_.length() > (sscopUDpdu_MINLEN + maxSDUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopUD_PDU;
                }
                break;
            case sscopMDpdu:
                if (frame_.length() > (sscopMDpdu_MINLEN + maxSDUsize_))
                {
                    error = SSCOP_PDU_INVALID_LENGTH;
                }
                else
                {
                    pdu = new sscopMD_PDU;
                }
                break;
            case sscopINVALIDpdu:
            default:
                error = SSCOP_PDU_OTHER_ERROR;
                break;
        }
    }
    
    // If pdu != 0, the type of the PDU had been identified; decode
    if (pdu != 0)
    {
        pdu->_frame = frame_;
        error = pdu->decode();
    }

    // If a decoding error occured, create a INVALID pdu messenger
    if (error != 0)
    {
        debugUser("decoding error occured now we have invalid pdu");
        delete pdu;
        pdu = new sscopINVALID_PDU;
        assert(pdu != 0);
        pdu->_errorCode = error;
    }
    return pdu;
}


sscopPDUmessenger :: sscopPDUmessenger(void)
    : pfMsgTransporter(0),
      _frame(),
      _N_S(0),
      _N_R(0),
      _N_PS(0),
      _N_SQ(0),
      _N_MR(0),
      _errorCode(0)
{
    return;
}


sscopPDUmessenger :: sscopPDUmessenger(const sscopPDUmessenger &other_)
    : pfMsgTransporter(0),
      _frame(other_._frame),
      _N_S(other_._N_S),
      _N_R(other_._N_R),
      _N_PS(other_._N_PS),
      _N_SQ(other_._N_SQ),
      _N_MR(other_._N_MR),
      _errorCode(other_._errorCode)
{
    return;
}


const sscopPDUmessenger &
sscopPDUmessenger :: operator=(const sscopPDUmessenger &other_)
{
    _frame = other_._frame;
    _N_S = other_._N_S;
    _N_R = other_._N_R;
    _N_PS = other_._N_PS;
    _N_SQ = other_._N_SQ;
    _N_MR = other_._N_MR;
    _errorCode = other_._errorCode;
    return *this;
}


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


//
// Functions: sscopPDUmessenger (shared) field get methods
//
// Description:
//     Return value of the field. These methods are defined in the
//     base class as are the respective fields (they are used in
//     more than one PDU).
//

pfFrame & sscopPDUmessenger :: getInformation(void)
{
    return _frame;
}


pfFrame & sscopPDUmessenger :: getSSCOP_UU(void)
{
    return _frame;
}


pfUlong sscopPDUmessenger :: getN_S(void) const
{
    return _N_S;
}


pfUlong sscopPDUmessenger :: getN_R(void) const
{
    return _N_R;
}


pfUlong sscopPDUmessenger :: getN_PS(void) const
{
    return _N_PS;
}


pfUlong sscopPDUmessenger :: getN_SQ(void) const
{
    return _N_SQ;
}


pfUlong sscopPDUmessenger :: getN_MR(void) const
{
    return _N_MR;
}


pfUlong sscopPDUmessenger :: getErrorCode(void) const
{
    return _errorCode;
}


//
// Function: sscopPDUmessenger :: readPadLength()
//
// Description:
//     Returns value of the "pad length" bits in SSCOP PDU type octet.
//

pfByte sscopPDUmessenger :: readPadLength(pfByte typeOctet_) const
{
    pfByte padLength = ((typeOctet_ >> 6) & B0000_0011);
    return padLength;
}


//
// Function: sscopPDUmessenger :: removeReserved()
//
// Description:
//     Removes n octets from the end of the frame (SSCOP PDU "reserved
//     for future use" fields).
//

void sscopPDUmessenger :: removeReserved(pfFrame &frame_, pfUlong n_) const
{
    assert(n_ <= (pfUlong) frame_.length());
    for (pfUlong i = 0; i < n_; i++)
    {
        frame_.getLast();
    }
    return;
}


//
// Function: sscopPDUmessenger :: padPDU
//
// Description:
//     Pads the frame containing an information chunk into 4-octet
//     multiply and returns the number of padding octets
//

pfUlong sscopPDUmessenger :: padPDU(pfFrame &frame_)
{
    pfUlong pads = frame_.length() % 4;
    if (pads > 0)
    {
        pads = 4 - pads;
        appendReserved(frame_, pads);
    }
    return pads;
}


//
// Function: sscopPDUmessenger :: appendReserved
//
// Description:
//     Appends n octets of reserved field at the end of a frame

void sscopPDUmessenger :: appendReserved(pfFrame &frame_, int n_)
{
    for (int i = 0; i < n_; i++)
    {
        frame_.putLast(B0000_0000);
    }
    return;
}


//
// Function: sscopPDUmessenger :: appendTypeOctet
//
// Description:
//     Encodes a type field into a byte, adds pad length field and
//     appeds the result at the end of a frame
//

void sscopPDUmessenger :: appendTypeOctet(
    pfFrame &frame_,
    sscopPDU pdu_,
    int padLength_)
{
    pfByte byte = (pfByte) (pdu_ & B0000_1111);
    byte |= (pfByte) ((padLength_ & B0000_0011) << 6);
    frame_.putLast(byte);
    return;
}


//----------------------------------------------------------------------
// Functions: SSCOP PDU apply/decode methods
//
// Description:
//     These methods are defined separately for each SSCOP PDU. Apply
//     calls the respective input method of the SSCOP state machine
//     and decode tries to decode a frame (that create() method has
//     put into the newly created PDU messenger).
//

// MODIFIED: jturunen
sscopBGN_PDU :: sscopBGN_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopBGN_PDU :: encode(pfFrame &sscop_uu_, pfUlong n_sq_, pfUlong n_mr_)
{
    pfUlong pads = padPDU(sscop_uu_);
    appendReserved(sscop_uu_, 3);
    sscop_uu_.putLast((pfByte) n_sq_);
    appendTypeOctet(sscop_uu_, sscopBGNpdu, pads);
    sscop_uu_.putLast24bit(n_mr_);
    return;
}


// The BGN PDU maximum lenght is checked against a dynamically configurable
// maximum size kept in sscopProtocol.

pfUlong sscopBGN_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopBGNpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
        pfByte pads = readPadLength(_frame.getLast());
        _N_SQ = (pfUlong) _frame.getLast();
        removeReserved(_frame, 3 + pads);
    }
    return error;
}


void sscopBGN_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopBGNpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopBGAK_PDU :: sscopBGAK_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopBGAK_PDU :: encode(pfFrame &sscop_uu_, pfUlong n_mr_)
{
    pfUlong pads = padPDU(sscop_uu_);
    appendReserved(sscop_uu_, 4);
    appendTypeOctet(sscop_uu_, sscopBGAKpdu, pads);
    sscop_uu_.putLast24bit(n_mr_);
    return;
}


pfUlong sscopBGAK_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopBGAKpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
        pfByte pads = readPadLength(_frame.getLast());
        removeReserved(_frame, 4 + pads);
    }
    return error;
}


void sscopBGAK_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopBGAKpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopBGREJ_PDU :: sscopBGREJ_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopBGREJ_PDU :: encode(pfFrame &sscop_uu_)
{
    pfUlong pads = padPDU(sscop_uu_);
    appendReserved(sscop_uu_, 4);
    appendTypeOctet(sscop_uu_, sscopBGREJpdu, pads);
    appendReserved(sscop_uu_, 3);
    return;
}


pfUlong sscopBGREJ_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopBGREJpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        removeReserved(_frame, 3);
        pfByte pads = readPadLength(_frame.getLast());
        removeReserved(_frame, 4 + pads);
    }
    return error;
}


void sscopBGREJ_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopBGREJpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------


void sscopEND_PDU :: encode(pfFrame &sscop_uu_, pfUlong source_)
{
    pfUlong pads = padPDU(sscop_uu_);
    appendReserved(sscop_uu_, 4);
    pfByte byte = (pfByte) (sscopENDpdu & B0000_1111);
    byte |= (pfByte) ((source_ & B0000_0001) << 4);
    byte |= (pfByte) ((pads & B0000_0011) << 6);
    sscop_uu_.putLast(byte);
    appendReserved(sscop_uu_, 3);
    return;
}


pfUlong sscopEND_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopENDpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        removeReserved(_frame, 3);
        pfByte typeOctet = _frame.getLast();
        pfByte pads = readPadLength(typeOctet);
        _source = (typeOctet & 0x10) >> 4;
        removeReserved(_frame, 4 + pads);
    }
    return error;
}


sscopEND_PDU :: sscopEND_PDU(void)
    : sscopPDUmessenger(),
      _source(SSCOP_RELEASE_SSCOP)
{
    return;
}


sscopEND_PDU :: sscopEND_PDU(const sscopEND_PDU &other_)
    : sscopPDUmessenger(other_),
      _source(other_._source)
{
    return;
}


const sscopEND_PDU &sscopEND_PDU :: operator=(const sscopEND_PDU &other_)
{
    sscopPDUmessenger::operator=(other_);
    _source = other_._source;
    return *this;
}


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


void sscopEND_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopENDpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//
// Function: sscopEND_PDU :: getSource()
//
// Description:
//     Returns the value of "Source" field that is used only in END PDU.
//

pfUlong sscopEND_PDU :: getSource(void) const
{
    return _source;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopENDAK_PDU :: sscopENDAK_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopENDAK_PDU :: encode(pfFrame &frame_)
{
    appendReserved(frame_, 4);
    appendTypeOctet(frame_, sscopENDAKpdu);
    appendReserved(frame_, 3);
    return;
}


pfUlong sscopENDAK_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopENDAKpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    return error;
}


void sscopENDAK_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopENDAKpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopRS_PDU :: sscopRS_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopRS_PDU :: encode(pfFrame &sscop_uu_, pfUlong n_sq_, pfUlong n_mr_)
{
    pfUlong pads = padPDU(sscop_uu_);
    appendReserved(sscop_uu_, 3);
    sscop_uu_.putLast((pfByte) n_sq_);
    appendTypeOctet(sscop_uu_, sscopRSpdu, pads);
    sscop_uu_.putLast24bit(n_mr_);
    return;
}


pfUlong sscopRS_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopRSpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
        pfByte pads = readPadLength(_frame.getLast());
        _N_SQ = (pfUlong) _frame.getLast();
        removeReserved(_frame, 3 + pads);
    }
    return error;
}


void sscopRS_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopRSpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopRSAK_PDU :: sscopRSAK_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopRSAK_PDU :: encode(pfFrame &frame_, pfUlong n_mr_)
{
    appendReserved(frame_, 4);
    appendTypeOctet(frame_, sscopRSAKpdu);
    frame_.putLast24bit(n_mr_);
    return;
}


pfUlong sscopRSAK_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopRSAKpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
    }
    return error;
}


void sscopRSAK_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopRSAKpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopER_PDU :: sscopER_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopER_PDU :: encode(pfFrame &frame_, pfUlong n_sq_, pfUlong n_mr_)
{
    appendReserved(frame_, 3);
    frame_.putLast((pfByte) n_sq_);
    appendTypeOctet(frame_, sscopERpdu);
    frame_.putLast24bit(n_mr_);
    return;
}


pfUlong sscopER_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopERpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
        _frame.getLast();
        _N_SQ = (pfUlong) _frame.getLast();
    }
    return error;
}


void sscopER_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);    
    state->sscopERpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopERAK_PDU :: sscopERAK_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopERAK_PDU :: encode(pfFrame &frame_, pfUlong n_mr_)
{
    appendReserved(frame_, 4);
    appendTypeOctet(frame_, sscopERAKpdu);
    frame_.putLast24bit(n_mr_);
    return;
}


pfUlong sscopERAK_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopERAKpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_MR = _frame.getLast24bit();
    }
    return error;
}


void sscopERAK_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopERAKpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopSD_PDU :: sscopSD_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopSD_PDU :: encode(pfFrame &data_, pfUlong n_s_)
{
    pfUlong pads = padPDU(data_);
    appendTypeOctet(data_, sscopSDpdu, pads);
    data_.putLast24bit(n_s_);
    return;
}


pfUlong sscopSD_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopSDpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_S = _frame.getLast24bit();
        pfByte pads = readPadLength(_frame.getLast());
        removeReserved(_frame, pads);
    }
    return error;
}


void sscopSD_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopSDpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//
// Function: sscopSD_PDU :: getSequenceNumber()
//
// Description:
//     Returns the sequence number of the SD pdu. Does the same as
//     getN_S() method.
//

pfUlong sscopSD_PDU :: getSequenceNumber(void) const
{
    return _N_S;
}


//
// Function: sscopSD_PDU :: setSequenceNumber()
//
// Description:
//     SD PDUs that have been sent but not yet acknowledged get saved
//     into transmission buffer with their sequence number. Set method
//     is defined, because in this phase the sequence number is not
//     set by decode() method as usually with PDU fields.
//

void sscopSD_PDU :: setSequenceNumber(pfUlong sequenceNumber_)
{
    _N_S = sequenceNumber_;
    return;
}


//
// Function: sscopSD_PDU :: setN_PS()
//
// Description:
//     SD PDUs in the transmission buffer also have corresponding N_PS
//     field set (although this is not present in the actual PDU frame)
//     so set method for it is necessary.
//

void sscopSD_PDU :: setN_PS(pfUlong pollSequenceNumber_)
{
    _N_PS = pollSequenceNumber_;
    return;
}


//
// Function: sscopSD_PDU :: setInformation()
//
// Description:
//     Finally, also set method for information field is defined, because
//     the SD PDUs in transmission buffer are not created by create()
//     method, but "by hand".
//

void sscopSD_PDU :: setInformation(const pfFrame &frame_)
{
    _frame = frame_;
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopPOLL_PDU :: sscopPOLL_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopPOLL_PDU :: encode(pfFrame &frame_, pfUlong n_ps_, pfUlong n_s_)
{
    appendReserved(frame_, 1);
    frame_.putLast24bit(n_ps_);
    appendTypeOctet(frame_, sscopPOLLpdu);
    frame_.putLast24bit(n_s_);
    return;
}


pfUlong sscopPOLL_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopPOLLpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_S = _frame.getLast24bit();
        _frame.getLast();
        _N_PS = _frame.getLast24bit();
    }
    return error;
}


void sscopPOLL_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopPOLLpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------


void sscopSTAT_PDU :: encode(
    pfFrame &list_,
    pfUlong n_ps_,
    pfUlong n_mr_,
    pfUlong n_r_)
{
    appendReserved(list_, 1);
    list_.putLast24bit(n_ps_);
    appendReserved(list_, 1);
    list_.putLast24bit(n_mr_);
    appendTypeOctet(list_, sscopSTATpdu);
    list_.putLast24bit(n_r_);
    return;
}


pfUlong sscopSTAT_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopSTATpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_R = _frame.getLast24bit();
        _frame.getLast();
        _N_MR = _frame.getLast24bit();
        _frame.getLast();
        _N_PS = _frame.getLast24bit();
        _frame.getLast();

        // Each list element takes 4 octets and the PDU is aligned
        // to 4 octets, so plain division by 4 gives the right number
        _nItems = (_frame.length() / 4);
    }
    return error;
}


sscopSTAT_PDU :: sscopSTAT_PDU(void)
    : sscopPDUmessenger(),
      _nItems(0)
{
    return;
}


sscopSTAT_PDU :: sscopSTAT_PDU(const sscopSTAT_PDU &other_)
    : sscopPDUmessenger(other_),
      _nItems(other_._nItems)
{
    return;
}


const sscopSTAT_PDU &sscopSTAT_PDU :: operator=(const sscopSTAT_PDU &other_)
{
    sscopPDUmessenger::operator=(other_);
    _nItems = other_._nItems;
    return *this;
}


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


void sscopSTAT_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopSTATpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//
// Function: sscopSTAT_PDU :: getNItems
//
// Description:
//     Returns the number of list elemenst in STAT PDU. Value is
//     initially set by decode() and updated by getNextListElement.
//

pfUlong sscopSTAT_PDU :: getNItems(void) const
{
    return _nItems;
}


//
// Function: sscopSTAT_PDU :: getNextListElement
//
// Description:
//     Removes one list element from the HEAD of the STAT PDU frame
//     and returns its value. If there are no more elements, return 0.
//

pfUlong sscopSTAT_PDU :: getNextListElement(void)
{
    pfUlong element = 0;
    if (_nItems > 0)
    {
        _frame.getFirst();  // Remove padding octet
        element = _frame.getFirst24bit();
        _nItems--;
    }
    return element;
}


//----------------------------------------------------------------------


void sscopUSTAT_PDU :: encode(
    pfFrame &frame_,
    pfUlong listElement1_,
    pfUlong listElement2_,
    pfUlong n_mr_,
    pfUlong n_r_)
{
    appendReserved(frame_, 1);
    frame_.putLast24bit(listElement1_);
    appendReserved(frame_, 1);
    frame_.putLast24bit(listElement2_);
    appendReserved(frame_, 1);
    frame_.putLast24bit(n_mr_);
    appendTypeOctet(frame_, sscopUSTATpdu);
    frame_.putLast24bit(n_r_);
    return;
}


pfUlong sscopUSTAT_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() != sscopUSTATpdu_ABSLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        _N_R = _frame.getLast24bit();
        _frame.getLast();
        _N_MR = _frame.getLast24bit();
        _frame.getLast();
        _listElement2 = _frame.getLast24bit();
        _frame.getLast();
        _listElement1 = _frame.getLast24bit();
    }
    return error;
}


sscopUSTAT_PDU :: sscopUSTAT_PDU(void)
    : sscopPDUmessenger(),
      _listElement1(0),
      _listElement2(0)
{
    return;
}


sscopUSTAT_PDU :: sscopUSTAT_PDU(const sscopUSTAT_PDU &other_)
    : sscopPDUmessenger(other_),
      _listElement1(other_._listElement1),
      _listElement2(other_._listElement2)
{
    return;
}


const sscopUSTAT_PDU &
sscopUSTAT_PDU :: operator=(const sscopUSTAT_PDU &other_)
{
    sscopPDUmessenger::operator=(other_);
    _listElement1 = other_._listElement1;
    _listElement2 = other_._listElement2;
    return *this;
}


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


void sscopUSTAT_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopUSTATpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//
// Functions: sscopUSTAT_PDU :: getListElement1 and getListElement2
//
// Description:
//     Return the values of USTAT PDU list elements.
//

pfUlong sscopUSTAT_PDU :: getListElement1(void) const
{
    return _listElement1;
}


pfUlong sscopUSTAT_PDU :: getListElement2(void) const
{
    return _listElement2;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopUD_PDU :: sscopUD_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopUD_PDU :: encode(pfFrame &data_)
{
    pfUlong pads = padPDU(data_);
    appendTypeOctet(data_, sscopUDpdu, pads);
    appendReserved(data_, 3);
    return;
}


pfUlong sscopUD_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopUDpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        removeReserved(_frame, 3);
        pfByte pads = readPadLength(_frame.getLast());
        removeReserved(_frame, pads);
    }
    return error;
}


void sscopUD_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopUDpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopMD_PDU :: sscopMD_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


void sscopMD_PDU :: encode(pfFrame &data_)
{
    pfUlong pads = padPDU(data_);
    appendTypeOctet(data_, sscopMDpdu, pads);
    appendReserved(data_, 3);
    return;
}


pfUlong sscopMD_PDU :: decode(void)
{
    pfUlong error = SSCOP_PDU_OK;
    if (_frame.length() < sscopMDpdu_MINLEN)
    {
        error = SSCOP_PDU_INVALID_LENGTH;
    }
    else
    {
        removeReserved(_frame, 3);
        pfByte pads = readPadLength(_frame.getLast());
        removeReserved(_frame, pads);
    }
    return error;
}


void sscopMD_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopMDpduAct(this, (sscopProtocol *) protocol_);
    return;
}


//----------------------------------------------------------------------

// MODIFIED: jturunen
sscopINVALID_PDU :: sscopINVALID_PDU(void)
    : sscopPDUmessenger()
{
    return;
}


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


pfUlong sscopINVALID_PDU :: decode()
{
    // No actual decoding, just return error.
    return SSCOP_PDU_OTHER_ERROR;
}


void sscopINVALID_PDU :: atProtocol(pfProtocol *protocol_, pfState *state_)
{
    sscopState *state = dynamic_cast<sscopState *>(state_);
    THROW_IF_DYNAMIC_CAST_FAILED(state);
    state->sscopINVALIDpduAct(this, (sscopProtocol *) protocol_);
    return;
}





