//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / SCCP protocol
//
//File: sccppdus.h
//
//Version: $Revision: 1.19 $
//
//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/frame.h"
#include "pf/protocol.h"
#include "pf/bytes.h"
#include "pf/debug.h"
#include "sccpdefs.h"
#include "sccpstate.h"
#include "iface/mtpif/mtpupprimitives.h"
#include "iface/sccpif/sccpdownprimitives.h"
#include "sccppdus.h"

//
//Function: createPdu
//
//Description:
//    Static method to create a PDU when a primitive is
//    received from MTP3 layer.
//

sccpPdu *sccpPdu :: createPdu(mtpTRANSFERind *messenger_)
{
    assert(messenger_ != 0);
    sccpPdu *pdu = 0;
    pfFrame frame = messenger_->getSignallingInformation();

    if (frame.length() < 1)
    {
        debugUser("--SCCP pdu too short--: discard");
    }
    else
    {
        sccpPduType messageType = frame.getFirst();
        switch(messageType)
        {
            case sccpUDT_PDU :
                pdu = new sccpUDTpdu;
                break;
            default :
                debugUser("--Invalid SCCP message type--: discard");
        }
        if (pdu != 0)
        {
            pdu->_frame = frame;
            pdu->setInteger("destinationPointCode",
                            messenger_->getDestinationPointCode());
            pdu->setInteger("originatingPointCode",
                            messenger_->getOriginatingPointCode());
            pdu->setInteger("signallingLinkSelection",
                            messenger_->getSignallingLinkSelection());
            if (pdu->decode() != 0)
            {
	        debugUser("--decode error--: discard");
                delete pdu;
                pdu = 0;
            }
            
        }
    }
    // Null pointer is returned in error case !!
    return pdu;
}

sccpPdu :: sccpPdu(void)
    : pfMessenger(),
      _frame()
{
    defineInteger("destinationPointCode");
    defineInteger("originatingPointCode");
    defineInteger("signallingLinkSelection");
    return;
}

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

//
//Function: routeOnGT
//
//Description:
//    Check a routing indicator field in called adderess
//    to see is routing based on global title.
//
        
bool sccpPdu :: routeOnGT(void)
{
    bool result = false;
    pfStorage calledAddress = getStorage("calledAddress");
    if (calledAddress.getInteger("routingIndicator") == 0)
    {
        result = true;
    }
    return result;
}

//
//Function: checkMandatoryVariableField
//
//Description:
//    Method checks is there enough bytes for mandatory variable field
//    is frame. It also verify boundaries.
//    Default values (0) for min and max indicates no limitations.
//

bool sccpPdu :: checkMandatoryVariableField(pfUlong start_,
                                                 pfUlong &variableLength_,
                                                 pfUlong min_,
                                                 pfUlong max_)    
{
    bool errors = true;
    pfUlong frameLength = _frame.length();
    if (frameLength > start_)
    {
        variableLength_ = _frame.read(start_);
        if (variableLength_ >= min_ && (variableLength_ <= max_ || max_ == 0))
        {
            if (frameLength >= (start_ + variableLength_ + 1))
            {
                errors = false;
            }
        }
    }
    return errors;
}

//
//Functions: decodeAddress and encodeAddress
//
//Description:
//    These methods are used to encode/decode SCCP calling
//    and called addresses.
//

pfStorage sccpPdu :: decodeAddress(pfUlong offSet_, pfUlong length_)
{
    pfStorage address;
    address.defineInteger("routingIndicator");
    address.defineInteger("pointCode");
    address.defineInteger("subSystemNumber");
    address.defineString("globalTitle");
    
    length_--;
    pfByte addressIndicator = _frame.read(offSet_++);

    address.setInteger("routingIndicator",
                       (addressIndicator & B0100_0000));

    if ((addressIndicator & B0000_0001) != 0)
    {
        pfUlong pointCode = _frame.read(offSet_++);
        pointCode += (_frame.read(offSet_++)) << 8;
        address.setInteger("pointCode", pointCode);
        length_-= 2;
    }
    if ((addressIndicator & B0000_0010) != 0)
    {
        pfUlong subSystemNumber = _frame.read(offSet_++);
        address.setInteger("subSystemNumber", subSystemNumber);
        length_--;
    }
    // Global title indicator = 0010 is only supportred type.
    // 0010 indicates that global title includes translation type only
    if ((addressIndicator & B0011_1100) != B0000_0010) 
    {
        string globalTitle;
        length_--;
        // Translation type is not used (skipped)
        _frame.read(offSet_++);

        for (pfUlong i=0; i<length_; i++)
        {
            globalTitle.append(1, _frame.read(offSet_++));
        }
        address.setString("globalTitle", globalTitle);
    }
    return address;
}

void sccpPdu :: encodeAddress(pfStorage &address_)
{
    pfByte addressIndicator = 0;
    if (address_.isValuePresent("globalTitle") != 0)
    {
        // Global title indicator = 0010 is only supportred type.
        // 0010 indicates that global title includes translation type only
        
        addressIndicator = B0000_1000;
        string GT = address_.getString("globalTitle");
        string::iterator i = GT.end();
        while (--i != GT.begin()-1)
        {
            _frame.putFirst(*i);
        }
        // Translation type is not used (coded 00000000)
        _frame.putFirst(0);
    }
    if (address_.isValuePresent("subSystemNumber") != 0)
    {
        // SSN indicator 
        addressIndicator += B0000_0010;
	pfUlong SSN = address_.getInteger("subSystemNumber");
	if (SSN != 0)
	{
            // Routing indicator (SSN routing)
            addressIndicator += B0100_0000;
        }
        _frame.putFirst(SSN);
    }
    if (address_.isValuePresent("pointCode") != 0)
    {
        addressIndicator += B0000_0001;
        pfUlong pointCode = address_.getInteger("pointCode");
        _frame.putFirst(pointCode & 0xFF);
        _frame.putFirst(pointCode >> 8);
    }
    _frame.putFirst(addressIndicator);
    return;
}

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

sccpUDTpdu *sccpUDTpdu :: createUDTpdu(sccpN_UNITDATAreq *messenger_)
{
    sccpUDTpdu *pdu = new sccpUDTpdu;
    
    pfStorage calledAddress = messenger_->getCalledAddress();
    pfStorage callingAddress = messenger_->getCallingAddress();
    
    pdu->setStorage("calledAddress", calledAddress);
    pdu->setStorage("callingAddress", callingAddress);
    
    pfFrame frame = messenger_->getUserData(); 
    pdu->setFrame("userData", frame);
    pdu->setInteger("protocolClass", PROTOCOL_CLASS);
    return pdu;
}
    
sccpUDTpdu :: sccpUDTpdu(void)
    : sccpPdu()
{
    defineInteger("protocolClass");
    defineStorage("calledAddress");
    defineStorage("callingAddress");
    defineFrame("userData");
    return;
}

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

void sccpUDTpdu :: apply(pfState *state_, pfProtocol *protocol_)
{
    sccpState *state = dynamic_cast<sccpState *>(state_);
    assert(state != 0);
    state->sccpUDTpduAct(this, protocol_);
};

//
//Functions: decode and encode
//
//Description:
//    Encodes and decodes Unitdata (UDT) PDUs.
//

bool sccpUDTpdu :: decode(void)
{
    bool errors = false;
    
    if (_frame.length() < 12)
    {
        debugUser("--UDT pdu too short--: discard");
        errors = true;
    }
    else
    {
        pfUlong protocolClass = _frame.getFirst();
        // bits 1-4 indicates protocol class and
        // bits 5-8 indicates message handling option (not supported)
        setInteger("protocolClass", (protocolClass & 0xF));

        pfUlong calledAddressPointer = _frame.getFirst();
        pfUlong callingAddressPointer = _frame.getFirst();
        pfUlong userDataPointer = _frame.getFirst();
        
        pfUlong length = 0;
        pfUlong offSet = calledAddressPointer - 3;
        
        // calledAddress
        if (checkMandatoryVariableField(offSet, length, 3) != 0)
        {
            errors = true;
        }
        else
        {
            offSet++;
            pfStorage address = decodeAddress(offSet, length);
            setStorage("calledAddress", address);
            
            offSet = callingAddressPointer - 2;
            if (checkMandatoryVariableField(offSet, length, 2) != 0)
            {
                errors = true;
            }
            else
            {
                offSet++;
                pfStorage address = decodeAddress(offSet, length);
                setStorage("callingAddress", address);
                
                offSet = userDataPointer - 1;
                if (checkMandatoryVariableField(offSet, length) != 0)
                {
                    errors = true;
                }
                else
                {
                    // NOTE: User data length is coded unstandardized way.
                    //       Length should be coded to one octet 
                    //       (max length 255). Here length is coded 
                    //       using 16 bit. This is done because TCAP messages
                    //       with INAP operations could exceed standardized
                    //       length. Segmentation and reassembly should be
                    //       implement to solve this problem.
                    //       
                    //
                    // Start unstandardized...
                    //
                    offSet++;
                    pfUlong lengthSecondOctet = _frame.read(offSet); 
                    length = (length << 8) + lengthSecondOctet;
                    //
                    // ...end unstandardized

                    offSet++;
                    pfFrame userData = _frame.getSubFrame(offSet, length);
                    setFrame("userData", userData);
                }
            }
        }
    }
    return errors;
}

pfFrame sccpUDTpdu :: encode(void)
{
    pfFrame frame;
    _frame = frame;

    pfUlong calledAddressPointer;
    pfUlong callingAddressPointer;
    pfUlong userDataPointer;

    pfUlong counter = 0;
    pfUlong length = 0;

    pfFrame userData = getFrame("userData");
    pfUlong dataLength = userData.length();
    for (pfUlong i=0; i<dataLength; i++)
    {
        _frame.putFirst(userData.getLast());
    }

    // NOTE: User data length is coded unstandardized way.
    //       Length should be coded to one octet (max length 255).
    //       Here length is coded using 16 bit.
    //       This is done because TCAP messages
    //       with INAP operations could exceed standardized
    //       length. Segmentation and reassembly should be
    //       implement to solve this problem.

    _frame.putFirst(dataLength & 0xFF);
    _frame.putFirst((dataLength >> 8) & 0xFF);

    //
    // Standardized
    //
    // _frame.putFirst(dataLength);
    //

    counter = _frame.length();
    pfStorage callingAddress = getStorage("callingAddress");
    encodeAddress(callingAddress);
    length = _frame.length() - counter;
    _frame.putFirst(length);

    userDataPointer = length;
    
    counter = _frame.length();
    pfStorage calledAddress = getStorage("calledAddress");
    encodeAddress(calledAddress);
    length = _frame.length() - counter;
    _frame.putFirst(length);

    calledAddressPointer = 3;
    callingAddressPointer = calledAddressPointer + length;
    userDataPointer += callingAddressPointer;
    
    _frame.putFirst(userDataPointer);
    _frame.putFirst(callingAddressPointer);
    _frame.putFirst(calledAddressPointer);
    
    _frame.putFirst(getInteger("protocolClass"));
    _frame.putFirst(B0000_1001);

    return _frame;
}


