/*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/

/**@file GPRS L2 RLC Messages, from GSM 04.60 Section 11 */

#ifndef GPRSL2RLCMESSAGES_H
#define GPRSL2RLCMESSAGES_H


#include <iostream>
#include <stdint.h>
#include "Defines.h"
#include "BitVector.h"
#include "Logger.h"
#include "GSMCommon.h"
//#include "GSMTransfer.h"
//#include "GSMTDMA.h"
//#include <Globals.h>
#include "MsgBase.h"
#include "RLCHdr.h"
#include "TBF.h"
#include "MemoryLeak.h"


namespace GPRS {
class TBF;


/** GSM 04.60 11.2 */
class RLCMessage : public Text2Str {
	public:
	virtual void text(std::ostream&) const = 0;
	RLCMessage() { RN_MEMCHKNEW(RLCMessage) }
	// The virtual keyword on a destructor indicates that both the base destructor (this one)
	// and the derived class destructor are both called.  Otherwise they aren't.  It is foo bar.
	virtual ~RLCMessage() { RN_MEMCHKDEL(RLCMessage) }
};
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg);
#if RLCMESSAGES_IMPLEMENTATION
std::ostream& operator<<(std::ostream& os, const RLCMessage *msg)
{
	msg->text(os);
	return os;
}
#endif

class RLCMsgUplinkIE
{
	public:
	virtual void parseElement(const BitVector &src, size_t &rp) = 0;
	virtual void writeBody(MsgCommon&dst) const = 0;
	// The indivual IE can overwrite textElement is MsgCommon does not work very well for it.
	virtual void textElement(std::ostream& os) const {
		MsgCommonText dst(os);
		writeBody(dst);
	}
};

class RLCMsgDownlinkIE
{
	public:
	virtual void writeBody(MsgCommon&dst) const = 0;

	void writeOptional01(MsgCommon &dst, bool control) const {
		if (control) {
			dst.write1();
			writeBody(dst);
		} else {
			dst.write0();
		}
	}
};

// The RLCDownlink and RLCUplink Messages are RLC Control messages -
// they are not L3 Messages as defined in GSM 04.18.
// These messages are used at the RLC/MAC layer to control the PDCH channels
// assigned for packet (GPRS) use.  Their primary use is to transfer
// L3 Messages and user data between the SGSN and the MS.
// Mobility Management routines reside entirely inside the SGSN.
class RLCDownlinkMessage :
	public MACDownlinkHeader,
	public RLCMessage
{
	public:

	/** Message Type GSM 04.06 11.2.0.2 */
	// The ones we will implement marked with <<<
	// Message marked PCCCH only cannot be implemented now.
	// From sec 11.1.1.1: if the high bit of a downlink control message type is 1,
	// then it is a distribution message to all MS, otherwise it is a non-distribution message.
	// Note that the downlink message content is byte aligned (6 bit MessageType + 2 bit PageMode)
	// Also note that the uplink message content is not byte aligned.
	// From sec 11.1.1.2: Non-distribution messages may have distribution contents,
	// followed by an Address Information (to identify a single MS) followed by
	// non-distribution contents. 
	// NOTE: The unused bits at the end of the control message must be packed out
	// as per GSM04.60sec11: one 0 bit, followed by 0 bits to get to a byte boundary,
	// followed by byte 0x2b forever.
	enum MessageType {
		/*100001 */  PacketAccessReject  = 0x21,		// PACCH or PCCCH <<<<
		/*000001 */  PacketCellChangeOrder = 0x1,	// PCCCH or PACCH
		/*000010 */  PacketDownlinkAssignment = 0x2,	// PCCCH or PACCH <<<< acknowledged
		/*000011 */  PacketMeasurementOrder  = 0x3,	// PCCCH or PACCH

		// PagingRequest is only sent on PACCH to initiate an RR connection, so skip for now:
		// Note: You can send still GSM paging messages on CCCH when MS is in packet-idle mode.
		/*100010 */  PacketPagingRequest  = 0x22,		// PCCCH or PACCH
		/*100011 */  PacketPDCHRelease = 0x23,			// PACCH
		/*000100 */  PacketPollingRequest = 0x4,		// PACCH or PCCH <<<< not used implicitly acknowldged
		/*000101 */  PacketPowerControlTimingAdvance = 0x5,	// PACCH
		/*100100 */  PacketPRACHParameters = 0x24,		// PCCCH only
		/*000110 */  PacketQueueingNotification = 0x6,	// PCCCH only
		/*000111 */  PacketTimeslotReconfigure = 0x7,	// PACCH
		/*001000 */  PacketTBFRelease = 0x8,			// PACCH <<< acknowledged
		/*001001 */  PacketUplinkAckNack = 0x9,			// PACCH <<<
		// The acknowledgement to Packet Uplink Assignment is not strictly needed
		// because we will know as soon as the MS starts sending blocks.
		/*001010 */  PacketUplinkAssignment = 0xa,		// PCCCH or PACCH <<< acknowledged
		/*100101 */  PacketDownlinkDummyControlBlock = 0x25,	// PCCCH or PACCH
		/*110001 */  PSI1 = 0x31,
		/*110010 */  PSI2 = 0x32,
		/*110011 */  PSI3 = 0x33,
		/*110100 */  PSI3bis = 0x34,
		/*110101 */  PSI4 = 0x35,
		/*110110 */  PSI5 = 0x36,
		/*110000 */  PSI6 = 0x30,
		/*111000 */  PSI7 = 0x38,
		/*111001 */  PSI8 = 0x39,
		/*110111 */  PSI13 = 0x37,
		/*111010 */  PSI14 = 0x3a,
		/*111100 */  PSI3ter = 0x3c,
		/*111101 */  PSI3quater = 0x3d,
		/*111110 */  PSI15 = 0x3e
	};
	static const char *name(MessageType mtype);

	// There are optional additional RLC Control Block header fields here, but
	// we will not use them, so they are not even mentioned here.

	MessageType mMessageType;	// 6 bits
	unsigned mPageMode;			// 2 bits

	TBF *mTBF;					// If this message is associated with a tbf, here it is.
	
	RLCDownlinkMessage(MessageType mtype, bool wRequiresAck, TBF *wtbf) :
		mMessageType(mtype),
		mPageMode(0),
		mTBF(wtbf)
	{
		MACDownlinkHeader::init(MACPayloadType::RLCControl);
	}


	// Not all the messages have setTLLI().
	virtual void setTLLI(int) {};
	// Not all the messages have setTimingAdvance().
	virtual void setTimingAdvance(int) {};

	// writeBody defined in each message class to write the message body starting after PageMode.
	virtual void writeBody(MsgCommon&dst) const = 0;
	void writeHeader(MsgCommon &dst) const;

	/** Serialize this message into a BitVector.  The caller is responsible for deleting the memory.
	 */
	void write(BitVector&vec) const;
	void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCDownlinkMessage::writeHeader(MsgCommon& dst) const {
		MACDownlinkHeader::writeMACHeader(dst);
		dst.WRITE_FIELD(mMessageType,6);
		dst.WRITE_FIELD(mPageMode,2);
	}
	void RLCDownlinkMessage::text(std::ostream &os) const {
		MsgCommonText dst(os);
		os << name(mMessageType) << ":";
		writeHeader(dst);
		writeBody(dst);
	}
	void RLCDownlinkMessage::write(BitVector&vec) const {
		MsgCommonWrite dst(vec);
		writeHeader(dst);
		writeBody(dst);
		// GSM04.60 sec 11: 
		// The padding bits may be the 'null' string. Otherwise, the padding
		// bits starts with bit '0', followed by 'spare padding'.
		if (dst.wp & 0x7) { dst.write0(); }	// one optional 0 bit
		while (dst.wp & 0x7) { dst.writeL(); }	// pad to a byte boundary.
		while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
	}
#endif



/** GSM 04.60 11.2 */
class RLCUplinkMessage : public RLCMessage
{
	public:
	// GSM04.60 sec 11.2
	enum MessageType {
		/* 000000 */ PacketCellChangeFailure = 0x0,
		/* 000001 */ PacketControlAcknowledgement = 0x1,
		/* 000010 */ PacketDownlinkAckNack = 0x2,
		/* 000011 */ PacketUplinkDummyControlBlock = 0x3,
		/* 000100 */ PacketMeasurementReport = 0x4,
		/* 001010 */ PacketEnhancedMeasurementReport = 0xa,
		/* 000101 */ PacketResourceRequest = 0x5,
		/* 000110 */ PacketMobileTBFStatus = 0x6,
		/* 000111 */ PacketPSIStatus = 0x7,
		/* 001000 */ EGPRSPacketDownlinkAckNack = 0x8,
		/* 001001 */ PacketPause = 0x9,
		/* 001011 */ AdditionalMSRadioAccessCapabilities = 0xb
	};
	static const char *name(MessageType type);
	MACUplinkHeader mmac;				// 8 bits
	MessageType mMessageType;	// 6 bits


	// Parse/text just the body.  These must be defined in each descendent class
	//virtual void textBody(std::ostream&os) const = 0;

	virtual int getTFI() { return -1; }
	virtual MSInfo *getMS(PDCHL1FEC *chan, bool create) { assert(0); }
	virtual MSInfo *getTBF(PDCHL1FEC *chan) { assert(0); }

	virtual void parseBody(const BitVector &src, size_t &rp) = 0;
	virtual void writeBody(MsgCommon&dst) const = 0;
	void parse(const RLCRawBlock *src);
	void writeHeader(MsgCommon &dst) const;

	/** Serialize this message into a BitVector.  The caller is responsible for deleting the memory.
	 */
	void write(BitVector&vec) const;
	void text(std::ostream &os) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCUplinkMessage::parse(const RLCRawBlock*src) {
		// The MACUplinkHeader is explict (mmac) instead of a base class
		// to make this line of code more obvious:
		mmac = src->mmac;		// Parsed previously.
		size_t rp = mmac.lengthBits();
		mMessageType = (MessageType) src->mData.readField(rp,6);
		parseBody(src->mData,rp);
	}

	void RLCUplinkMessage::writeHeader(MsgCommon&dst) const {
		mmac.writeMACHeader(dst);
		dst.WRITE_FIELD(mMessageType,6);
	}

	void RLCUplinkMessage::text(std::ostream &os) const {
		MsgCommonText dst(os);
		os << name(mMessageType) << ":";
		mmac.text(os);		// or: mmac.writeMACHeader(dst);
		dst.WRITE_FIELD(mMessageType,6);
		writeBody(dst);
	}
	void RLCUplinkMessage::write(BitVector&vec) const {
		MsgCommonWrite dst(vec);
		writeHeader(dst);
		writeBody(dst);
		// GSM04.60 sec 11: For RLC messages: one 0 followed by byte-aligned 0x2b
		if (dst.wp & 0x7) { dst.write0(); }	// one optional 0 bit
		while (dst.wp & 0x7) { dst.writeL(); }	// pad to a byte boundary.
		while (dst.wp < RLCBlockSizeInBits[0]) { dst.writeField(0x2b,8); }
	}
#endif

// (pat) This is for downlink power parameters, as per GSM04.60 sec 11.2.29.
// We will probably never use this.
//struct L3IADownlinkPowerOptionIE {
//  int mPowerOption;   // 1 if P0, PWR_CTRL_MODE, PR_MODE should be included.
//  unsigned mP0:4;
//  unsigned mBTSPwrCtrlMode:1;
//  unsigned mPRMode:1;
//  void writePower(L3Frame &dst, int whichDesignationMethod) {
//      if (mPowerOption) {
//          whichDesignationMethod ? dst.writeH() : dst.writeField(1,1);
//          dst.writeField(P0,4);
//          dst.writeField(mBTSPwrCtrlMode,1);
//          dst.writeField(mPRMode,1);
//      } else {
//          whichDesignationMethod ? dst.writeL() : dst.writeField(0,1);
//      }
//  }
//  L3AssignmentPowerOptionIE::L3AssignmentPowerOptionIE() { memset(this,0,sizeof(*this)); }
//};


// GSM04.60 12.3
// Note this IE is both uplink and downlink.
struct RLCMsgPacketAckNackDescriptionIE : public RLCMsgUplinkIE, public RLCMsgDownlinkIE
{
	Field<1> mFinalAckIndication;
	Field<7> mSSN;	// "Starting Sequence Number" except it is not.
					// It is actually the ending sequence number.
	static const int mbitmapsize = 64;
	bool mBitMap[mbitmapsize];

	void parseElement(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketAckNackDescriptionIE::parseElement(const BitVector &src, size_t &rp)
	{
		mFinalAckIndication = src.readField(rp,1);
		mSSN = src.readField(rp,7);
		for (int i = 0; i < mbitmapsize; i++) {
			mBitMap[i] = src.readField(rp,1);
		}
	}
	void RLCMsgPacketAckNackDescriptionIE::writeBody(MsgCommon&dst) const
	{
		dst.WRITE_ITEM(mFinalAckIndication);
		dst.WRITE_ITEM(mSSN);
		dst.writeBitMap((bool*)mBitMap,mbitmapsize,"Bitmap");
	}
#endif

// GSM04.60 12.7
struct RLCMsgChannelRequestDescriptionIE : public RLCMsgUplinkIE
{
	Field<4> mPeakThroughputClass; // See 3GPP 24.008
	Field<2> mRadioPriority;	// See 11.2.5 0 =highest, 3=lowest
	Field<1> mRLCMode;	// 0 == acknowledged, 1 == unacknowleged mode.
	Field<1> mLLCPDUType;	// 0 == LLC PDU is SACK or ACK[acknowledged mode], 1 == its not
	Field<16> mRLCOctetCount;	// Number of octets MS wants to transfer, or 0 if unspecified.

	void parseElement(const BitVector &src, size_t &rp);
	//void textElement(std::ostream&) const;
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgChannelRequestDescriptionIE::parseElement(const BitVector &src, size_t &rp)
	{
		mPeakThroughputClass = src.readField(rp,4);
		mRadioPriority = src.readField(rp,2);
		mRLCMode = src.readField(rp,1);
		mLLCPDUType = src.readField(rp,1);
		mRLCOctetCount = src.readField(rp,16);
	}
	void RLCMsgChannelRequestDescriptionIE::writeBody(MsgCommon&dst) const {
		dst.WRITE_ITEM(mPeakThroughputClass);
		dst.WRITE_ITEM(mRadioPriority);
		dst.WRITE_ITEM(mRLCMode);
		dst.WRITE_ITEM(mLLCPDUType);
		dst.WRITE_ITEM(mRLCOctetCount);
	}

	/*
	void RLCMsgChannelRequestDescriptionIE::textElement(std::ostream&os) const
	{
		os << " ChannelRequest=(";
		RN_WRITE_TEXT(mPeakThroughputClass);
		RN_WRITE_TEXT(mRadioPriority);
		RN_WRITE_TEXT(mRLCMode);
		RN_WRITE_TEXT(mLLCPDUType);
		RN_WRITE_TEXT(mRLCOctetCount);
		os << ")";
	}
	*/
#endif

struct MSRACap_s : public RLCMsgUplinkIE
{
	Field<4> mAccessTechnologyType;	// The caller parses and sets this.
	bool mA5BitsPresent;
		Field<3> mRFPowerCapability;
	Field<7> mA5Bits;
	Field<1> mESInd;
	Field<1> mPS;
	Field<1> mVGCS;
	Field<1> mVBS;
	bool mMultiSlotCapPresent;
		bool mHSCDMultiSlotClassPresent;
			Field<5> mHSCDMultiSlotClass;
		bool mGPRSMultiSlotClassPresent;
			Field<5> mGPRSMultiSlotClass;
			Field<1> mGPRSExtendedDynamicAllocationCapability;
		bool mSMSPresent;
			Field<4> mSMS_VALUE; Field<4> mSM_VALUE;
	// There is much more, but we dont keep it.

	void parseElement(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
	//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void MSRACap_s::parseElement(const BitVector &src, size_t &rp)
	{
		mRFPowerCapability = src.readField(rp,3);
		if ((mA5BitsPresent = src.readField(rp,1))) {
			mA5Bits = src.readField(rp,7);
		}
		mESInd = src.readField(rp,1);
		mPS = src.readField(rp,1);
		mVGCS = src.readField(rp,1);
		mVBS = src.readField(rp,1);
		if ((mMultiSlotCapPresent = src.readField(rp,1))) {
			// Multislot Capability Struct:
			if ((mHSCDMultiSlotClassPresent = src.readField(rp,1))) {
				mHSCDMultiSlotClass = src.readField(rp,5);
			}
			if ((mGPRSMultiSlotClassPresent = src.readField(rp,1))) {
				mGPRSMultiSlotClass = src.readField(rp,5);
				mGPRSExtendedDynamicAllocationCapability = src.readField(rp,1);
			}
			if ((mSMSPresent = src.readField(rp,1))) {
				mSMS_VALUE = src.readField(rp,4);
				mSM_VALUE = src.readField(rp,4);
			}
		} else {
			mGPRSMultiSlotClassPresent = 0;
		}
	}
	void MSRACap_s::writeBody(MsgCommon&dst) const {
		dst.WRITE_ITEM(mAccessTechnologyType);
		dst.WRITE_ITEM(mRFPowerCapability);
		if (dst.write01(mA5BitsPresent)) { dst.WRITE_ITEM(mA5Bits); }
		dst.WRITE_ITEM(mESInd);
		dst.WRITE_ITEM(mVGCS);
		dst.WRITE_ITEM(mVBS);
		if (mMultiSlotCapPresent) {
			if (dst.write01(mHSCDMultiSlotClassPresent)) dst.WRITE_ITEM(mHSCDMultiSlotClass); 
			if (dst.write01(mGPRSMultiSlotClassPresent)) {
				dst.WRITE_ITEM(mGPRSMultiSlotClass);
				dst.WRITE_ITEM(mGPRSExtendedDynamicAllocationCapability);
			}
			if (dst.write01(mSMSPresent)) {
				dst.WRITE_ITEM(mSMS_VALUE);
				dst.WRITE_ITEM(mSM_VALUE);
			}
		}
		// More stuff ignored.
	}

	/*
	void MSRACap_s::textElement(std::ostream&os) const {
		RN_WRITE_TEXT(mAccessTechnologyType);
		RN_WRITE_TEXT(mRFPowerCapability);
		RN_WRITE_OPT_TEXT(mA5Bits,mA5BitsPresent);
		RN_WRITE_TEXT(mESInd);
		RN_WRITE_TEXT(mPS);
		RN_WRITE_TEXT(mVGCS);
		RN_WRITE_TEXT(mVBS);
		RN_WRITE_OPT_TEXT(mGPRSMultiSlotClass,mGPRSMultiSlotClassPresent);
	}
	*/
#endif

// GSM24.008sec10.5.5.12a
struct RLCMsgMSRACapabilityValuePartIE : public RLCMsgUplinkIE
{
	// There can be many of these.
	// The first one is all we are interested in.
	// We will map all the other caps into the second struct, which we wont even use.
	MSRACap_s MSRACap[2];
	int mNumMSRACap;

	void parseElement(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
	//void textElement(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgMSRACapabilityValuePartIE::parseElement(const BitVector &src, size_t &rp)
	{
		mNumMSRACap = 0;
		do {
			int AccessTechnologyType = src.readField(rp,4);
			unsigned len = src.readField(rp,7);	// Length in bits of the rest of this access cap.
			// Kyle at L3 reported that this was crashing in the src.readField below,
			// which means this struct is invalid or not being parsed properly.
			// Lets check, although it actually needs to be much bigger than this
			// because this is embedded inside a large message.
			if (len + 1 >= src.size()) {
				LOG(INFO) << "Invalid RA Capability Value Part struct"<<LOGVAR(len)<<LOGVAR(mNumMSRACap);
			}
			size_t startrp = rp;
			if (AccessTechnologyType != 0xf) {
				if (mNumMSRACap < 2) {	// Save the first two.
					MSRACap[mNumMSRACap].mAccessTechnologyType = AccessTechnologyType;
					MSRACap[mNumMSRACap].parseElement(src,rp);
					mNumMSRACap++;
				}
			} else {
				// Additional access technologies.  Just throw it away.
			}
			rp = startrp + len;
		} while (src.readField(rp,1));
	}
	void RLCMsgMSRACapabilityValuePartIE::writeBody(MsgCommon&dst) const {
		std::ostream *os;
		if ((os = dst.getStream())) {
			for (int i=0; i < mNumMSRACap; i++) {
				*os << " RACapability[" << i <<"]=(";
					MSRACap[i].writeBody(dst);
				*os << ")";
			}
		} else {
			// Not implemented.  It is an uplink IE so we dont need it.
		}
	}
	/*
	void RLCMsgMSRACapabilityValuePartIE::textElement(std::ostream&os) const
	{
		for (int i=0; i < mNumMSRACap; i++) {
			os << " RACapability[" << i <<"]=(";  MSRACap[i].textElement(os); os << ")";
		}
	}
	*/
#endif

// GSM 04.60 12.10
struct RLCMsgGlobalTFIIE : public RLCMsgDownlinkIE, public RLCMsgUplinkIE
{
	Field_z<1> mIsDownlinkTFI;	// 1 for downlink TFI, 0 for uplink TFI
	Field_z<5> mGTFI;	// This is NOT a TFI assignment; it is used
							// to identify the MS which receives this message.

	void setTFI(bool wIsDownlinkTFI,int wTFI) {
		// TBF uninitialized TFI is -1; it is an error if that value finds its way here.
		assert(wTFI >= 0 && wTFI < 32);
		mIsDownlinkTFI = wIsDownlinkTFI; mGTFI = wTFI;
	}
	void parseElement(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgGlobalTFIIE::parseElement(const BitVector &src, size_t &rp) {
		mIsDownlinkTFI = src.readField(rp,1);
		mGTFI = src.readField(rp,5);
	}
	void RLCMsgGlobalTFIIE::writeBody(MsgCommon &dst) const {
		dst.WRITE_ITEM(mIsDownlinkTFI);
		dst.WRITE_ITEM(mGTFI);
	}
#endif

// 3GPP 44-060 11.2.6
// See 45.008 for definitions of these things.
struct ChannelQualityReportIE : public RLCMsgUplinkIE
{

	// 45.008 10.2.3: "C Value is the enormalized received signal level at the MS
	//		as defined in 10.2.3.1".
	// During Packet Transfer mode (which we are for a DownlinkAckNack)
	// C Value and SIGN_VAR (variance of C Value) are measured from BCCH and
	// subsequently used to determine MS output power as per 10.2.1.
	Field<6> mCValue;
	// RXQUAL is derived from decoder BEP [Bit Error Probability]
	Field<3> mRXQual;	// From decoder, low is better, meaningless for CS-4 encoding.
	// SIGN_VAR from 0 to 64 encoded as: 0 to 15.75dB in 0.25dB steps.
	Field<6> mSignVar;
	// 45.008 10.3: I_LEVEL is measured interference level on each channel.
	Bool_z mHaveILevel[8];
	Field<4> mILevel[8];	// For each timeslot.
	void parseElement(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void ChannelQualityReportIE::parseElement(const BitVector &src, size_t &rp) {
		mCValue = src.readField(rp,6);
		mRXQual = src.readField(rp,3);
		mSignVar = src.readField(rp,6);
		for (int i = 0; i <= 7; i++) {
			if ((mHaveILevel[i] = src.readField(rp,1))) {
				mILevel[i] = src.readField(rp,4);
			}
		}
	}
	void ChannelQualityReportIE::writeBody(MsgCommon&dst) const {
		dst.WRITE_ITEM(mCValue);
		dst.WRITE_ITEM(mRXQual);
		dst.WRITE_ITEM(mSignVar);
		for (int i = 0; i <= 7; i++) {
			if (mHaveILevel[i]) { dst.WRITE_ITEM(mILevel[i]); }
		}
	}
#endif

/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketDownlinkAckNack : public RLCUplinkMessage
{
	Field<5> mTFI;
	RLCMsgPacketAckNackDescriptionIE mAND;
	bool mHaveChannelRequest;
	RLCMsgChannelRequestDescriptionIE mCRD;
	ChannelQualityReportIE mCQR;

	// We can ignore the rest of the message.
	// struct ChannelQualityReport CQR;
	// unsigned PFI:7 // (we wont use it)

	RLCMsgPacketDownlinkAckNack(const RLCRawBlock *src) { parse(src); }

	int getTFI() { return mTFI; }

	void parseBody(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
	//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketDownlinkAckNack::parseBody(const BitVector &src, size_t &rp)
	{
		mTFI = src.readField(rp,5);
		mAND.parseElement(src,rp);
		mHaveChannelRequest = src.readField(rp,1);
		if (mHaveChannelRequest) { mCRD.parseElement(src,rp); }
		mCQR.parseElement(src,rp);
	}
	void RLCMsgPacketDownlinkAckNack::writeBody(MsgCommon&dst) const {
		dst.WRITE_ITEM(mTFI);
		mAND.writeBody(dst);
		if (dst.write01(mHaveChannelRequest)) { mCRD.writeBody(dst); }
		mCQR.writeBody(dst);
	}

	/*
	void RLCMsgPacketDownlinkAckNack::textBody(std::ostream&os) const
	{
		RN_WRITE_TEXT(mTFI);
		mAND.textElement(os);
		if (mHaveChannelRequest) { mCRD.textElement(os); }
	}
	*/
#endif

//static uint64_t readOptField(BitVector&src,size_t&rp,int bits,bool&option) {
//	if ((option = src.readField(rp,1))) {
//		return src.readField(rp,bits);
//	}
//}


/** GSM04.60 11.2.6 From MS to network. */
struct RLCMsgPacketControlAcknowledgement : public RLCUplinkMessage
{
	Field<32> mTLLI;		// Yes, this really is not byte aligned in the message.
	Field<2> mCtrlAck;	// Which segments of the RLC/MAC control message were received?

	RLCMsgPacketControlAcknowledgement(const RLCRawBlock *src) { parse(src); }
	void parseBody(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
	//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketControlAcknowledgement::parseBody(const BitVector&src, size_t&rp)
	{
		mTLLI = src.readField(rp,32);
		mCtrlAck = src.readField(rp,2);
	}
	void RLCMsgPacketControlAcknowledgement::writeBody(MsgCommon&dst) const {
		dst.writeField(mTLLI,32,"TLLI",tohex);
		dst.WRITE_ITEM(mCtrlAck);
	}

	/*
	void RLCMsgPacketControlAcknowledgement::textBody(std::ostream&os) const
	{
		RN_WRITE_TEXT(mTLLI);
		RN_WRITE_TEXT(mCtrlAck);
	}
	*/
#endif

/** GSM04.60 11.2.16 From MS to network. */
struct RLCMsgPacketResourceRequest : public RLCUplinkMessage
{
	bool mAccessTypePresent; // 0=two phase, 1=page response, 2=cell update, 3=mm procedure
	Field<2> mAccessType;
	RLCMsgGlobalTFIIE mGTFI;
	bool mTLLIPresent;	// 1 for TLLI present, 0 for TFI present.
	Field<32> mTLLI;
	bool mMSRadioAccessCapability2Present;
	RLCMsgMSRACapabilityValuePartIE mMSRACap;
	RLCMsgChannelRequestDescriptionIE mCRD;
	bool mChangeMarkPresent;
	Field<2> mChangeMark;
	Field<5> mCValue;
	bool mSignVarPresent;
	Field<6> mSignVar;	// Not present for two phase access.
	bool mILevelPresent[8];
	Field<4> mILevelTN[8];
	bool mExtensionsPresent;

	RLCMsgPacketResourceRequest(const RLCRawBlock *src) { parse(src); }

	// Return the MSInfo identified by the contents of this message.
	MSInfo *getMS(PDCHL1FEC *chan, bool create);

	void parseBody(const BitVector &src, size_t &rp);
	void writeBody(MsgCommon&dst) const;
	//void textBody(std::ostream&) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketResourceRequest::parseBody(const BitVector &src, size_t &rp)
	{
		if ((mAccessTypePresent = src.readField(rp,1))) { mAccessType = src.readField(rp,2); }
		if ((mTLLIPresent = src.readField(rp,1))) {
			mTLLI = src.readField(rp,32);
		} else {
			mGTFI.parseElement(src,rp);
		}

		if ((mMSRadioAccessCapability2Present = src.readField(rp,1))) {
			mMSRACap.parseElement(src,rp);
		}

		mCRD.parseElement(src,rp);	// We will use this.
		if ((mChangeMarkPresent = src.readField(rp,1))) { mChangeMark = src.readField(rp,2); }
		mCValue = src.readField(rp,6);
		if ((mSignVarPresent = src.readField(rp,1))) { mSignVar = src.readField(rp,2); }
		for (int i = 0; i < 8; i++) {
			mILevelTN[i] = (mILevelPresent[i] = src.readField(rp,1)) ? src.readField(rp,4) : -1;
		}
		mExtensionsPresent = rp < src.size() && src.readField(rp,1);
		// But ignore the exensions.
	}
	void RLCMsgPacketResourceRequest::writeBody(MsgCommon&dst) const {
		if (dst.write01(mAccessTypePresent)) { dst.WRITE_ITEM(mAccessType); }
		if (dst.write01(mTLLIPresent)) {
			dst.writeField(mTLLI,32,"TLLI",tohex);
		} else {
			mGTFI.writeBody(dst);
		}
		if (dst.write01(mMSRadioAccessCapability2Present)) {
			mMSRACap.writeBody(dst);
		}
		mCRD.writeBody(dst);
		if (dst.write01(mChangeMarkPresent)) { dst.WRITE_ITEM(mChangeMark); }
		dst.WRITE_ITEM(mCValue);
		if (dst.write01(mSignVarPresent)) { dst.WRITE_ITEM(mSignVar); }
		for (int i = 0; i < 8; i++) {
			if (dst.write01(mILevelPresent[i])) dst.WRITE_ITEM(mILevelTN[i]);
		}
		dst.writeField(mExtensionsPresent,1,"mExtensionsPresent");
	}

	/*
	void RLCMsgPacketResourceRequest::textBody(std::ostream&os) const {
		RN_WRITE_OPT_TEXT(mAccessType,mAccessTypePresent);
		RN_WRITE_OPT_TEXT(mTLLI,mTLLIPresent);
		RN_WRITE_OPT_TEXT(mIsDownlinkTFI,!mTLLIPresent);
		RN_WRITE_OPT_TEXT(mTFI,!mTLLIPresent);
		if (mMSRadioAccessCapability2Present) { mMSRACap.textElement(os); } 
		mCRD.textElement(os);
		RN_WRITE_OPT_TEXT(mChangeMark,mChangeMarkPresent);
		RN_WRITE_TEXT(mCValue);
		RN_WRITE_OPT_TEXT(mSignVar,mSignVarPresent);
		for (int i = 0; i < 8; i++) {
			RN_WRITE_OPT_TEXT(mILevelTN[i],mILevelTN[i] >= 0);
		}
	}
	*/
#endif

/** GSM04.60 11.2.8b From MS to network. */
struct RLCMsgPacketUplinkDummyControlBlock : public RLCUplinkMessage
{
	Field<32> mTLLI;

	RLCMsgPacketUplinkDummyControlBlock(const RLCRawBlock*src) { parse(src); }

	void parseBody(const BitVector &src, size_t &rp);
	void textBody(std::ostream&) const;
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketUplinkDummyControlBlock::parseBody(const BitVector &src, size_t &rp)
	{
		mTLLI = src.readField(rp,32);
	}
	void RLCMsgPacketUplinkDummyControlBlock::writeBody(MsgCommon&dst) const {
		dst.writeField(mTLLI,32,"TLLI",tohex);
	}

	/*
	void RLCMsgPacketUplinkDummyControlBlock::textBody(std::ostream&os) const
	{
		RN_WRITE_TEXT(mTLLI);
	};
	*/
#endif


// GSM04.60 12.12
struct RLCMsgPacketTimingAdvanceIE : public RLCMsgDownlinkIE
{
	// Timing Advance defined in GSM05.10
	Field_z<1> mHaveTimingAdvanceValue;
	Field_z<6> mTimingAdvanceValue;		// New timing advance if present.
	// We dont use the following:
	// Timeslot number defined in GSM05.10
	Field_z<1> mHaveContinuousTiming;	// Controls next two fields:
	Field_z<4> mTimingAdvanceIndex;		// Used for continuous timing advance.
	Field_z<3> mTimingAdvanceTimeslotNumber;	// Timeslot that gets the continuous timing advance.

	void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketTimingAdvanceIE::writeBody(MsgCommon &dst) const {
		if (dst.write01(mHaveTimingAdvanceValue)) {
			dst.WRITE_ITEM(mTimingAdvanceValue);
		}
		if (dst.write01(mHaveContinuousTiming)) {
			dst.WRITE_ITEM(mTimingAdvanceIndex);
			dst.WRITE_ITEM(mTimingAdvanceTimeslotNumber);
		}
	}
#endif

// GSM04.60 12.12a - same as TimingAdvanceIE but allows separate
// values for uplink and downlink.
struct RLCMsgPacketGlobalTimingAdvanceIE : public RLCMsgDownlinkIE
{
	// Timing Advance defined in GSM05.10
	Bool_z mHaveTimingAdvanceValue;
	Field_z<6> mTimingAdvanceValue;		// New timing advance if present.
	// Timeslot number defined in GSM05.10
	// We dont use the following:
	Bool_z mHaveUplinkContinuousTiming;	// Controls next two fields:
	Field_z<4> mUplinkTimingAdvanceIndex;		// Used for continuous timing advance.
	Field_z<3> mUplinkTimingAdvanceTimeslotNumber;	// Timeslot that gets the continuous timing advance.
	Bool_z mHaveDownlinkContinuousTiming;	// Controls next two fields:
	Field_z<4> mDownlinkTimingAdvanceIndex;		// Used for continuous timing advance.
	Field_z<3> mDownlinkTimingAdvanceTimeslotNumber;	// Timeslot that gets the continuous timing advance.

	void setTimingAdvance(int ta) { mHaveTimingAdvanceValue=1; mTimingAdvanceValue=ta; }

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketGlobalTimingAdvanceIE::writeBody(MsgCommon &dst) const {
		if (dst.write01(mHaveTimingAdvanceValue)) {
			dst.WRITE_ITEM(mTimingAdvanceValue);
		}
		if (dst.write01(mHaveUplinkContinuousTiming)) {
			dst.WRITE_ITEM(mUplinkTimingAdvanceIndex);
			dst.WRITE_ITEM(mUplinkTimingAdvanceTimeslotNumber);
		}
		if (dst.write01(mHaveDownlinkContinuousTiming)) {
			dst.WRITE_ITEM(mDownlinkTimingAdvanceIndex);
			dst.WRITE_ITEM(mDownlinkTimingAdvanceTimeslotNumber);
		}
	}
#endif

// GSM04.60 12.13
struct RLCMsgPowerControlParametersIE : public RLCMsgDownlinkIE
{
	Field_z<4> mAlpha;		// 4 bits.
	Bool_z mHaveTN[8];
	Field_z<5> mGammaTN[8];		// 5 bits each
	void setAlpha(int alpha) { mAlpha = alpha; }
	void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHaveTN[timeslot] = true; }
	void setFrom(TBF *tbf);

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPowerControlParametersIE::writeBody(MsgCommon &dst) const {
		dst.WRITE_ITEM(mAlpha);
		for (int i=0; i<8; i++) {
			if (dst.write01(mHaveTN[i])) dst.WRITE_ITEM(mGammaTN[i]);
		}
	}
#endif

// This is not an official Informaion Element, but is commonly used in several messages.
// Used to identify to the MS targetted for a downlink message.
// The MS may be identified by TLLI, uplink TFI, or downlink TFI.
// In some messages, can also use TQI  or Packet Request Reference,
// but we dont use those.
// This is not an official "Information Element", but it is used the
// same way in many downlink messages, since they all have to target an MS.
struct RLCMsgGlobalTFIorTLLIElt : public RLCMsgDownlinkIE
{
	Bool_z mHaveGlobalTFI;
	// Global TFI includes the following two fields:
	RLCMsgGlobalTFIIE mGlobalTFI; // This is NOT a TFI assignment; it is used
							// to identify the MS which receives this message.
	Bool_z mHaveTLLI;
	Field_z<32> mTLLI;

	void setTLLI(int tlli) {
		mHaveGlobalTFI=0; mHaveTLLI=1; mTLLI=tlli;
	}
	void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
		mHaveGlobalTFI = 1; mGlobalTFI.setTFI(wIsDownlinkTFI,wTFI);
	}

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgGlobalTFIorTLLIElt::writeBody(MsgCommon &dst) const {
		if (mHaveGlobalTFI) {
			dst.write0();
			mGlobalTFI.writeBody(dst);
		} else if (mHaveTLLI) {
			dst.write1();
			dst.write0();
			dst.writeField(mTLLI,32,"TLLI",tohex);
		} else {
			// Could be TQI  or Packet Request Reference
			assert(0);
		}
	}
#endif

// GSM04.60 12.21
class RLCMsgStartingFrameNumberIE : public RLCMsgDownlinkIE
{
	public:
	Field_z<1> mAbsoluteOrRelative;	// 0 => absolute, 1 => relative
	Field_z<16> mAbsoluteFrameNumber;
	Field_z<13> mRelativeFrameNumber;

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgStartingFrameNumberIE::writeBody(MsgCommon &dst) const {
		//dst.writeField(mAbsoluteOrRelative,1);
		dst.WRITE_ITEM(mAbsoluteOrRelative);
		if (mAbsoluteOrRelative) {
			dst.WRITE_ITEM(mRelativeFrameNumber);
		} else {
			dst.WRITE_ITEM(mAbsoluteFrameNumber);
		}
	}
#endif

// GSM04.60 11.2.29
// Also used in Timeslot Reconfigure message 44.060 11.2.31
class RLCMsgPacketUplinkAssignmentDynamicAllocationElt : public RLCMsgDownlinkIE
{
	public:
	Field_z<1> mExtendedDynamicAllocation;	// Needed if number up > number down
	// P0, PR_MODE unused.
	Field_z<1> mUSFGranularity;				// Use 0 for USF specified per-block.

	// In the TimeslotReconfigure message, this is never present, always one-bit 0:
	Bool_z mUplinkTFIAssignmentPresent;
	Field_z<5> mUplinkTFIAssignment;

	//Bool_z mRLCDataBlocksGrantedPresent;
	//Field_z<8> mRLCDataBlocksGranted;

	Field_z<1> mTBFStartingTimePresent;
	RLCMsgStartingFrameNumberIE mTBFStartingTime;

	// There are two types of Timeslot allocation, with and without power control params.
	Bool_z mHavePower;	// Set if gamma supplied for any timeslot.
	Field_z<4> mAlpha;		// 4 bits.
	Bool_z mHaveTN[8];
	Field_z<3> mUSFTN[8];		// 3 bits each.
	Field_z<5> mGammaTN[8];		// 5 bits each

	void setUplinkTFI(int tfi) { mUplinkTFIAssignmentPresent=1; mUplinkTFIAssignment=tfi; }
	void setUSF(int timeslot, int usf) { mUSFTN[timeslot] = usf; mHaveTN[timeslot] = true; }
	void setGamma(int timeslot, int gamma) { mGammaTN[timeslot] = gamma; mHavePower = true; }
	void setAlpha(int alpha) { mAlpha = alpha; mHavePower = true; }
	void setFrom(TBF *tbf,MultislotSymmetry);

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketUplinkAssignmentDynamicAllocationElt::writeBody(MsgCommon &dst) const {
		dst.WRITE_ITEM(mExtendedDynamicAllocation);
		dst.write0();	// No P0, PR_MODE.
		dst.WRITE_ITEM(mUSFGranularity);
		//dst.WRITE_OPT_FIELD01(mUplinkTFIAssignment,5,mUplinkTFIAssignmentPresent);
		dst.WRITE_OPT_ITEM01(mUplinkTFIAssignment,mUplinkTFIAssignmentPresent);
		// 44.060 removed the RLC Data Blocks Granted field, just inserts a 0 bit here.
		// dst.WRITE_OPT_ITEM01(mRLCDataBlocksGranted,mRLCDataBlocksGrantedPresent);
		dst.write0();	// RLC Data Blocks Granted removed from 44.060 11.2.29
		//dst.writeField(mTBFStartingTimePresent,1);
		dst.WRITE_ITEM(mTBFStartingTimePresent);
		if (mTBFStartingTimePresent) {
			mTBFStartingTime.writeBody(dst);
		}

		char name[14];	// Make the output pretty.
		strcpy(name,"USF_TN?");
		char *end = name+strlen(name)-1;
		if (dst.write01(mHavePower)) {
			dst.WRITE_ITEM(mAlpha);
			for (int i=0; i<8; i++) {
				if (dst.write01(mHaveTN[i])) {
					*end = '0' + i;
					dst.writeField(mUSFTN[i],3,name);
					dst.writeField(mGammaTN[i],5,"Gamma");
				}
			}
		} else {
			for (int i=0; i<8; i++) {
				if (dst.write01(mHaveTN[i])) {
					*end = '0' + i;
					dst.writeField(mUSFTN[i],3,name);
				}
			}
		}
	}
#endif


/** GSM04.60 11.2.29 From network to MS */
class RLCMsgPacketUplinkAssignment : public RLCDownlinkMessage
{
	public:
	//Bool_z mPersistenceLevelPresent;
	//Field_z<4> mPersistenceLevel[4];		// This is for PRACH control, so we wont use it.

	// mTQI		// We wont use it.
	// mPacketRequestReference // We wont use it.
	RLCMsgGlobalTFIorTLLIElt mMSID;
	void setTLLI(int tlli) { mMSID.setTLLI(tlli); }

	// Currently we only support CS-1 for uplink.
	ChannelCodingType mChannelCodingCommand;	// MS to use which: CS-1, etc.

	// For one phase access, the MS must send the TLLI in an RLC Data Block early on.
	// TLLIBlockChannelCoding controls the coding of that block.  We are not using it.
	Field_z<1>  mTLLIBlockChannelCoding;	// 0 MS to use CS-1 for TLLI block, 1 to use mChannelCoding.
	RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
	void setTimingAdvance(int ta) { mPacketTimingAdvance.setTimingAdvance(ta); }

	// FreqParams specifies an ARFCN, TSC (Training Sequence Code) frequency hopping, etc.
	// FrequenceParameters;	// We dont use it.

	// Finally, the allocation itself:  We only support dynamic on uplink for now.
	Field_z<2> mAllocationType;	// 1 => dynamic, 2 => single block, 3 => fixed

	RLCMsgPacketUplinkAssignmentDynamicAllocationElt mDynamicAllocation;
	RLCMsgPacketUplinkAssignmentDynamicAllocationElt *setDynamicAllocation() {
		mAllocationType = 1;
		return &mDynamicAllocation;
	}
	
	RLCMsgPacketUplinkAssignment(TBF *tbf)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketUplinkAssignment,true,tbf),
		mChannelCodingCommand(tbf->mtChannelCoding())
		//mChannelCodingCommand(ChannelCodingCS1)
	{ 
		// Blackberry was using CS-1 when commanded to use CS-4, and it shouldnt matter
		// what we put here, so try 1.  Didnt help, back to 0
		mTLLIBlockChannelCoding = 0;
	}

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketUplinkAssignment::writeBody(MsgCommon &dst) const {
		dst.write0();	// No persistence Level field.
		// MSID Could also be TQI or Packet Request Reference in this message; we dont support them.
		mMSID.writeBody(dst);
		dst.write0();	// Message escape, means more stuff follows.
		dst.WRITE_FIELD(mChannelCodingCommand,2);
		dst.WRITE_ITEM(mTLLIBlockChannelCoding);
		mPacketTimingAdvance.writeBody(dst);
		dst.write0();	// No frequency parameters.
		dst.WRITE_ITEM(mAllocationType);
		switch (mAllocationType) {
			case 0:	// reserved.
				assert(0);
			case 1: // dynamic allocation
				mDynamicAllocation.writeBody(dst);
				break;
			case 2: // single block allocation
				assert(0);
			case 3: // fixed allocation
				assert(0);
		}
		dst.write0();	// One more 0 required to mark no Packet Extended Timing Advance.
	}
#endif

/** GSM 04.60 11.2.7 From network to MS */
// Unlike an uplink assignment there is no ChannelCoding in the downlink assignment
// because it is passed in the qbits in each radio block.
// See: PDCHL1Downlink::transmit(BitVector *mI, const int *qbits)
class RLCMsgPacketDownlinkAssignment : public RLCDownlinkMessage
{
	public:

	//Bool_z mPersistenceLevelPresent;
	//Field_z<r> mPersistenceLevel[4];		// This is for PRACH control, so we wont use it.

	RLCMsgGlobalTFIorTLLIElt mMSID;
	void setTLLI(int tlli) { mMSID.setTLLI(tlli); }

	Field_z<2> mMACMode;	// 0 == dynamic, 2 = fixed.  Not sure why this is here.
	Field_z<1> mRLCMode;	// 0 == acknowledged, 1 == unacknowleged mode.
	Field_z<1> mControlAck;	// Set if establishing a new downlink TBF and T3192 is running.
							// We only use this message to change an existing downlink,
							// so always 0.  Not sure how it could ever be 1.
	Field_z<8> mTimeslotAllocation;
	RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
	void setTimingAdvance(int ta) { mPacketTimingAdvance.setTimingAdvance(ta); }
	// No P0, BTS_PWR_CTRL_MODE, PR_MODE.
	// No Frequence Parameters
	Bool_z mHaveDownlinkTFIAssignment;
	Field_z<5> mDownlinkTFIAssignment;	// Assigned TFI for this downlink.
	Bool_z mHavePowerControlParameters;
	RLCMsgPowerControlParametersIE mPowerControlParameters;
	// No TBF Starting Time.
	// No Measurement Mapping.

	RLCMsgPacketDownlinkAssignment(TBF *tbf, bool isNewAssignment);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	RLCMsgPacketDownlinkAssignment::RLCMsgPacketDownlinkAssignment(TBF *tbf,bool isNewAssignment)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketDownlinkAssignment,true,tbf)
	{
		mTimeslotAllocation = tbf->mtMS->msGetDownlinkTimeslots(MultislotSymmetric);
		// We dont track T3192, we track T3193, which is (almost) the same thing.
		mControlAck = isNewAssignment ? tbf->mtMS->msT3193.active() : 0;
		// 5-13-2012 I tried setting this to true all the time, and it seemed to make no difference
		// on the Blackberry, in particular, did not get rid of the cause=3105 retries.
		// 5-23-2012: After a 3105 failure we retry with a new TBF.  That tbf was established
		// with controlack=false for some reason, and based on its first downlinkacknack, it clearly
		// took over from the previous TBF rather than establishing a new one.
		// So I am setting this to true permanently.
		// 6-18: The above is all fixed.  The Blackberry ignores ControlAck
		// but other phones do not, so it must be set properly.

		assert(tbf->mtTFI >= 0);
		mHaveDownlinkTFIAssignment = true;
		mDownlinkTFIAssignment = tbf->mtTFI;
		mHavePowerControlParameters = true;
		mPowerControlParameters.setFrom(tbf);
	}

	void RLCMsgPacketDownlinkAssignment::writeBody(MsgCommon &dst) const {
		dst.write0();	// No persistence level yet.
		mMSID.writeBody(dst); // MS identified by either uplink or downlink TFI.
		dst.write0();	// Message escape, means more stuff follows.
		// Next comes MAC_MODE, dynamic, fixed, etc.
		// This is mostly meaningless for a downlink, and I dont know why it is here.
		// Except if assigned fixed slots, the MS can do other stuff when its not busy
		// listening to us.
		dst.WRITE_ITEM(mMACMode);
		dst.WRITE_ITEM(mRLCMode);
		dst.WRITE_ITEM(mControlAck);
		dst.WRITE_ITEM(mTimeslotAllocation);
		mPacketTimingAdvance.writeBody(dst);
		dst.write0();	// No P0, PR_MODE.
		dst.write0();	// No Frequency Parameters.
		if (dst.write01(mHaveDownlinkTFIAssignment)) {
			dst.WRITE_ITEM(mDownlinkTFIAssignment);
		}
		mPowerControlParameters.writeOptional01(dst,mHavePowerControlParameters);
		dst.write0();	// No TBF Starting time.
		dst.write0();	// No Measurement mapping.
		dst.write0();	// End of message (no EGPRS, etc, stuff.)
	}
#endif

// 44.060 11.2.31
// This message does not include a TLLI so it can not be used to initiate
// an uplink or downlink assignment.  This message includes tfi assignment
// elements, but they are only to change the tfi of an existing TBF.
// The normal uplink and downlink assignment messages can only allocate PDCH
// symmetrically, ie, 2-up/2-down, and we must use this message to change
// to any other multislot mode.
class RLCMsgPacketTimeslotReconfigure : public RLCDownlinkMessage
{
	public:
	RLCMsgGlobalTFIIE mGlobalTFI; // This is NOT a TFI assignment; it is used
							// to identify the MS which receives this message.

	// This IE has timing advance for both uplink and downlink.
	// We dont need it so unimplemented, just use three '0' bits instead.
	// GLOBAL_PACKET_TIMING_ADVANCE

	// Elements for downlink modification:
	Field_z<1> mDownlinkRlcMode;	// 0 == acknowledged, 1 == unacknowleged mode.
									// Always acknowledged for us.
	Field_z<1> mControlAck;	// Set if T3192 is running in MS.
	// DOWNLINK_TFI_ASSIGNMENT unused
	// UPLINK_TFI_ASSIGNMENT unused
	Field_z<8> mDownlinkTimeslotAllocation;

	// FrequenceParameters;	// We dont use it.

	// Elements for an uplink modification:
	ChannelCodingType mChannelCodingCommand;
	RLCMsgPacketUplinkAssignmentDynamicAllocationElt mDynamicAllocation;
	RLCMsgPacketUplinkAssignmentDynamicAllocationElt *setDynamicAllocation() {
		//mAllocationType = 1;
		return &mDynamicAllocation;
	}
	
	RLCMsgPacketTimeslotReconfigure(TBF *tbf);

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	RLCMsgPacketTimeslotReconfigure::RLCMsgPacketTimeslotReconfigure(TBF *tbf)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketTimeslotReconfigure,true,tbf)
	{
		mGlobalTFI.setTFI(tbf->mtDir == RLCDir::Down,tbf->mtTFI);
		mDownlinkTimeslotAllocation = tbf->mtMS->msGetDownlinkTimeslots(MultislotFull);
		mChannelCodingCommand = tbf->mtChannelCoding();
		// Must not call mDynamicAllocationsetUplinkTFI()
		mDynamicAllocation.setFrom(tbf,MultislotFull);
	}
	void RLCMsgPacketTimeslotReconfigure::writeBody(MsgCommon &dst) const
	{
		// PAGE_MODE is handled by the parent class
		dst.write0();	// unlabeled extra 0 bit.
		mGlobalTFI.writeBody(dst);
		dst.write0();	// message escape: not EGPRS
		dst.WRITE_FIELD(mChannelCodingCommand,2);
		dst.writeField(0,3);	// Skip Global Packet Timing Advance
		dst.WRITE_ITEM(mDownlinkRlcMode);
		dst.WRITE_ITEM(mControlAck);
		dst.write0();			// no downlink tfi assignment
		dst.write0();			// no uplink tfi assignment
		dst.WRITE_ITEM(mDownlinkTimeslotAllocation);
		dst.write0();			// no frequence parameters
		dst.write0();			// just an extra 0 bit.
		mDynamicAllocation.writeBody(dst);
		dst.write0();			// Marks end of message, no extensions.
	}
#endif

/** GSM 04.60 11.2.1 From network to MS */
class RLCMsgPacketAccessReject : public RLCDownlinkMessage
{
	public:
	Field_z<32> mTLLI;
	// GlobalTFI mGlobalTFI;	We dont use.
	Bool_z mHaveWaitIndication;
	Field_z<8> mWaitIndication;
	Field_z<1> mWaitIndicationSize;
	
	RLCMsgPacketAccessReject(TBF *tbf)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketAccessReject,false,NULL),
		mTLLI(tbf->mtMS->msTlli)
	{ }

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketAccessReject::writeBody(MsgCommon &dst) const {
		dst.write1();	// Start of reject struct.
		dst.write0();	// Indicate we have a TLLI
		dst.writeField(mTLLI,32,"TLLI",tohex);
		if (dst.write01(mHaveWaitIndication)) {
			dst.WRITE_ITEM(mWaitIndication);
			dst.WRITE_ITEM(mWaitIndicationSize);
		}
		dst.write0();	// No more reject structs.
	}
#endif

/** GSM 04.60 11.2.26 From network to MS */
class RLCMsgPacketTBFRelease : public RLCDownlinkMessage
{
	public:
	RLCMsgGlobalTFIIE mGlobalTFI;
	Field_z<1> mUplinkRelease;
	Field_z<1> mDownlinkRelease;
	Field_z<4> mTBF_RELEASE_CAUSE;
	RLCMsgPacketTBFRelease(TBF *tbf);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	RLCMsgPacketTBFRelease::RLCMsgPacketTBFRelease(TBF *tbf)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketTBFRelease,true,tbf)
	{
		assert(tbf->mtTFI != -1);	// TBF has not been released yet.
		mGlobalTFI.mGTFI = tbf->mtTFI;
		if (tbf->mtDir == RLCDir::Down) {
			mGlobalTFI.mIsDownlinkTFI = true;
			mDownlinkRelease = true;
		} else {
			mGlobalTFI.mIsDownlinkTFI = false;
			mUplinkRelease = true;
		}
		mTBF_RELEASE_CAUSE = 2;	// Abnormal release, the only reason we will ever do this.
	}
	void RLCMsgPacketTBFRelease::writeBody(MsgCommon &dst) const {
		dst.write0();		// Just an extra 0 in the spec.
		mGlobalTFI.writeBody(dst);
		dst.WRITE_ITEM(mUplinkRelease);
		dst.WRITE_ITEM(mDownlinkRelease);
		dst.WRITE_ITEM(mTBF_RELEASE_CAUSE);
	}
#endif


/** GSM 04.60 11.2.28 From network to MS */
// Note that description in the GSM documentation is indented out of sync
// with the options; be careful reading it.
class RLCMsgPacketUplinkAckNack : public RLCDownlinkMessage
{
	private:
	Field_z<5> mUplinkTFI;
	ChannelCodingType mChannelCodingCommand;
	RLCMsgPacketAckNackDescriptionIE mAND;
	// 12.12b: Allows larger timing advance values. We wont use.
	Bool_z mPacketExtendedTimingAdvancePresent;
	Field_z<2> mPacketExtendedTimingAdvance;
	// 4.60 9.2.3.4 Describes TBF_EST use.
	// For dynamic uplink (which we use) the MS may return either a ControlAcknowledgement
	// or a PacketResourceRequest in the RRBP reservation to this message.
	Field<1> mTBFEst;		// If true, MS may request a new uplink TBF in the response Packet Control Acknowledgment.
							// It helps avoid raches in the case where there is an
							// uplink and no downlink.

	// The contention resolution TLLI is needed for one-phase uplink access, for which
	// the network does not know the TLLI when it does the uplink resource assignment,
	// and has to get it from the uplink data; this specifies that the necessary
	// TLLI is inside this block somewhere.
	// We arent using this.
	//bool mHaveContentionResolutionTLLI;
	//unsigned mContentionResolutionTLLI;

	// We dont need to supply timing advance or power control in this message,
	// because we supplied these in the assignment message.
	Bool_z mHavePacketTimingAdvance;
	RLCMsgPacketTimingAdvanceIE mPacketTimingAdvance;
	void setTimingAdvance(int ta) {
		mHavePacketTimingAdvance = true;
		mPacketTimingAdvance.setTimingAdvance(ta);
	}

	Bool_z mHavePowerControlParameters;
	RLCMsgPowerControlParametersIE mPowerControlParameters;

	public:
	RLCMsgPacketUplinkAckNack(TBF *wtbf, const RLCMsgPacketAckNackDescriptionIE& wAND);
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	RLCMsgPacketUplinkAckNack::RLCMsgPacketUplinkAckNack(
		TBF *wtbf, const RLCMsgPacketAckNackDescriptionIE& wAND)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketUplinkAckNack,false,wtbf),
		mUplinkTFI(wtbf->mtTFI),
		mChannelCodingCommand(wtbf->mtChannelCoding()),
		mAND(wAND),
		// 6-26-2012: set TBF_EST true by default.
		mTBFEst(gConfig.getBool("GPRS.TBF.EST")) // Allow MS to request another uplink assignment instead of Packet Control Acknowledgment in RRBP response.
	{
		assert(wtbf->mtDir == RLCDir::Up);
		setTimingAdvance(wtbf->mtMS->msGetTA());
	}
	void RLCMsgPacketUplinkAckNack::writeBody(MsgCommon &dst) const {
		dst.write0();	// two extra 0 after PAGE_MODE.
		dst.write0();
		dst.WRITE_ITEM(mUplinkTFI);
		dst.write0();	// message escape - indicates normal (non-EGPRS) message.
		dst.WRITE_FIELD(mChannelCodingCommand,2);
		mAND.writeBody(dst);
		dst.write0();	// no CONTENTION_RESOLUTION_TLLI
		if (dst.write01(mHavePacketTimingAdvance)) {
			mPacketTimingAdvance.writeBody(dst);
		}
		if (dst.write01(mHavePowerControlParameters)) {
			mPowerControlParameters.writeBody(dst);
		}
		dst.write0();	// no Extension Bits.
		dst.write0();	// no Fixed Allocation Parameters.
		// Note that the indentation in the documentation here is misleading.
		dst.write1();	// Enable additions for R99
		if (dst.write01(mPacketExtendedTimingAdvance)) {
			dst.WRITE_ITEM(mPacketExtendedTimingAdvance);
		}
		dst.WRITE_ITEM(mTBFEst);
	}
#endif

// GSM04.60 11.2.8 From network to MS
struct RLCMsgPacketDownlinkDummyControlBlock : public RLCDownlinkMessage
{
	//Field_z<1> mPersistenceLevelPresent;
	//Field_z<4> mPersistenceLevel[4];		// This is for PRACH control, so we wont use it.

	RLCMsgPacketDownlinkDummyControlBlock()
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketDownlinkDummyControlBlock,false,NULL)
	{ RN_MEMCHKNEW(RLCMsgPacketDownlinkDummyControlBlock) }

	~RLCMsgPacketDownlinkDummyControlBlock() { RN_MEMCHKDEL(RLCMsgPacketDownlinkDummyControlBlock) }

	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketDownlinkDummyControlBlock::writeBody(MsgCommon &dst) const {
		dst.write0();	// No Persistence Level
	}
#endif

///** A factory function for uplink messages */
//RLCUplinkMessage *RLCUplinkFactory(RLCUplinkMessage::MTI);

///** Generic RLC uplink parser */
extern RLCUplinkMessage* RLCUplinkMessageParse(RLCRawBlock *src);


/** GSM 04.60 11.2.13 From network to MS */
// Note that description in the GSM documentation is indented out of sync
// with the options; be careful reading it.
class RLCMsgPacketPowerControlTimingAdvance : public RLCDownlinkMessage
{
	// The MS may be identified by GlobalTFI, TQI, or PacketRequestReference,
	// but we only use GlobalTFI, so I left the other methods out:
	Bool_z mHaveGlobalTFI;	// Better be true.
	RLCMsgGlobalTFIIE mGlobalTFI;				// 12.10
	// Global Power Control Parameters not implemented:
	// Bool_z mHaveGlobalPowerControlParameters;
	// RLCMsgGlobalPowerControlParametersIE mPowerControlParameters;	// 12.9
	Bool_z mHavePowerControlParameters;
	RLCMsgPowerControlParametersIE mPowerControlParameters;	// 12.9
	Bool_z mHaveGlobalTimingAdvance;
	RLCMsgPacketGlobalTimingAdvanceIE mGlobalTimingAdvance; // 12.12a

	// 12.12b: Allows larger timing advance values.
	Bool_z mPacketExtendedTimingAdvancePresent;
	Field_z<2> mPacketExtendedTimingAdvance;
	public:
	RLCMsgPacketPowerControlTimingAdvance(TBF *wtbf)
		: RLCDownlinkMessage(RLCDownlinkMessage::PacketPowerControlTimingAdvance,false,wtbf)
	{
		assert(wtbf->mtTFI >= 0);
		setGlobalTFI(wtbf->mtDir == RLCDir::Down,wtbf->mtTFI);
		setTimingAdvance(wtbf->mtMS->msGetTA());
	}

	void setTimingAdvance(int ta) {
		mHaveGlobalTimingAdvance = true;
		mGlobalTimingAdvance.setTimingAdvance(ta);
	}
	void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
		mHaveGlobalTFI = 1; mGlobalTFI.setTFI(wIsDownlinkTFI,wTFI);
	}
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketPowerControlTimingAdvance::writeBody(MsgCommon &dst) const {
		// page_mode is written by RLCDownlinkMessage writeHeader()
		if (mHaveGlobalTFI) {
			dst.write0();
			mGlobalTFI.writeBody(dst);
		} else {
			assert(0);	// Other MS ID methods not supported.
		}
		dst.write0();	// message escape.
		dst.write0();	// No Global Power Parameters.
		if (mHaveGlobalTimingAdvance && mHavePowerControlParameters) {
			dst.write0();
			mGlobalTimingAdvance.writeBody(dst);
			mPowerControlParameters.writeBody(dst);
		} else {
			dst.write1();
			if (mHaveGlobalTimingAdvance) {
				dst.write0();
				mGlobalTimingAdvance.writeBody(dst);
			} else {
				dst.write1();
				mPowerControlParameters.writeBody(dst);
			}
		}
		// The following is confusing in the spec because
		// the indentation is misleading.
		// Look carefully and note that there is no ending
		// curly brace on the line  " null | 0 bit"
		if (dst.write01(mPacketExtendedTimingAdvancePresent)) {
			if (dst.write01(mPacketExtendedTimingAdvancePresent)) {
				dst.WRITE_ITEM(mPacketExtendedTimingAdvance);
			}
		}
	}
#endif

// GSM04.60 11.2.9b.
// When you set the NETWORK_CONTROL_ORDER to NC2 (network does cell reselection)
// then the MS sends measurement reports at the reporting periods defined below.
// These messages clog up our very limited ACCH.
// This message is one of the ways to change the reporting period.
// I'm not sure there is any other way for us.
class RLCMsgPacketMeasurementOrder : public RLCDownlinkMessage
{
	RLCMsgGlobalTFIorTLLIElt mMSID;
	void setTLLI(int tlli) { mMSID.setTLLI(tlli); }
	void setGlobalTFI(bool wIsDownlinkTFI,int wTFI) {
		mMSID.setGlobalTFI(wIsDownlinkTFI,wTFI);
	}

	Field_z<3> mPMOIndex;
	Field_z<3> mPMOCount;
	Bool_z mHaveNCMeasurementParameters;
	Field<2> mNetworkControlOrder;
	Field<3> mNCNonDrxPeriod;	// Non-drx period after MS sends a measurement.
	// Reporting period is documented in PSI5
	// It is a 3 bit code.  Larger values mean longer times.
	// Min is 0.48 sec and max is 61.44 sec.
	Field<3> mNCReportingPeriodI;	// GPRS default is 7 == 61.44 sec.
	Field<3> mNCReportingPeriodT;	// GPRS default is 3 == 3.84 sec.

	RLCMsgPacketMeasurementOrder(TBF*wtbf) :
		RLCDownlinkMessage(RLCDownlinkMessage::PacketMeasurementOrder,false,wtbf),
		mNetworkControlOrder(2),
		mNCNonDrxPeriod(0),
		mNCReportingPeriodI(7),	// Max these out
		mNCReportingPeriodT(7)
	{}
	void writeBody(MsgCommon&dst) const;
};
#if RLCMESSAGES_IMPLEMENTATION
	void RLCMsgPacketMeasurementOrder::writeBody(MsgCommon &dst) const {
		// page_mode is written by RLCDownlinkMessage writeHeader()
		mMSID.writeBody(dst); // MS identified by either uplink or downlink TFI.
		dst.WRITE_ITEM(mPMOIndex);
		dst.WRITE_ITEM(mPMOCount);
		if (dst.write01(mHaveNCMeasurementParameters)) {
			dst.WRITE_ITEM(mNetworkControlOrder);
			dst.write1();	// We always include this optional section.
			dst.WRITE_ITEM(mNCNonDrxPeriod);
			dst.WRITE_ITEM(mNCReportingPeriodI);
			dst.WRITE_ITEM(mNCReportingPeriodT);
			dst.write0(); // No NC_FREQUENCY_LIST.
		}
		dst.write0();	// No EXT Measurement Parameters.
		dst.write0();	// No optional stuff follows.
	}
#endif


}; // namespace GPRS
#endif
