//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / testing/testadapter/sscop
//
//File: sscoppducoder.cpp
//
//Version: $Revision: 1.37 $
//
//State: $State: Exp $
//
//Date: $Date: 1998/11/30 13:46:17 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//      Timo Kokkonen
//      Jussi Turunen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History:
//
#include "pf/bytes.h"
#include "pf/debug.h"

#include "sscoppducoder.h"

sscopPDUcoder :: sscopPDUcoder(void)
    : pduCoderBase()
{
    return;
}


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

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

//
//Function: decode
//
//Description:
//
//      A general decode method for SSCOP PDUs.
//      Specific decodeXXpdu is called from this method based on
//      the PDU type. Invalid PDU is handled in a separate method.
//

otMessage::SerializedMessage sscopPDUcoder :: decode(const pfFrame &frame_)
{
    try
    {
        debugFrame("decoded frame", frame_);

        frameToDataBuffer(frame_);

        // Header is the 4th octet from the end of pdu and it 32 bits long
        pfByte header = _dataBuffer.readBits(_dataBuffer.length() - 32, 8); 

        // pduType is 4 bits long.
        pfUlong pduType = B0000_1111 & header; //_dataBuffer.readBits(4, 4);

        switch (pduType)
        {
            case B0000_0001: // BGN
                decodeBGNpdu();
                break;
            case B0000_0010: // BGAK
                decodeBGAKpdu();
                break;
            case B0000_0011: // END
                decodeENDpdu();
                break;
            case B0000_0100: // ENDAK
                decodeENDAKpdu();
                break;
            case B0000_0101: // RS
                decodeRSpdu();
                break;
            case B0000_0110: // RSAK
                decodeRSAKpdu();
                break;
            case B0000_0111: // BGREJ
                decodeBGREJpdu();
                break;
            case B0000_1000: // SD
                decodeSDpdu();
                break;
            case B0000_1001: // ER
                decodeERpdu();
                break;
            case B0000_1010: // POLL
                decodePOLLpdu();
                break;
            case B0000_1011: // STAT
                decodeSTATpdu();
                break;
            case B0000_1100: // USTAT
                decodeUSTATpdu();
                break;
            case B0000_1101: // UD
                decodeUDpdu();
                break;
            case B0000_1110: // MD
                decodeMDpdu();
                break;
            case B0000_1111: // ERAK
                decodeERAKpdu();
                break;
            default:
                unknownPdu(); // type 0000
                break;
        }
    }
    catch (pfException &except)
    {
        string name = except.getName();
        debugString("Exception caught", name);
    }

    return _message;
}

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

//
//Function: decodeBGNpdu
//
//Description:
//      Decodes sscop BGN pdu.
//
// Type Name Length 
// Octetstring   UU          0...UU_Max_Len
// Octetstring   PAD         0...3
// Octetstring   RESERVED    3
// BitString     N_SQ        8
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Bitstring     N_MR        24

void sscopPDUcoder :: decodeBGNpdu(void)
{
    debugUser("decodeBGN");

    storePDUname("BGN");

    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - N_MRLength - headerLength - N_SQLength - RESERVEDLength
    // - PADLength.
    pfUlong UULength =
        _dataBuffer.length()/OCTETSIZE - 3 - 1 - 1 - 3 - padLength;

    decodeOctetString("UU", UULength);
    decodeOctetString("PAD", padLength);
    decodeOctetString("RESERVED", 3);
    decodeBitString("N_SQ", 8);
    decodeHeaderType1("RSVD");
    decodeBitString("N_MR", 24);

    return;
}

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

//
//Function: decodeBGAKpdu
//
//Description:
//      Decodes sscop BGAK pdu.
//
// Type Name Length 
// Octetstring   UU          0...UU_Max_Len
// Octetstring   PAD         0...3
// Octetstring   RESERVED    4
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Bitstring     N_MR        24

void sscopPDUcoder :: decodeBGAKpdu(void)
{
    debugUser("decodeBGAK");
    storePDUname("BGAK");
    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - N_MRLength - headerLength - RESERVEDLength - PADLength.
    pfUlong UULength = _dataBuffer.length()/OCTETSIZE - 3 - 1 - 4 - padLength;

    decodeOctetString("UU", UULength);
    decodeOctetString("PAD", padLength);
    decodeOctetString("RESERVED", 4);
    decodeHeaderType1("RSVD");
    decodeBitString("N_MR", 24);

    return;
}

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

//
//Function: decodeENDpdu
//
//Description:
//      Decodes sscop END pdu.
//
// Type Name Length 
// Octetstring   UU          0...UU_Max_Len
// Octetstring   PAD         0...3
// Octetstring   RESERVED1   4
// BitString     PL          2
// BitString     RR          1
// BitString     S           1
// Bitstring     PDU_Type    4
// Octetstring   RESERVED2   3

void sscopPDUcoder :: decodeENDpdu(void)
{
    debugUser("decodeEND");
    // END1 since END is a reserved word in TTCN
    storePDUname("END1");

    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - RESERVED2Length - headerLength
    // - RESERVED1Length - PADLength.
    pfUlong UULength =
        _dataBuffer.length()/OCTETSIZE - 3 - 1 - 4 - padLength;

    decodeOctetString("UU", UULength);
    decodeOctetString("PAD", padLength);
    decodeOctetString("RESERVED1", 4);

    string PL = getBitField(2);
    string rsvd = getBitField(1);
    string s = getBitField(1);
    string type = getBitField(4);

    insertToMessage("PL", PL, otMessage::BitString);
    insertToMessage("RR", rsvd, otMessage::BitString);
    insertToMessage("S", s, otMessage::BitString);
    insertToMessage("PDU_Type", type, otMessage::BitString);
    decodeOctetString("RESERVED2", 3);

    return;
}

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

//
//Function: decodeENDAKpdu
//
//Description:
//      Decodes sscop ENDAK pdu.
//
// Type Name Length 
// Octetstring   RESERVED1   4
// Bitstring     RESERVED2   4
// Bitstring     PDU_Type    4
// OctetString   RESERVED3   3

void sscopPDUcoder :: decodeENDAKpdu(void)
{
    debugUser("decodeENDAK");
    storePDUname("ENDAK");
    // frameLength - reserved3Length - headerLength.
    pfUlong Reserved1Length = _dataBuffer.length()/OCTETSIZE - 3 - 1;

    decodeOctetString("RESERVED1", Reserved1Length);
    decodeHeaderType2("RESERVED2");
    decodeOctetString("RESERVED3", 3);

    return;
}

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

//
//Function: decodeRSpdu
//
//Description:
//      Decodes sscop RS pdu.
//
// Type Name Length 
// Octetstring   UU          0...UU_Max_Len
// Octetstring   PAD         0...3
// Octetstring   RESERVED    3
// BitString     N_SQ        8
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Bitstring     N_MR        24

void sscopPDUcoder :: decodeRSpdu(void)
{
    debugUser("decodeRS");
    storePDUname("RS");

    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - N_MRLength - headerLength - N_SQLength - RESERVEDLength
    // - PADLength.
    pfUlong UULength =
        _dataBuffer.length()/OCTETSIZE - 3 - 1 - 1 - 3 - padLength;

    decodeOctetString("UU", UULength);
    decodeOctetString("PAD", padLength);
    decodeOctetString("RESERVED", 3);
    decodeBitString("N_SQ", 8);
    decodeHeaderType1("RSVD");
    decodeBitString("N_MR", 24);

    return;
}

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

//
//Function: decodeRSAKpdu
//
//Description:
//      Decodes sscop RSAK pdu.
//
// Type Name Length 
// Octetstring   RESERVED1   4
// Bitstring     RESERVED2   4
// Bitstring     PDU_Type    4
// BitString     N_MR        24

void sscopPDUcoder :: decodeRSAKpdu(void)
{
    debugUser("decodeRSAK");
    storePDUname("RSAK");
    // frameLength - N_MRLength - headerLength.
    pfUlong Reserved1Length = _dataBuffer.length()/OCTETSIZE - 3 - 1;

    decodeOctetString("RESERVED1", Reserved1Length);
    decodeHeaderType2("RESERVED2");
    decodeBitString("N_MR", 24);

    return;
}

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

//
//Function: decodeBGREJpdu
//
//Description:
//      Decodes sscop BGREJ pdu.
//
// Type Name Length 
// Octetstring   UU          0...UU_Max_Len
// Octetstring   PAD         0...3
// Octetstring   RESERVED1   4
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Octetstring   RESERVED2   3

void sscopPDUcoder :: decodeBGREJpdu(void)
{
    debugUser("decodeBGREJ");
    storePDUname("BGREJ");

    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - RESERVED2Length - headerLength - RESERVED1Length
    // - PADLength.
    pfUlong UULength = _dataBuffer.length()/OCTETSIZE - 3 - 1 - 4 - padLength;

    decodeOctetString("UU", UULength);
    decodeOctetString("PAD", padLength);
    decodeOctetString("RESERVED1", 4);
    decodeHeaderType1("RSVD");
    decodeOctetString("RESERVED2", 3);

    return;
}

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

//
//Function: decodeSDpdu
//
//Description:
//      Decodes sscop SD pdu.
//
// Type Name Length 
// Octetstring   Information 0...Info_Max_Len
// Octetstring   PAD         0...3
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Bitstring     N_S         24

void sscopPDUcoder :: decodeSDpdu(void)
{
    debugUser("decodeSD");
    storePDUname("SD");
    // Gets header information from _dataBuffer.
    // The order is PL, rsvd and type

    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - N_SLength - headerLength - PADLength.
    pfUlong infoLength = _dataBuffer.length()/OCTETSIZE - 3 - 1 - padLength;

    decodeOctetString("Information", infoLength);
    decodeOctetString("PAD", padLength);
    decodeHeaderType1("RSVD");
    decodeBitString("N_S", 24);

    return;
}

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

//
//Function: decodeERpdu
//
//Description:
//      Decodes sscop ER pdu.
//
// Type Name Length 
// Octetstring   RESERVED    3
// Bitstring     N_SQ        8
// Bitstring     RSVD        4
// Bitstring     PDU_Type    4
// Bitstring     N_MR        24

void sscopPDUcoder :: decodeERpdu(void)
{
    debugUser("decodeER");
    storePDUname("ER");
    // frameLength - N_MRLength - headerLength - N_SQLength
    pfUlong ReservedLength = _dataBuffer.length()/OCTETSIZE - 3 - 1 - 1;

    decodeOctetString("RESERVED", ReservedLength);
    decodeBitString("N_SQ", 8);
    decodeHeaderType2("RSVD");
    decodeBitString("N_MR", 24);

    return;
}

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

//
//Function: decodeEPOLLpdu
//
//Description:
//      Decodes sscop POLL pdu.
//
// Type Name Length 
// Octetstring   RESERVED1   1
// Bitstring     N_PS        24
// Bitstring     RESERVED2   4
// Bitstring     PDU_Type    4
// Bitstring     N_S         24

void sscopPDUcoder :: decodePOLLpdu(void)
{
    debugUser("decodePOLL");
    storePDUname("POLL");

    // frameLength - N_PSLength - headerLength - N_SLength
    pfUlong Reserved1Length = _dataBuffer.length()/OCTETSIZE - 3 - 1 - 3;

    decodeOctetString("RESERVED1", Reserved1Length);
    decodeBitString("N_PS", 24);
    decodeHeaderType2("RESERVED2");
    decodeBitString("N_S", 24);

    return;
}

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

//
//Function: decodeSTATpdu
//
//Description:
//      Decodes sscop STAT pdu.
//
// Type Name Length 
// LIST_TYPE     LIST        0...L
// Octetstring   RSVD1       1
// Bitstring     N_PS        24
// Octetstring   RSVD2       1
// Bitstring     N_MR        24
// Bitstring     RESERVED    4
// Bitstring     PDU_Type    4
// Bitstring     N_R         24

void sscopPDUcoder :: decodeSTATpdu(void)
{
    debugUser("decodeSTAT");
    storePDUname("STAT");

    // frameLength - RSVD1Length - N_PSLength - RSVD2Length - N_MRLength
    // - headerLength - N_RLength
    pfLong listLength =
        _dataBuffer.length()/OCTETSIZE - 1 - 3 - 1 - 3 - 1 - 3;

    // list is decoded always. listLength is bytes.
    decodeListElement(listLength);
    
    decodeOctetString("RSVD1", 1);
    decodeBitString("N_PS", 24);
    decodeOctetString("RSVD2", 1);
    decodeBitString("N_MR", 24);
    decodeHeaderType2("RESERVED");
    decodeBitString("N_R", 24);

    return;
}

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

//
//Function: decodeUSTATpdu
//
//Description:
//      Decodes sscop USTAT pdu.
//
// Type Name Length 
// Octetstring   PAD1        1
// Bitstring     LE1         24
// Octetstring   PAD2        1
// Bitstring     LE2         24
// Octetstring   RESERVED1   1
// Bitstring     N_MR        24
// Bitstring     RESERVED2   4
// Bitstring     PDU_Type    4
// Bitstring     N_R         24

void sscopPDUcoder :: decodeUSTATpdu(void)
{
    debugUser("decodeUSTAT");
    storePDUname("USTAT");
    // frameLength - LE1Length - PAD2Length - LE2Length - RESERVED1Length
    // - N_MRLength - headerLength - N_RLength
    pfUlong PAD1Length =
        _dataBuffer.length()/OCTETSIZE - 3 - 1 - 3 - 1 - 3 - 1 - 3;

    // ++TODO++ list
    decodeOctetString("PAD1", PAD1Length);
    decodeBitString("LE1", 24);
    decodeOctetString("PAD2", 1);
    decodeBitString("LE2", 24);
    decodeOctetString("RESERVED1", 1);
    decodeBitString("N_MR", 24);
    decodeHeaderType2("RESERVED2");
    decodeBitString("N_R", 24);

    return;
}

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

//
//Function: decodeUDpdu
//
//Description:
//      Decodes sscop UD pdu.
//
// Type Name Length 
// Octetstring   Information 0...Info_Max_Len
// Octetstring   PAD         0...3
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Octetstring   RESERVED    3

void sscopPDUcoder :: decodeUDpdu(void)
{
    debugUser("decodeUD");
    storePDUname("UD");
    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - RESERVEDLength - headerLength - PADLength.
    pfUlong infoLength = _dataBuffer.length()/OCTETSIZE - 3 - 1 - padLength;

    decodeOctetString("Information", infoLength);
    decodeOctetString("PAD", padLength);
    decodeHeaderType1("RSVD");
    decodeOctetString("RESERVED", 3);

    return;
}

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

//
//Function: decodeMDpdu
//
//Description:
//      Decodes sscop MD pdu.
//
// Type Name Length 
// Octetstring   Information 0...Info_Max_Len
// Octetstring   PAD         0...3
// BitString     PL          2
// BitString     RSVD        2
// Bitstring     PDU_Type    4
// Octetstring   RESERVED    3

void sscopPDUcoder :: decodeMDpdu(void)
{
    debugUser("decodeMD");
    storePDUname("MD");
    pfUlong padLength = _dataBuffer.readBits(_dataBuffer.length()-32, 2);

    // frameLength - RESERVEDLength - headerLength - PADLength.
    pfUlong infoLength =
        _dataBuffer.length()/OCTETSIZE - 3 - 1 - padLength;

    decodeOctetString("Information", infoLength);
    decodeOctetString("PAD", padLength);
    decodeHeaderType1("RSVD");
    decodeOctetString("RESERVED", 3);

    return;
}

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

//
//Function: decodeERAKpdu
//
//Description:
//      Decodes sscop ERAK pdu.
//
// Type Name Length 
// Octetstring   RESERVED    4
// Bitstring     RSVD        4
// Bitstring     PDU_Type    4
// Bitstring     N_MR        24

void sscopPDUcoder :: decodeERAKpdu(void)
{
    debugUser("decodeERAK");
    storePDUname("ERAK");
    // frameLength - N_MRLength - headerLength
    pfUlong ReservedLength = _dataBuffer.length()/OCTETSIZE - 3 - 1;

    decodeOctetString("RESERVED", ReservedLength);
    decodeHeaderType2("RSVD");
    decodeBitString("N_MR", 24);

    return;
}


//
//Function: unknownPdu
//
//Description:
//      PDU type wasn't recognized. This method makes an unkown PDU
//      which is passed to SSCOP.
//
void sscopPDUcoder :: unknownPdu(void)
{
    debugUser("UNKNOWN");
    storePDUname("UNKNOWN_PDU");
    
    decodeOctetString("UNKNOWN_FIELD", _dataBuffer.length()/OCTETSIZE);

    return;
}

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

//
//Function: decodeListElement
//
//Description:
//      Decodes type LIST_TYPE. 'Extra' i.e. not 32-bit aligned
//      octets are added to last PAD element. listLength is the
//      length of the list bytes.
//
void sscopPDUcoder :: decodeListElement(pfLong listLength_)
{
    string padName("PAD");
    string leName("LE");
    string elemName("elem_");
    string elem=elemName;

    // if listLength is zero or somehow negative no decoding is done
    if (listLength_ > 0)
    {
        // ListElement is a structured element so BOE/EOE are needed
        decodeBegOfElement("LIST");

        // done here since ignore field is inside the LIST struct
        addIgnoredToMessage();

        pfUlong extraOctets = listLength_ % 4;
        pfLong elementsInList = listLength_ / 4;
        
        // initialized to 0 because elem_0 is the name of first element
        pfLong currentElement = 0;
        
        // elementsInList because the last element contains 
        // the extra octets (if any)
        while (currentElement < (elementsInList-1))
        {
            elem=elemName;
            addUlongToString(elem, currentElement);
            
            decodeBegOfElement(elem);
            decodeOctetString(padName, 1);
            decodeBitString(leName, 24);
            decodeEndOfElement(elem);
            
            ++currentElement;
        }
        
        // the extra octets go to the last LE
        elem=elemName;
        addUlongToString(elem, currentElement);
        
        decodeBegOfElement(elem);
        decodeOctetString(padName, (1 + extraOctets));
        decodeBitString(leName, 24);
        decodeEndOfElement(elem);
        
        if (listLength_ < 4)
        {
            printErrorMsg(
            "Attempting to decode a list element shorter than 4 bytes");
        }
        decodeEndOfElement("LIST");
    }

    return;
}


// Function: decodeHeaderType1
//
// Description:
// 
// Decodes SSCOP-PDU header from a frame based on the PDU Type.
//
// Header type 1: Pad length 2 bits
//                reserved   2 bits
//                PDU type   4 bits
//
// reservedFieldName is passed as a parameter because the field name
// varies from one PDU ton another
// NOTE!! PL and PDU_Type could be const strings in the class.
//
void sscopPDUcoder :: decodeHeaderType1(const string &reservedFieldName_)
{
    string PL = getBitField(2);
    string rsvd = getBitField(2);
    string type = getBitField(4);

    insertToMessage("PL", PL, otMessage::BitString);
    insertToMessage(reservedFieldName_, rsvd, otMessage::BitString);
    insertToMessage("PDU_Type", type, otMessage::BitString);

    return;
}

//
// Function: decodeHeaderType2
//
// Description:
//
// See description for decodeHeaderType1
//
// Header type 2: reserved   4 bits
//                PDU type   4 bits
//
//
void sscopPDUcoder :: decodeHeaderType2(const string &reservedFieldName_)
{
    string rsvd = getBitField(4);
    string type = getBitField(4);

    insertToMessage(reservedFieldName_, rsvd, otMessage::BitString);
    insertToMessage("PDU_Type", type, otMessage::BitString);

    return;
}


// Function: addIgnoredToMessage
//
// Description:
//      In SSCOP testing the tester sends fields where identifier_ begins
//      with "ignore". These fields need to be stored since the tester
//      expects them back in the same order they were sent.

void sscopPDUcoder :: addIgnoredToMessage(void)
{
    debugUser("Adding ignored fields to message.");

    insertToMessage("ignore", "", otMessage::BegOfElement);

    insertToMessage("ignore_1", "00", otMessage::OctetString);

    insertToMessage("ignore_2",
                    "000000000000000000000000",
                    otMessage::BitString);

    insertToMessage("ignore", "", otMessage::EndOfElement);

    return;
}

