//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / bisup
//
//File: bisupiecoders.cpp
//
//Version: $Revision: 1.35 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/15 13:21:02 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Author:
//      Timo Kokkonen
//      Sami Raatikainen
//
//Description:
//      See corresponding header file.
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History:
//

#include "bisupiecoders.h"
#include <autoptr.h>
#include "pf/messenge.h"
#include "pf/debug.h"
#include "pf/error.h"

#include "protocol/sig/sigexceptions.h"

#include "ie/iestrings.h"
#include "ie/aalparameters.h"
#include "ie/atmcellrate.h"
#include "ie/broadbandbearercapability.h"
#include "ie/callreference.h"
#include "ie/cause.h"
#include "ie/connectionidentifier.h"
#include "ie/partynumber.h"
#include "ie/transitnetworkselection.h"
#include "ie/transparent.h"


//-----------------------------------------------------------------------
//
//Class: bisupAALParametersIECoder
//
//Description:
//      First phase this is not implemented, transparent information instead
//

bisupAALParametersIECoder :: bisupAALParametersIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(0);
    return;
}

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

void bisupAALParametersIECoder :: decodeContent(
    pfFrame &,
    pfUlong,
    pfMessenger *)
{
    // ++TODO++
    return;
}

void bisupAALParametersIECoder :: encodeContent(
    pfFrame &,
    pfMessenger *)
{
    // ++TODO++
    return;
}
  

//-----------------------------------------------------------------------
//
//Class: bisupATMCellRateIECoder
//
//Description:
//      ATM cell rate.
//

bisupATMCellRateIECoder :: bisupATMCellRateIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(0);
    return;
}

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

void bisupATMCellRateIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong IElength_,
    pfMessenger *msg_)
{
    ieATMCellRate *ie = new ieATMCellRate;
    auto_ptr<pfIE> iePtr(ie);
    
    pfUlong forwardPCR0 = 0;
    pfUlong backwardPCR0 = 0;
    pfUlong forwardPCR1 = 0;
    pfUlong backwardPCR1 = 0;
    pfUlong cellRateID = 0;

    while (IElength_ > 0)
    {
        cellRateID = frame_.getFirst();
        --IElength_;

        switch (cellRateID)
        {
            case B1000_0010: // Forward Peak Cell Rate For CLP 0
                if (forwardPCR0 == 0)
                {
                    forwardPCR0 = frame_.getFirst24bit();
                    ie->setForwardPCR0(forwardPCR0);
                }
                else
                {
                    // same value already found -> exception ??
                    frame_.getFirst24bit(); // removing duplicate
                }
                IElength_ -= 3;
                break;
            case B1000_0011: // Backward Peak Cell Rate For CLP 0
                if (backwardPCR0 == 0)
                {
                    backwardPCR0 = frame_.getFirst24bit();
                    ie->setBackwardPCR0(backwardPCR0);
                }
                else
                {
                    // same value already found -> exception ??
                    frame_.getFirst24bit(); // removing duplicate
                }
                IElength_ -= 3;
                break;
            case B1000_0100: // Forward Peak Cell Rate For CLP 0+1
                if (forwardPCR1 == 0)
                {
                    forwardPCR1 = frame_.getFirst24bit();
                    ie->setForwardPCR1(forwardPCR1);
                }
                else
                {
                    // same value already found -> exception ??
                    frame_.getFirst24bit(); // removing duplicate
                }
                IElength_ -= 3;
                break;
            case B1000_0101: // Backward Peak Cell Rate For CLP 0+1
                if (backwardPCR1 == 0)
                {
                    backwardPCR1 = frame_.getFirst24bit();
                    ie->setBackwardPCR1(backwardPCR1);
                }
                else
                {
                    // same value already found -> exception ??
                    frame_.getFirst24bit(); // removing duplicate
                }
                IElength_ -= 3;
                break;
            default:
                throwContentError();
                break;
        }
    }
    setIEtoMsg(msg_, iePtr);
    
    debugString("decoded", _IEname);
    return;
}

void bisupATMCellRateIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfUlong cellRate = 0;
    
    pfIE *ieRef = msg_->getIE(_IEname);
    ieATMCellRate *ie = dynamic_cast<ieATMCellRate*>(ieRef);
    
    // every value has to be checked if they are set at all!!
    if (ie->isBackwardPCR1() == true)
    {
        cellRate = ie->getBackwardPCR1();
        frame_.putFirst24bit(cellRate);
        frame_.putFirst(B1000_0101);
    }
    if (ie->isForwardPCR1() == true)
    {
        cellRate = ie->getForwardPCR1();
        frame_.putFirst24bit(cellRate);
        frame_.putFirst(B1000_0100);
    }
    if (ie->isBackwardPCR0() == true)
    {
        cellRate = ie->getBackwardPCR0();
        frame_.putFirst24bit(cellRate);
        frame_.putFirst(B1000_0011);
    }
    if (ie->isForwardPCR0() == true)
    {
        cellRate = ie->getForwardPCR0();
        frame_.putFirst24bit(cellRate);
        frame_.putFirst(B1000_0010);
    }
    
    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupBBCIECoder
//
//Description:
//      Broadband Bearer Capability
//

bisupBroadbandBearerIECoder :: bisupBroadbandBearerIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(3);
    return;
}

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

void bisupBroadbandBearerIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong IElength_,
    pfMessenger *msg_)
{
    frameToBitString(frame_, IElength_);
    getExtension();  // must be 1, or non compatible
    getBits(7); // Coding standard for ITU-T = 0
    endExtension();
    
    getExtension();
    skip(2); // spare
    pfUlong bClass(getBits(5));
    
    ieBroadbandBearerCapability *ie = new ieBroadbandBearerCapability(bClass);
    auto_ptr<pfIE> iePtr(ie);
    
    if (bClass == B0001_0000) // 16 = BCOB-X, then next octet may be present
    {
        getExtension();
        skip(2);
        ie->setTrafficType(getBits(3));
        ie->setTimingRequirements(getBits(2));
    }
    ie->setBearerClass(bClass);
    endExtension();
    
    getExtension();
    ie->setSusceptibilityToClipping(getBits(2));
    skip(3);
    ie->setUserPlaneConnectionConfiguration(getBits(2));
    endExtension();

    setIEtoMsg(msg_, iePtr);
    debugString("decoded", _IEname);
    return;
}

void bisupBroadbandBearerIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieBroadbandBearerCapability *ie = 
        ieBroadbandBearerCapability::narrow(ieRef);
    
    putBits(ie->getUserPlaneConnectionConfiguration(), 2);
    putSpare(3);
    putBits(ie->getSusceptibilityToClipping(), 2);
    putExtension(1);
        
    pfUlong bClass(ie->getBearerClass());
    if (bClass == B0001_0000)
    {
        putBits(ie->getTimingRequirements(), 2);
        putBits(ie->getTrafficType(), 3);
        putSpare(2);
        putExtension(1);
        putBits(bClass, 5);
        putSpare(2);
        putExtension(0);
    }
    else
    {
        putBits(bClass, 5);
        putSpare(2);
        putExtension(1);
    }
    putBits(B1000_0000, 8);
    bitStringToFrame(frame_);
    
    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupCalledPartyNumberIECoder
//
//Description:
//      Called Party Number
//

bisupCalledPartyNumberIECoder :: bisupCalledPartyNumberIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(2);
    return;
}

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

void bisupCalledPartyNumberIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong IElength_,
    pfMessenger *msg_)
{
    string address;
    frameToBitString(frame_, IElength_);
    
    pfUlong odd = getBits(1);
    pfUlong nature = getBits(7);    
    skip(1);
    pfUlong plan = getBits(3);
    if (plan != B0000_0001)  // ISDN number
    {
        debugUser("This numbering plan is not supported!");
        throwContentError();
    }
    skip(4);    
    address = decodeBCD(odd, IElength_);
    
    iePartyNumber *ie = new iePartyNumber();
    auto_ptr<pfIE> iePtr(ie);    
    switch(nature)
    {
        case B0000_0001:
            ie->setSubscriberISDN(address);
            break;
        case B0000_0011:
            ie->setNationalISDN(address);
            break;
        case B0000_0100:
            ie->setInternationalISDN(address);
            break;
        default:
            debugUser("This numbering nature of address is not supported!");
            throwContentError();
    }
    setIEtoMsg(msg_, iePtr);
    debugString("decoded", _IEname);    
    return;
}

void bisupCalledPartyNumberIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    iePartyNumber *ie = iePartyNumber::narrow(ieRef);
    
    string address;
    address = ie->getISDN();
    pfUlong odd;
    odd = encodeBCD(address);
    putSpare(4);
    
    pfUlong plan = ie->getPlan();
    putBits(plan, 3);
    putBits(1, 1);
    
    pfUlong nature = ie->getType();
    putBits(nature, 7);
    putBits(odd, 1);
    
    bitStringToFrame(frame_);
    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupCallingPartyNumberIECoder
//
//Description:
//      Calling Party Number
//

bisupCallingPartyNumberIECoder :: bisupCallingPartyNumberIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(2);
    return;
}

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

void bisupCallingPartyNumberIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong IElength_,
    pfMessenger *msg_)
{
    string address;
    frameToBitString(frame_, IElength_);
    
    // First octet
    pfUlong odd = getBits(1);
    pfUlong nature = getBits(7);    

    // Second octet
    skip(1);
    pfUlong plan = getBits(3);
    if (plan != B0000_0001)  // ISDN number
    {
        debugUser("This numbering plan is not supported!");
        throwContentError();
    }
    pfUlong present = getBits(2);
    pfUlong screening = getBits(2);
    
    // Address signal
    address = decodeBCD(odd, IElength_);
    
    iePartyNumber *ie = new iePartyNumber();
    auto_ptr<pfIE> iePtr(ie);    
    switch(nature)
    {
        case B0000_0001:
            ie->setSubscriberISDN(address);
            break;
        case B0000_0011:
            ie->setNationalISDN(address);
            break;
        case B0000_0100:
            ie->setInternationalISDN(address);
            break;
        default:
            debugUser("This numbering nature of address is not supported!");
            throwContentError();
    }
    ie->setPresentationIndicator(present);
    ie->setScreeningIndicator(screening);
    setIEtoMsg(msg_, iePtr);
    debugString("decoded", _IEname);    
    return;
}

void bisupCallingPartyNumberIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    iePartyNumber *ie = iePartyNumber::narrow(ieRef);
    
    string address;
    address = ie->getISDN();
    pfUlong odd;
    odd = encodeBCD(address);
    putSpare(4);
    
    pfUlong plan = ie->getPlan();
    putBits(plan, 3);
    putBits(1, 1);
    
    pfUlong nature = ie->getType();
    putBits(nature, 7);
    putBits(odd, 1);
    
    bitStringToFrame(frame_);
    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupCallReferenceIECoder
//
//Description:
//      Destination/Origination Signaling Identifier
//      NOTE! this encoding may not destroy the IE

bisupCallReferenceIECoder :: bisupCallReferenceIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(4);
    return;
}

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

void bisupCallReferenceIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong ,
    pfMessenger *msg_)
{
    pfUlong controlID = frame_.getFirst32bit();
    ieCallReference *ie = new ieCallReference;
    auto_ptr<pfIE> iePtr(ie);
    
    ie->setValue(controlID);
    setIEtoMsg(msg_, iePtr);
    
    debugString("decoded", _IEname);
    return;
}

void bisupCallReferenceIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieCallReference *ie = ieCallReference::narrow(ieRef);
    
    pfUlong controlID = ie->getValue();
    frame_.putFirst32bit(controlID);

    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupCauseIECoder
//
//Description:
//      Cause
//

bisupCauseIECoder :: bisupCauseIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(2);
    return;
}

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

void bisupCauseIECoder :: decodeContent(pfFrame &frame_,
                                        pfUlong IElength_,
                                        pfMessenger *msg_)
{
    frameToBitString(frame_, IElength_);
    
    getExtension();
    getBits(3); // coding standard + spare can use skip(4) also
    pfUlong location = getBits(4);
    --IElength_;
    pfUlong skippedOctets = endExtension();
    IElength_ -= skippedOctets;
    
    getExtension();
    pfUlong cause = getBits(7);
    --IElength_;
    skippedOctets = endExtension();
    IElength_ -= skippedOctets;
    
    // ++ TODO++ Dianostics (Q.2610)
    
    ieCause *ie = new ieCause(cause, location);
    auto_ptr<pfIE> iePtr(ie);
                              
    //ie->setLocation(location);
    //ie->setCauseValue(cause);
    setIEtoMsg(msg_, iePtr);
    
    debugString("decoded", _IEname);
    return;
}

void bisupCauseIECoder :: encodeContent(pfFrame &frame_,
                                        pfMessenger *msg_)
{
    // ++TODO++ error checking?
    
    pfIE *ieRef = msg_->getIE(_IEname);
    ieCause *ie = dynamic_cast<ieCause*>(ieRef);
    
    pfUlong cause = ie->getCauseValue();
    pfUlong location = ie->getLocation();
    
    // ++TODO++ Dianostic(s)
    putBits(cause, 7);
    putBits(1, 1); // extension

    putBits(location, 4);
    putBits(0, 1); // spare
    putBits(0, 2); // Coding standard
    putBits(1, 1); // extension
    
    // moves networkIdentification from _bitString to frame
    bitStringToFrame(frame_);
    
    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: bisupConnectionElementIdIECoder
//
//Description:
//      Connection Element Identifier.
//

bisupConnectionElementIdIECoder :: bisupConnectionElementIdIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(4);
    return;
}

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

void bisupConnectionElementIdIECoder :: decodeContent(
    pfFrame &frame_,
    pfUlong ,
    pfMessenger *msg_)
{
    ieConnectionIdentifier *ie = new ieConnectionIdentifier;
    auto_ptr<pfIE> iePtr(ie);

    pfUlong VPCI = frame_.getFirst16bit();
    pfUlong VCI = frame_.getFirst16bit();
    ie->setVPCI(VPCI);
    ie->setVCI(VCI);
    setIEtoMsg(msg_, iePtr);
    
    debugString("decoded", _IEname);    
    return;
}

void bisupConnectionElementIdIECoder :: encodeContent(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    ieConnectionIdentifier *ie = 
        ieConnectionIdentifier::narrow(msg_->getIE(_IEname));
    
    pfUlong VCI = ie->getVCI();
    pfUlong VPCI = ie->getVPCI();
    
    frame_.putFirst16bit(VCI);
    frame_.putFirst16bit(VPCI);    
    
    debugString("encoded", _IEname);
    return;
}

//-----------------------------------------------------------------------
//
//Class: bisupTransitNetworkSelectionIECoder
//
//Description:
//      Transit Network Selection
//

bisupTransitNetworkSelectionIECoder :: bisupTransitNetworkSelectionIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : bisupIECoder(IEname_, IEtype_, isMandatory_)
{
    setContentLength(1);
    return;
}

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

void bisupTransitNetworkSelectionIECoder :: decodeContent(pfFrame &frame_,
                                                          pfUlong IElength_,
                                                          pfMessenger *msg_)
{
    ieTransitNetworkSelection *ie = new ieTransitNetworkSelection;
    auto_ptr<pfIE> iePtr(ie);
    frameToBitString(frame_, IElength_);

    pfUlong odd = getBits(1);
    
    pfByte nType(getBits(3));
    pfByte nPlan(getBits(4));
    
    string networkIdentification = decodeBCD(odd, (IElength_ - 1));
    ie->setNetworkIdentification(networkIdentification);
    switch(nType)
    {
        default:
            ie->setType(ieTransitNetworkSelection::national);
            ie->setPlan(nPlan);
    }
    setIEtoMsg(msg_, iePtr);
    
    debugString("decoded", _IEname);
    return;
}

void bisupTransitNetworkSelectionIECoder :: encodeContent(pfFrame &frame_,
                                                          pfMessenger *msg_)
{
    // ++TODO++ error checking?
    string networkIdentification("");
    
    pfIE *ieRef = msg_->getIE(_IEname);
    ieTransitNetworkSelection *ie =
        dynamic_cast<ieTransitNetworkSelection*>(ieRef);
    
    networkIdentification = ie->getNetworkIdentification();
    pfByte nType(ie->getType());
    pfByte nPlan(ie->getPlan());
    // encodes networkIdentification to _bitString
    pfUlong odd = encodeBCD(networkIdentification);
    putBits(nType, 4); // plan
    putBits(nPlan, 3); // National Network ID = 010
    putBits(odd, 1);
    // moves networkIdentification from _bitString to frame
    bitStringToFrame(frame_);

    debugString("encoded", _IEname);
    return;
}


//-----------------------------------------------------------------------
//
//Class: transparentIECoder
//
//Description:
//      IECoder for transparent parameters. IEname_ and IEtype_ will be given 
//      for constructor because that's the only way to set/get the IE to/from
//      primitive? Other way to know the name of the IE?
//

bisupTransparentIECoder :: bisupTransparentIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : IECoder(IEname_, IEtype_, isMandatory_)
{
    return;
}

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

void bisupTransparentIECoder :: decodeIE(pfFrame &frame_,
                                         pfMessenger *msg_)
{
    frame_.getFirst(); // skip type
    pfUlong IElength(frame_.getFirst16bit());
    
    if (IElength > frame_.length())
    {
        debugUser("Incorrect IE length");
        throwContentError();
    }
    if (_maxTimeExist != 0)  // for transparent only first will be counted
    {
        // set the transparent information
        ieTransparent *ie = new ieTransparent();
        auto_ptr<pfIE> iePtr(ie);
        pfFrame frame;
        ie->setType(getType()); // for futher development of transparent IE
        while (IElength > 0)
        {
            frame.putFirst(frame_.getFirst());
            --IElength;
        }
        ie->setFrame(frame); // now frame is here in reverse order
        setIEtoMsg(msg_, iePtr);
        --_maxTimeExist;
    }
    else
    {
        debugUser("_maxTimeExist == 0, IE discarded");
        while (IElength > 0)
        {
            frame_.getFirst();
            --IElength;
        }
    }
    debugString("decoded transparent IE", _IEname);
    return;
}

void bisupTransparentIECoder :: encodeIE(pfFrame &frame_,
                                         pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieTransparent *ie = dynamic_cast<ieTransparent*>(ieRef);
    
    pfFrame frame(ie->getFrame());
    pfUlong contentLength(frame.length());    
    
    for (pfUlong i=0; i < contentLength; i++)
    {
        frame_.putFirst(frame.getFirst());
    }
    frame_.putFirst16bit(contentLength);
    frame_.putFirst(ie->getType());

    debugString("encoded transparent IE", _IEname);
    return;
}
