//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / pducoder
//
//File: pducoder.cpp
//
//Version: $Revision: 1.8 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/01/22 15:48:35 $
//
//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 "pducoder.h"
#include "sigexceptions.h"
#include "pf/debug.h"

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

pduCoder :: pduCoder(void)
    : _PDUtype(0)
{
    return;
}

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

pfUlong pduCoder :: getType(void) const
{
    return _PDUtype;
}

//
//Function: addIECoder
//
//Description:
//      Add new IECoder to the map and list. Used at pduCoder constructors.
//      Encoding starts from the beginning of the list, so those information
//      elements will be at the end of frame.
//

void pduCoder :: addIECoder(IECoder *coder_)
{
    pfUlong IEtype = coder_->getType();
    _IEmap[IEtype] = coder_;
    _IElist.push_back(coder_);
    return;
}

//
//Function: resetVariables
//
//Description:
//      Used to set those variables which would be acting differently after
//      the first time due to static pduCoders in protocols (ie. niProtocol).
//      This method is called usually before decoding a received frame.
//

void pduCoder :: resetVariables(void)
{
    listIterType listIter;
    for (listIter = _IElist.begin(); listIter != _IElist.end(); ++listIter)
    {
        (*listIter)->setMaxTimeExist(1);
    }
    return;
}


//
//Function: decode
//
//Description:
//      Public decode method for PDU. First call pure virtual decode method
//      for PDU header. IEs are decoded using map which includes pointers to
//      IECoders (key is the IE-type).
//

void pduCoder :: decode(pfFrame &frame_, pfMessenger *msg_)
{
    pfUlong length, nextIEtype = 0;
    mapIterType mapIter;
    string IEname;
    pfUlong previousIEtype = 0;
    bool isFirstIE = true;
    
    debugUser("PDUCODER::DECODE");
    debugFrame("Frame contents: ", frame_);
    
    resetVariables();
    decodeHeader(frame_, msg_);
    
    length = frame_.length();    
    while (length > 0)
    {
        previousIEtype = nextIEtype;
        nextIEtype = frame_.read(0);
        
        // If the next IE is not the same type with previous, it may not
        // exist any more. All copies of IE must be after each other.
        if ((isFirstIE == false) && (nextIEtype != previousIEtype))
        {
            (*mapIter).second->setMaxTimeExist(0);
            isFirstIE = false;
        }
        
        mapIter = _IEmap.find(nextIEtype);
        if (mapIter != _IEmap.end())
        {
            (*mapIter).second->decode(frame_, msg_);
        }
        else
        {
            debugPfUlong("Unknown IE: ", nextIEtype);
            throw sigUnrecognizedIEException(PF_EX_INFO, nextIEtype);
        }
        length = frame_.length();
    }
    return;
}

//
//Function: encode
//
//Description:
//      Public encode method for PDU. First call pure virtual encode method
//      for PDU header. IEs are encoded using list which includes pointers to
//      IECoders.
//      By catching sigExceptions (pfExceptions comes through), errors like
//      missing mandatory IE discovery lays on the receiving side.
//

void pduCoder :: encode(pfFrame &frame_, pfMessenger *msg_)
{
    listIterType listIter;
    for (listIter = _IElist.begin(); listIter != _IElist.end(); ++listIter)
    {
        try
        {
            (*listIter)->encode(frame_, msg_);
        }
        catch(sigException &excep)
        {
            excep.printInfo();
        }
    }
    encodeHeader(frame_, msg_);
    return;
}

//
//Function: checkMandatoryIEs
//
//Description:
//      Used after decoding to see if all IE objects were 'Decoded OK ?'.
//      If all answer 'yes' then PDU is decoded right.
//      Optional IE should every time answer 'yes' and if mandatory's IE
//      decode method has't been used it will answer 'no'.
//

void pduCoder :: checkMandatoryIEs(void)
{
    listIterType listIter;
    for(listIter = _IElist.begin(); listIter != _IElist.end(); ++listIter)
    {
        if ((*listIter)->isDecodedOK() == false)
        {
            pfUlong missingType = (*listIter)->getType();
            throw sigMandatoryIEMissingException(PF_EX_INFO, missingType);
        }
    }
    return;
}
