//Editor-Info: -*- C++ -*-
//
//Subject: TOVE project / IEcoder
//
//File: iecoder.cpp
//
//Version: $Revision: 1.11 $
//
//State: $State: Exp $
//
//Date: $Date: 1999/03/09 12:51:46 $
//
//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 "iecoder.h"
#include "pf/error.h"
#include "sigexceptions.h"

IECoder :: IECoder(
    const string &IEname_,
    const pfByte &IEtype_,
    bool isMandatory_)
    : _IEname(IEname_),
      _isMandatory(isMandatory_),
      _maxTimeExist(0),
      _IEtype(IEtype_),
      _IE_HEADER_MIN_LENGTH(0),
      _IE_CONTENT_MIN_LENGTH(0),
      _IE_CONTENT_MAX_LENGTH(100),
      _bitsUsageAllowed(false),
      _extensionBit(true),
      _isExtensionEnded(true),
      _isDataReadable(true),
      _actionInd(notSpecified)
{
    return;
}

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


//
//Function: decode
//
//Description:
//      Decodes IE. Passes all exceptions, here is how they should be used:
//      sigIgnoreException - Message has to be ignored
//      sigReportAndIgnoreException - Ignore message, report an error
//      sigReportAndProceedException - Ignore IE, proceed with message
//      sigClearCallException - clear call
//      When needed an action "Ignore IE, no report", it has to be implemented
//      at decodeIE-method by taking IE out of frame, and not inserting it
//      to the message.
//

void IECoder :: decode(pfFrame &frame_, pfMessenger *msg_)
{
    decodeIE(frame_, msg_);
    return;
}

//
//Function: encode
//
//Description:
//      Encodes IE. Uses protocol dependent encodeIE-method. Checking
//      if the parameter is mandatory enables some specific action to be done..
//

void IECoder :: encode(pfFrame &frame_, pfMessenger *msg_)
{
    //if(msg_->isVariableDefined(_IEname))
    if (msg_->ieAvailable(_IEname))
    {
        encodeIE(frame_, msg_);
    }
    else if (_isMandatory == true)
    {
        sigException(PF_EX_INFO);
    }
    return;
}


//
//Function: getType
//
//Description:
//      Returns typecode for the information element. _IEtype is private.
//

pfByte IECoder :: getType(void) const
{
    return _IEtype;
}

//
//Function: setHeaderMinLength / setContentMinLength
//
//Description:
//      Default value for content max length is set to 40, if it is
//      not defined as a parameter.
//

void IECoder :: setHeaderMinLength(pfUlong length_)
{
    _IE_HEADER_MIN_LENGTH = length_;
    return;
}

void IECoder :: setContentLength(pfUlong min_, pfUlong max_)
{
    _IE_CONTENT_MIN_LENGTH = min_;
    _IE_CONTENT_MAX_LENGTH = max_;
    return;
}

//
//Function: checkHeaderMinLength / checkContentLength
//
//Description:
//      Length errors will be treated as content errors. 5.6./Q.2931
//

void IECoder :: checkHeaderMinLength(pfUlong length_) const
{
    if (length_ < _IE_HEADER_MIN_LENGTH)
    {
        throwContentError();
    }
    return;
}

void IECoder :: checkContentLength(pfUlong length_) const
{
    if (length_ < _IE_CONTENT_MIN_LENGTH ||
        length_ > _IE_CONTENT_MAX_LENGTH)
    {
        throwContentError();
    }
    return;
}

//
//Function: setMaxTimeExist
//
//Description:
//       ---------------------------
//      | x1IE | x1IE | x2IE | x3IE |  OK (if current IE can exist twice)
//       ---------------------------
//       ---------------------------
//      | x1IE | x2IE | x1IE | x3IE |  NOT OK
//       ---------------------------
//

void IECoder :: setMaxTimeExist(pfUlong newMax_)
{
    _maxTimeExist = newMax_;
    return;
}


//
//Function: setIEtoMsg
//
//Description:
//      Sets new information element to the given primitive (storage).
//      _isDataReadable is false if extension is not read correctly, 
//      then we can't continue decoding.
//

void IECoder :: setIEtoMsg(pfMessenger *msg_, auto_ptr<pfIE> &ie_) const
{
    if (_isDataReadable == false)
    {
        debugUser("Failed usage of class IECoder!");
        throw sigException(PF_EX_INFO);
    }
    //msg_->defineIE(_IEname);
    msg_->setIE(_IEname, ie_);
    return;
}


//
//Function: isDecodedOK
//
//Description:
//      Mandatory IE returns true if it is decoded OK.
//      Optional IE returns always true.
//

bool IECoder :: isDecodedOK(void) const
{
    bool result = true;
    if ((_isMandatory == true) && (_maxTimeExist != 0))
    {
        result = false;
    }
    return result;
}

//
//Function: throwContentError
//
//Description:
//      Checks whether IE is mandatory or optional and throws an exception
//      depending on that.
//

void IECoder :: setContentErrorAction(IECoder::ActionInd action_)
{
    _actionInd = action_;
    return;
}

void IECoder :: throwContentError(void) const
{
    switch(_actionInd)
    {
        case notSpecified:
            break;
            
        case clearCall:
            // set clearCall errorInfo
            break;
            
        case discardIE:
            // set errorInfo
            break;
            
        case discardIEAndReport:
            // set errorInfo
            break;
            
        case discardMessage:
            // set errorInfo
            break;
            
        case discardMessageAndReport:
            // set errorInfo
            break;
    }
    if (_isMandatory == true) // Default for mandatory
    {
        throw sigMandatoryIEContentErrorException(
            PF_EX_INFO, _IEtype);
    }
    throw sigNonMandatoryIEContentErrorException(
        PF_EX_INFO, _IEtype);
    return;
}


//
//Function: frameToBitString
//
//Description:
//      Moves frame to bitString frameLength_ octets information.
//      Only after using calling this method following methods may
//      be used: getExtension, endExtension, getFirstBits, skip, and 
//      decodeBCD).
//

void IECoder :: frameToBitString(pfFrame &frame_, pfUlong length_)
{
    _bitString.fromFrame(frame_, length_);
    _bitsUsageAllowed = true;
    return;
}

//
//Function: BitStringToFrame
//
//Description:
//      Moves the whole bitString to the frame. If _bitString doesn't
//      contain full octets method throws pfOutOfRangeException.
//

void IECoder :: bitStringToFrame(pfFrame &frame_)
{
    _bitString.toFrame(frame_);
    _bitString.clear();
    _bitsUsageAllowed = false;
    return;
}

pfUlong IECoder :: getLength(void) const
{
    return _bitString.length();
}

void IECoder :: checkBitUsage(void)
{
    if (_bitsUsageAllowed == false)
    {
        debugUser("Failed usage of class IECoder!");
        throw sigException(PF_EX_INFO);
    }
    return;
}

//
//Functions: methods to use bitString
//
//Description:
//      Protected methods to use bitString. Inserting/getting allowed
//      only to/from beginning of the bitString.
//

void IECoder :: putBits(pfUlong value_, pfUlong length_)
{
    _bitString.putFirstBits(value_, length_);
    return;
}

pfUlong IECoder :: getBits(pfUlong length_)
{
    checkBitUsage();
    pfUlong value(_bitString.getFirstBits(length_));
    return value;
}


//
//Function: getExtension
//
//Description:
//      Get extensionBit from the bitString. If endExtension is not read
//      after extension 1 getExtension don't read anything and sets mode to
//      "data is not readable" as far as endExtension comes.
//

pfUlong IECoder :: getExtension(void)
{
    checkBitUsage();
    if (isGetExtOK() == true)
    {
        _extensionBit = _bitString.getFirstBits(1);
        _isExtensionEnded = false;
    }
    else
    {
        _isDataReadable = false;
    }
    return (pfUlong)_extensionBit;
}

//
//Function: putExtension
//
//Description:
//      Insert given extensionBit in the bitString
//

void IECoder :: putExtension(pfUlong value_)
{
    _bitString.putFirstBits(value_, 1);
    return;
}

//
//Function: endExtension
//
//Description:
//      Scrolls to the end of current extension, if there are octets that
//      belong to this extension and we can't decode those. Method returns
//      number of skipped octets. Used after reading last extensionfield
//      e.g. 3 3a 3b 4 5 (after reading 3b).
//

pfUlong IECoder :: endExtension(void)
{
    checkBitUsage();
    pfUlong skippedOctets = 0;
    
    while ((_extensionBit == false) && (_bitString.length() > 0))
    {
        _extensionBit = _bitString.getFirstBits(1);
        _bitString.getFirstBits(OCTET-1);
        ++skippedOctets;
    }
    _isExtensionEnded = true;
    _isDataReadable = true;
    return skippedOctets;
}

//
//Function: isExtensionOK
//
//Description:
//      Checks that can we take data from bitString. If last extension
//      was 1 and endExtension has not been marked then bitString has less
//      extension fields than we think and we can't continue reading from
//      bitString before endExtension become.
//

bool IECoder :: isGetExtOK(void) const
{
    bool result = false;
    
    // Previous extension is read to the end or extension still continues
    if ((_extensionBit == true && _isExtensionEnded == true) ||
        (_extensionBit == false && _isExtensionEnded == false))
    {
        result = true;
    }
    return result;
}

//
//Function: putSpare / skip
//
//Description:
//      "putSpare" inserts amount_ sparebits (value_) into bitString.
//      "skip" skips amount_ of bits in bitString.
//

void IECoder :: putSpare(pfUlong amount_, pfUlong value_)
{
    _bitString.putFirstBits(value_, amount_);
    return;
}

void IECoder :: skip(pfUlong amount_)
{
    checkBitUsage();
    if (_isDataReadable == true)
    {
        _bitString.getFirstBits(amount_);
    }
    return;
}


// ++TODO++ following methods made by TK need to be checked

//
//Function: decodeBCD
//
//Description:
//      Decodes BCD-coded number. Example of BCD-coded number found
//      Q.2763(Chap. 7.15). Method returns a number in the string.
//

string IECoder :: decodeBCD(pfUlong odd_, pfUlong length_)
{
    string addressSignal;
    pfUlong i;
    pfUlong oddElementsPart, evenElementsPart;
    char oddElementsPartChar, evenElementsPartChar;

    // length_ is length of octets and bitstring gives length in bytes.
    if (length_ > 0)
    {
        if (_bitString.length() < (length_ * OCTET))
        {
            debugUser("Decoding BCD number failed due incorrect field length");
            throw pfException(PF_EX_INFO);
        }
        
        for (i = 0; i < (length_ - 1); ++i)
        {
            evenElementsPart = _bitString.getFirstBits(4);
            oddElementsPart  = _bitString.getFirstBits(4);
            evenElementsPartChar = ulongToChar(evenElementsPart);
            oddElementsPartChar = ulongToChar(oddElementsPart);
            addressSignal = addressSignal.append(1, oddElementsPartChar);
            addressSignal = addressSignal.append(1, evenElementsPartChar);
        }
        
        // Decoding last two elements.
        if (odd_ != 0) // in case of odd number of address signal elements.
        {
            skip(4); // skip Filler
            oddElementsPart  = _bitString.getFirstBits(4);
            oddElementsPartChar = ulongToChar(oddElementsPart);
            addressSignal = addressSignal.append(1, oddElementsPartChar);
        }
        else // in case of even number of address signal elements.
        {
            evenElementsPart = _bitString.getFirstBits(4);
            oddElementsPart  = _bitString.getFirstBits(4);
            evenElementsPartChar = ulongToChar(evenElementsPart);
            oddElementsPartChar = ulongToChar(oddElementsPart);
            addressSignal = addressSignal.append(1, oddElementsPartChar);
            addressSignal = addressSignal.append(1, evenElementsPartChar);
        }
    }
    
    return addressSignal;
}

//
//Function: encodeBCD
//
//Description:
//      Encodes number to BCD and returns even/odd depending amount of numbers.
//

pfUlong IECoder :: encodeBCD(const string &address_)
{
// Method for encode address signal. Used for exsample Q.2763(Chap. 7.15).
// Address signal includes 1 to n 4-bit fields.

    pfLong addrLength = address_.length();
    pfUlong isOdd = addrLength % 2;
    int i;
    --addrLength; // address index are 0...addrLength-1

    if (isOdd != 0)
    {
        _bitString.putFirstBits(address_[addrLength], 4);
        // If number of address signal is odd, needeed 4-bit filler-field.
        putSpare(4);
        --addrLength;
    }

    for (i = addrLength; i > 0; i -= 2)
    {
        _bitString.putFirstBits(address_[i-1], 4);
        _bitString.putFirstBits(address_[i], 4);
    }
    return isOdd;

}

//
//Function: decodeIA5
//
//Description:
//      Decodes IA5-coded number.

string IECoder :: decodeIA5(pfLong &ieLength_)
{
    string digits;
    pfUlong dummy;

    while (ieLength_ > 0) 
    {
        dummy = getBits(8);
        dummy = dummy & B0111_1111;
        digits.append(1, (char) dummy);
        ieLength_--;
    }

    return digits;
}

//
//Function: encodeIA5
//
//Description:
//      Encodes IA5-coded number.

pfUlong IECoder :: encodeIA5(string &digits_)
{
    pfUlong length;
    pfUlong digit;
    pfLong index = digits_.length() - 1;

    for (length = 0; index >= 0; index--)
    {
        digit = digits_[index];
        digit = digit & B0111_1111;
        putBits(8, digit);
        length++;
    }

    return length;
}


//
//Function: ulongToChar
//
//Description:
//      Makes pfUlong "value" to char "value", for exchample pfUlong "1" comes
//      char '1'. If value is higher than 9 then method returns ascii-mark value+48,
//      for exchamples 11 comes 11+48=59 and it is char ';'.
//      This method is useful when decoding BCD-number to the string.
//      BCD-number can include some other values than 0-9 e.g. 'code 11' see specs.
//

char IECoder :: ulongToChar(pfUlong value_)
{
    char result;
    value_ += 48;
    result = (char)value_;
    return result;
}
