//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / uni
//
//File: unipducoder.cpp
//
//Version: $Revision: 1.8 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/01/22 15:47:16 $
//
//Organisation:
//      Helsinki University of Technology
//      Laboratory of Telecommunications Software and Multimedia
// 
//Author:
//      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 "unipducoder.h"
#include "iface/sigif/sigupprimitives.h"
#include "protocol/uni/unistrings.h"
#include "protocol/uni/unierrorinfo.h"
#include "uniprimitives.h"

#include "protocol/sig/sigdefs.h"
#include "protocol/sig/sigexceptions.h"
#include "ie/iestrings.h"
#include "ie/callreference.h"
#include "pf/debug.h"


uniPduCoder :: uniPduCoder(pfUlong PDUtype_)
    : pduCoder(PDUtype_)
{
    return;
}

uniPduCoder :: uniPduCoder(void)
    : pduCoder()
// ++TODO++
{
    return;
}

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


//
// Function: create
//
// Description:
//
//

pfMessenger *uniPduCoder :: create(
    pfFrame &frame_, 
    uniCoOrdProtocol *protocol_)
{
    uniPduCoder *coder = 0;
    pfMessenger *msg = 0;
    
    if (frame_.length() < uniPduMinimumSize)
    {
        throw sigDiscardMessageException(PF_EX_INFO);
    }
    
    pfUlong pduType = frame_.read(uniMessageTypePosition);
    try
    {
        switch (pduType)
        {
            case uniMsgAlerting :
                // coder = uniALERTINGcoder
                msg = new sigALERTINGind;
                break;
                
                //++TODO++
                


            default:
                // Message type not implemented
                uniErrorInfo info(sigCauseValue_MessageTypeNonexistent);
                info.setMessageType(pduType);
                protocol_->sendSTATUSreplyToAAL(info);
                throw sigDiscardMessageException(PF_EX_INFO);
        }
        
        if (coder == 0 || msg == 0)
        {
            throw pfException(PF_EX_INFO);
        }
        
        // Decode the received pdu
        coder->decode(frame_, msg);
        
        // Check if all mandatory parameters were decoded
        coder->checkMandatoryIEs();
    }
    
    catch (sigNonMandatoryIEContentErrorException &exc)
    {
        coder->checkMandatoryIEs();
        //coder->uniNonMandatoryIEContentErrorAct(protocol_, msg, missingIEtype);
    }
    catch (sigUnrecognizedIEException &exc)
    {
        coder->checkMandatoryIEs();
        //coder->uniUnrecognizedIEAct(...) // eri error info / toiminta
    }
    catch (sigMandatoryIEContentErrorException &exc)
    {
        //coder->uniMandatoryIEContentErrorAct();
    }
    catch (sigMandatoryIEMissingException &exc)
    {
        //coder->uniMandatoryIEMissingAct(...)
    }
    
    // Exceptions coming through causes ignoring the message
    return msg;
}

//
//Function: decodeHeader
//
//Description:
//      Decodes uniPdu's header. Doesn't store any information
//      from the header part.
//

void uniPduCoder :: decodeHeader(pfFrame &frame_, pfMessenger *msg_)
{
    pfBitString bitFrame;
    bitFrame.fromFrame(frame_, uniPduMinimumSize); // Header part
    
    pfByte discriminator = bitFrame.getFirstBits(8);
    if (discriminator != uniProtocolDiscriminator)
    {
        debugPfUlong("Invalid Protocol Discriminator", discriminator);
        throw sigDiscardMessageException(PF_EX_INFO);
    }
    
    // Call Reference
    pfByte referenceLength = bitFrame.getFirstBits(8);
    if (referenceLength != 3)
    {
        throw sigDiscardMessageException(PF_EX_INFO);
    }
    // Set Call Reference IE to msg
    ieCallReference *ie = new ieCallReference;
    auto_ptr<pfIE> iePtr(ie);
    pfUlong crFlag = bitFrame.getFirstBits(1);
    pfUlong uniCallReference = bitFrame.getFirstBits(23);
    ie->setValue(uniCallReference);
    msg_->setIE(ieCallReferenceStr, iePtr);
    
    pfUlong muxReference =
        ((1-crFlag) << uniCallReferenceFlagBitPosition) | uniCallReference;
    msg_->setInteger(uniMuxReferenceStr, muxReference);
    
    // Message type
    msg_->setInteger(uniMessageTypeStr, bitFrame.getFirstBits(8));
    bitFrame.getFirstBits(3);
    pfUlong flag = bitFrame.getFirstBits(1); // get flag
    bitFrame.getFirstBits(2);
    // action indicator (see. 5.4.4.1, Note 1)
    pfUlong indicator = bitFrame.getFirstBits(2);
    
    if (flag != 0)
    {
        // Action indicator used in case of unrecognized or unexpected message
        // ++TODO++
        switch (indicator)
        {
            case B0000_0000 :
                // Clear Call
                debugUser("Clear Call by MT-action ind. (not impl.)");
                break;
                
            case B0000_0001 :
                // Discard and Ignore
                debugUser("Discard Message and Ignore (MT)");
                break;
                
            case B0000_0010 :
                // Discard and report Status
                debugUser("Discard Message and Report (MT)");
                break;
        }
    }
    
    pfUlong pduLength = bitFrame.getFirstBits(16);
    if (pduLength != frame_.length())
    {
        debugUser("Framelength did not match with message length!");
        // Decoding shall continue as far as possible. 5.6.5./Q.2931
    }
    return;
}


//
//Function: encodeHeader
//
//Description:
//      Encodes uniPDU's header:
//      16 bits Message Length,
//      2 bits IF action indicator
//      2 spare bits,
//      1 bit (0 Message IF not significant),
//      2 spare bits,
//      1 bit (1),
//      8 bits, uniMessageTypeStr
//

void uniPduCoder :: encodeHeader(pfFrame &frame_, pfMessenger *msg_)
{
    pfUlong messageLength = frame_.length();
    frame_.putFirst16bit(messageLength);
    frame_.putFirst(B1000_0000); //Message type, second octet
    frame_.putFirst(getType()); //Message type
    
    // Get Call Reference IE and put value to frame
    pfBitString cr;
    pfIE *ieRef = msg_->getIE(uniCallReferenceStr);
    ieCallReference *crIE = ieCallReference::narrow(ieRef);
    cr.putFirstBits(23, crIE->getValue());
    cr.putFirstBits(1, crIE->getFlag());
    cr.toFrame(frame_);
    // Call reference length
    frame_.putFirst24bit(8); 
    // Protocol Discriminator
    frame_.putFirst(uniProtocolDiscriminator);

    return;
}
