//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project
//
//File: 
//
//Version: $Revision: 1.1 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/01/22 15:53:43 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
//
//Author:
//	Samu Uimonen
//	Saku Vaittinen
//
//Description:
//
//
//Copyright:
//      Copyright 1999 Helsinki University of Technology
//      ALL RIGHTS RESERVED BETWEEN JANUARY 1996 AND JUNE 1999.
//
//Licence:
//
//
//History: 

#include "uniiecoder.h"
#include "ie/connectionidentifier.h"
#include "ie/qos.h"
#include "ie/callstate.h"
#include "ie/cause.h"
#include "ie/broadbandbearercapability.h"
#include "pf/debug.h"
#include "protocol/sig/sigexceptions.h"
#include "protocol/sig/sigdefs.h"

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

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

void uniIECoder :: decodeIE(pfFrame &frame_, pfMessenger *msg_)
{
    pfUlong frameLength = frame_.length();
    if (frameLength < uniIEMinimumSize)
    {
        debugUser("Frame too short for IE");
        throw sigDiscardMessageException(PF_EX_INFO);
    }
    frameToBitString(frame_, uniIEMinimumSize);	
    
    IEType elementIdentifier = IEType(getBits(8));
    debugPfUlong("Decoding ie", elementIdentifier);
    
    skip(1);
    pfUlong standard = getBits(2);
    pfUlong IEFlag = getBits(1);
    skip(1);
    pfUlong IEActionInd = getBits(3);
    
    // Move the length of the IE from the frame to bitString for content decode
    pfUlong length = getBits(16);
    frameLength = frame_.length();
    if (frameLength < length)
    {
        debugUser("Frame too short for IE");
        throw sigDiscardMessageException(PF_EX_INFO);
    }
    frameToBitString(frame_, length);
    
    // Now IE is no longer in frame, and we can safely skip 
    // the rest of decoding if an error is occured.
    checkContentLength(length);  // min and max values should be set
    if ((standard != 0) && (standard != 3))
    {
        debugPfUlong("Invalid IE standard", standard);
        throwContentError();
    }
    
    if (IEFlag != 0)
    {
        switch (IEActionInd)
        {
            case B0000_0000 : // Clear call
                debugUser("Clear Call by IET-action field (not impl.)");
                setContentErrorAction(clearCall);
                break;
                
            case B0000_0001 : // Discard IE and proceed
                debugUser("Discard IE and Proceed");
                setContentErrorAction(discardIE);
                break;
                
            case B0000_0010 : // Discard IE, proceed and report status
                debugUser("Discard IE, Proceed, Report");
                setContentErrorAction(discardIEAndReport);
                break;
                
            case B0000_0101 : // Discard message and ignore
                debugUser("Discard Message and Ignore");
                setContentErrorAction(discardMessage);
                break;
                
            case B0000_0110 : // Discard message and report status
                debugUser("Discard Message and Report");
                setContentErrorAction(discardMessageAndReport);
                break;
                
            default :
                debugUser("Action indicator undefined");
                setContentErrorAction(discardIEAndReport);
        }
    }
    // After this there should come only content errors

    decodeContent(msg_);

    return;
}


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

uniConnectionIdIdentifierIECoder :: uniConnectionIdIdentifierIECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    pfBoolean isMandatory_)
    : uniIECoder(IEname_, IEtype_, isMandatory_)
{
    //setContentLength(x)
    return;
}

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

void uniConnectionIdIdentifierIECoder :: decodeContent(pfMessenger *msg_)
{
    ieConnectionIdentifier *ie = new ieConnectionIdentifier;
    auto_ptr<pfIE> iePtr(ie);
    
    getExtension();
    skip(2);
    pfUlong VPAssosiated = getBits(2);
    pfUlong preferredExclusive = getBits(3);
    endExtension();

    if ((VPAssosiated != 1) || (preferredExclusive != 0))
    {
	skip(32);
//	throw uniIEFieldErrorException(PF_EX_INFO);
    }

    ie->setPreferred(preferredExclusive);
    ie->setVPassociated(VPAssosiated);
    ie->setVPCI(getBits(16));
    ie->setVCI(getBits(16));

    setIEtoMsg(msg_, iePtr);

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

void uniConnectionIdIdentifierIECoder :: encodeContent(
    pfFrame &frame_, 
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieConnectionIdentifier *ie = ieConnectionIdentifier::narrow(ieRef);


    putBits(16, ie->getVCI());
    putBits(8, ie->getVCI());
    putBits(8, 0);
    putBits(3, B0000_0000); // Exclusive VPCI; exclusive VCI
    putBits(2, B0000_0001); // VP Associated Signaling
    putSpare(2);
    putExtension(1); 
    putBits(16, 5);
    putBits(8, 0);
    putBits(8, uniIEConnectionIdentifier);

    bitStringToFrame(frame_);

    return;

}

void uniQoSIECoder :: decodeQoSparameters(pfMessenger *msg_)
{
    ieQoS *ie = new ieQoS;
    auto_ptr<pfIE> iePtr(ie);

//    checkIELength(2);
    checkContentLength(2);

    ie->setClassForward(getBits(8));
    ie->setClassBackward(getBits(8));

    setIEtoMsg(msg_, iePtr);

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

void uniQoSIECoder :: encodeQoSparameters(
    pfFrame &frame_, 
    pfMessenger *msg_)
{
    ieQoS *ie = new ieQoS;
    auto_ptr<pfIE> iePtr(ie);
    
    pfUlong coding = specificInstructionField;
    if ((ie->getClassForward() == 0) && (ie->getClassBackward() == 0))
    {
        coding = standardInstructionField;
    }

    putBits(8, ie->getClassBackward());
    putBits(8, ie->getClassForward());
    putBits(16, 2);
    putBits(8, coding);
    putBits(8, uniIEQoSParameters);

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


void uniBBCIECoder :: decodeBroadbandBearerCapability(pfMessenger *msg_)
{
    ieBroadbandBearerCapability *ie = new ieBroadbandBearerCapability;
    auto_ptr<pfIE> iePtr(ie);
    
    //checkIELength(2,3); checked at uniIECoder::decodeIE method
    
    getExtension();
    skip(2);	// spare
    ie->setBearerClass(getBits(5));
    getExtension();
    skip(2);    // spare
    ie->setTrafficType(getBits(3));
    ie->setTimingRequirements(getBits(2));
    endExtension();

    getExtension();
    ie->setSusceptibilityToClipping(getBits(2));
    skip(3);	// spare
    ie->setUserPlaneConnectionConfiguration(getBits(2));
    endExtension();

    setIEtoMsg(msg_, iePtr);

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


void uniBBCIECoder :: encodeBroadbandBearerCapability(
    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);

    if (ie->getBearerClass() == B0001_0000)
    {
    	putBits(ie->getTimingRequirements(), 2);
    	putBits(ie->getTrafficType(), 3);
    	putSpare(2);
    	putExtension(1);

    	putBits(ie->getBearerClass(), 5);
    	putSpare(2);
    	putExtension(0);
    }
    else
    {
        putBits(ie->getBearerClass(), 5);
        putSpare(2);
        putExtension(1);
    }
    putBits(B1000_0000, 8);
    bitStringToFrame(frame_);

    debugString("encoded", _IEname);
    return;

}

void uniCallStateIECoder :: decodeCallState(pfMessenger *msg_)
{
    ieCallState *ie = new ieCallState;
    auto_ptr<pfIE> iePtr(ie);

    //checkIELength(1); checked at uniIECoder::decodeIE method
    skip(2);

    ie->setValue(getBits(6));

    setIEtoMsg(msg_, iePtr);

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

void uniCallStateIECoder :: encodeCallState(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieCallState *ie = ieCallState::narrow(ieRef);

    putBits(ie->getValue(), 6);
    putSpare(2);
    
    bitStringToFrame(frame_);

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

void uniCauseIECoder :: decodeCause(pfMessenger *msg_)
{
    ieCause *ie = new ieCause;
    auto_ptr<pfIE> iePtr(ie);

    //checkIELength(2,30); checked at uniIECoder::decodeIE method
    skip(4);
    pfUlong location = getBits(4);
    ie->setLocation(location);
    skip(1);
    pfUlong causeValue = getBits(7);
    ie->setCauseValue(causeValue);
//    ie->setCauseClass(causeValue >> 4); not needed ?

//    skip(elementLength * 8);	// Skip diagnostics for now

    // Checkings
    if ((location == B0000_0110) ||
        ((location > B0000_1000) &&
         (location != B0000_1010)))
    {
//        throw uniIEFieldErrorException(PF_EX_INFO);
    }

    setIEtoMsg(msg_, iePtr);

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

void uniCauseIECoder :: encodeCause(
    pfFrame &frame_,
    pfMessenger *msg_)
{
    pfIE *ieRef = msg_->getIE(_IEname);
    ieCause *ie = dynamic_cast<ieCause*>(ieRef);
    
    pfUlong cause = ie->getCauseValue();
    pfUlong location = ie->getLocation();
    
    putBits(cause, 7);
    putExtension(1);

    putBits(location, 4);
    putSpare(1);
    putBits(0, 2); // Coding standard
    putExtension(1);
    
    bitStringToFrame(frame_);
    
    debugString("encoded", _IEname);
    return;
}

/*
void uniCallingPartyNumberIECoder :: decodeCallingPartyNumber(
    pfMessenger *msg_)
{
    //++TODO++

    return;
}
*/
