/*
* 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.
*/
#ifndef MSINFO_H
#define MSINFO_H

#include <Interthread.h>
//#include <list>

#include "GPRSInternal.h"
#include "GPRSRLC.h"
//#include "RLCHdr.h"
#include "RList.h"
//#include "BSSG.h"
#include "Utils.h"
#include "SgsnExport.h"

#define CASENAME(x) case x: return #x;

namespace GSM { struct RadData; }
namespace GPRS {

typedef RList<PDCHL1Downlink*> PDCHL1DownlinkList_t;
typedef RList<PDCHL1Uplink*> PDCHL1UplinkList_t;
typedef RList<TBF*> TBFList_t;

// A way to describe a collection of tbf states.
// See TBF.h isActive() and isTransmitting().
enum TbfMacroState {
	TbfMAny,		// any state
	TbfMActive,		// any active state
	TbfMTransmitting	// any transmitting state.
};

// This should be in TBF.h but classes TBF and MSInfo are circularly referential.
enum TbfCancelMode {
	TbfRetryInapplicable,	// Tbf retry is inapplicable to an uplink tbf.
	TbfNoRetry,				// Kill the tbf forever
	TbfRetryAfterRelease,	// Retry tbf after sending a TbfRelease message.
							// If the tbf release message fails, fall back to RetryAfterTimeout.
	TbfRetryAfterWait		// Retry tbf after timeout.
	};

enum MultislotSymmetry {
	MultislotSymmetric,	// Use only symmetric multislot assignment, eg 2-down/2-up.
	MultislotFull		// Use full multislot assignment, which may be assymetric.
};

// GSM03.64 sec 6.2
// Note: The RROperatingMode is a state of the MS and known only at the layer2 MAC level.
// The GSM spec says essentially that the RROperatingMode is simply whether
// the MS thinks there is an active TBF running, which is not particularly useful to us.
// What we need to track is whether the MS is listening to
// CCCH (PacketIdle mode) or PDCH (PacketTransfer mode.)  Generally when all TBFs are
// finished the MS goes back to PacketIdle mode, but after a downlink transaction
// we keep it camped on PDCH for a time determined by the T3192 timer, whose value
// we broadcast in the System Information messages.
//
// Note: For T3192 timeout determination, dont forget to add the time delay
// of the outgoing message queue, but that should be short.
// But messages enqueued on CCCH may have long delays.
// 
// This mode has almost nothing to do with Mobility Management Status, which is up in Layer3.
// When the docs talk about "GPRS attached" they usually mean Mobility Mangement State, not this.
// An MS in GMM state "Ready" (meaning the MS and SGSN have negotiated an SGSN supplied
// TLLI for the MS instead of using the random TLLI the MS uses for its first uplink message)
// will usually be in PacketIdle mode unless there is an ongoing TBF transaction.
// For a non dual-transfer-mode MS, the MS must relinquish (or suspend) its GMM "Ready" state
// to make a voice call, in which case it would no longer be in PacketIdle mode,
// but that has almost no meaning at the MAC level.  If the voice call ends and the MS wants
// to use GPRS again, it will send us another RACH and we dont need to know that
// a voice call was made in the meanwhile.
class RROperatingMode {
	public:
	enum type {
		PacketIdle,
		PacketTransfer,
		//DualTransfer,
		// This mode was unnecessary:
		//Camped		// This is our own mode, not an official RROperatingMode.
				// It marks the time MS is camped on Packet Channel between PacketTransfer
				// and PacketIdle, when the T3192 timer is running.
				// If a new transfer does not commence before T3192 expires,
				// MS is in PacketIdle mode.
	};
	static const char *name(RROperatingMode::type mode);
};

std::ostream& operator<<(std::ostream& os, const RROperatingMode::type &mode);

// When we lose contact with the MS or something bad happens, we stop talking to it.
// This says why.  Reserve the first 100 numbers so the MsgTransactionType can be used
// as a stop cause when the corresponding MsgTransactionType counter expires.
class MSStopCause {
	public:
	enum type {
		AssignCounter = 1,
		// These were the values in release 3.0:
		//ShutDown = 2,
		//Stuck = 3,
		//Reassign = 4,	// Reassignment failed.
		ShutDown = 102,
		Stuck = 103,
		//Reassign = 104,	// Reassignment failed.
		Rach = 105,		// Running TBF killed by a RACH.
		ReleaseCounter = 106,
		ReassignCounter = 107,
		NonResponsive = 108,	// MS does not talk to us any more.
		Goof = 109,
		N3101 = 3101,
		N3103 = 3103,
		N3105 = 3105,
		T3191 = 3191,
		T3168 = 3168,
		CauseUnknown = 9999 	// Used for unrecoverable internal inconsistency.
	};
	//int mValue;
	//MSStopCause(/*enum MsgTransactionType*/ int wMsgTransType) : mValue(wMsgTransType) {}
};


struct SignalQuality {
	// TODO: Get the Channel Quality Report from packet downlink ack/nack GSM04.60 11.2.6
	Statistic<float> msTimingError;
	Statistic<int> msRSSI;		// Dont bother saving RSSI as a float
	Statistic<int> msChannelCoding;
	Statistic<int> msCValue;
	Statistic<int> msILevel;
	Statistic<int> msRXQual;
	Statistic<int> msSigVar;
	void setRadData(GSM::RadData &rd);
	void setRadData(float wRSSI,float wTimingError);
	void dumpSignalQuality(std::ostream &os) const;
};

struct StatTotalHits {
	// Use ints instead of unsigned in case some statistic is buggy and runs backwards.
	Int_z mTotal;
	Int_z mGood;
	void clear() { mGood = mTotal = 0; }
};

// keep the history of some success rate over the last few seconds for reporting purposes.
class StatHits {
	// Keep totals for each of the last NumHist 48-block-multiframes, which is approx one second.
	public: static const int cNumHist = 20;
	private:
	StatTotalHits mTotal; 				// Totals for all time.
	StatTotalHits mHistory[cNumHist];	// Recent history.
	UInt_z mWhen[cNumHist];	// When the historical data in this history bucket was collected.
	// Return index in arrays above, which is the current 48-block-multiframe
	unsigned histind();

	public:
	void addTotal() {
		mTotal.mTotal++;
		mHistory[histind()].mTotal++;
	}
	void addGood() {	// increment only good count, not total.
		mTotal.mGood++;
		mHistory[histind()].mGood++;
	}
	void addMiss() { addTotal(); }
	void addHit() {		// increment good and total.
		unsigned i = histind();
		mTotal.mTotal++; mTotal.mGood++;
		mHistory[i].mTotal++; mHistory[i].mGood++;
	}

	void getStats(float *pER, int *pTotal, float *pWorstER, int *pWorstTotal);
	void textRecent(std::ostream &os); 	// Print the average for the last N seconds and worst second.
	void textTotal(std::ostream&os);	// Print totals.
};

// More MS statistics.  Separate from MSInfo just because MSInfo is so large.
struct MSStat {
	// This is a measure of the instantaneous traffic, used to pick the least busy channel.
	// It is incremented every time a block is sent/received, and decayed on a regular time schedule.
	UInt_z msTrafficMetric;

	StatHits msCountCcchReservations;
	StatHits msCountRbbpReservations;

	StatHits msCountBlocks;	// Counts both uplink and downlink.

	UInt_z msConnectTime;
	void msAddConnectTime(unsigned msecs) { msConnectTime += msecs; }
	UInt_z msCountTbfs, msCountTbfFail, msCountTbfNoConnect;
	UInt_z msBytesUp, msBytesDown;


	//UInt_z msCountCcchReservations;
	//UInt_z msCountCcchReservationReplies;
	//UInt_z msCountRbbpReservations;
	//UInt_z msCountRbbpReservationReplies;
	//void service() {
	//	// There are approx 48 blocks per second.
	//	if (gBSNNext % 48 != 0) { return; }
	//	unsigned ind = (gBSNNext / 48) % numHist;
	//	msHistoryCcchReservations.sethits(int,msCountCcchReservations,msCountCcchReplies);
	//	msHistoryRbbpReservations.sethits(int,msCountRbbpReservations,msCountRbbpReplies);
	//	fivesecavg.countCcchReservations = msCountCcchReservations.
	//}

	// We use talkUp/talkDown to determine when the MS is non-responsive:
	GprsTimer msTalkUpTime;		// When the MS last talked to us.
	GprsTimer msTalkDownTime;	// When we last talked to the MS.

	// pat 3-2014:  Use msDownlinkAttempts to detect that a high-side initiated downlink failed.
	UInt_z msDownlinkAttempts;

	// Called at every uplink/downlink communication from MS.
	// These are not strictly statistics because they are also used to kill a non-responsive MS.
	// These timers differ from the persistent mode timers in that those
	// count only data, and these count anything.
	// SVGDBG this means we are got something from the phone
	void talkedUp(bool doubleCount=false) { msDownlinkAttempts=0; msTalkUpTime.setNow(); if (!doubleCount) {msTrafficMetric++;} }
	void talkedDown() { msDownlinkAttempts=0; msTalkDownTime.setNow(); msTrafficMetric++; }

	// Dump all except traffic metric.
	void msStatDump(const char *indent,std::ostream &os);
};

// MS Info a.k.a. Radio Context.  There is one of these for each TLLI (not per-MS, per-TLLI)
// GSM04.08 4.7.1.4 talks about GPRS attach, P-TMSI, and TLLI.
// An MS, for our purposes in L2, is defined by its TLLI.  No TLLI, no MSInfo struct.
// The TLLI identifies the MS in all transactions except the initial RACH.
// The RACH creates an anonymous packet uplink assignment for the MS, still identified
// only by the RACH time, to transmit one block, which will be a Packet Resource Request
// containing the all important TLLI.  However, knowing the TLLI does not identify
// a unique MS; the MS may (and usually does) use several of them.
// Note that the MS can pick a "random" TLLI for itself when it does its first
// GPRS-attach, but the SGSN issues a new "local" TLLI based on P-TMSI on AttachAccept.
// The TLLI is specific to the sgsn, so if you switched sgsns, you would have a new TLLI,
// however, the MS remembers its old TLLIs and will try calling in with them,
// converted to foreign TLLIs.
// 
// The spec is entirely botched up about whether the critical information needed
// to communicate with the MS is in L3 (the SGSN) or L2 (the BTS.)
// The SGSN stores the MS capabilities (RACap and DRX mode), which is ok but unnecessary,
// since the MS retransmits them in every RAUpdate.
// (The SGSN copies would be forwarded to the new cell during a cell change though.)
// The problem is that we need to remember the radio parameters for the MS
// (RSSI and TimingError), and a whole bunch of ad-hoc timers running in the MS
// here in L2, where we identify MS using TLLI,
// but the mapping of TLLI to MS (as known by IMSI) is in L3.  Whoops!
// Also note that during a TLLI reassignment procedure using BSSG, the SGSN commands
// the BTS to switch TLLIs *after* it has received the Attach Complete Message,
// which has already arrived [possibly but not always] using the new TLLI,
// so here at L2 the MSInfo for the new TLLI already exists.
// We are supposed to combine the two RadioContexts (MSInfos) when we get
// the TLLI reassignment, and then recognize either TLLI.
// The RSSI and TimingError are updated separately per-TLLI (ie, per-MSInfo struct)
// because the MS initiates each conversation.
//
// The entire communication system between the MS and the SGSN can best
// be described in terms of two different state-universes, corresponding
// to the GPRS-Registration state: Registered (GPRS-Attached) or not.
// In the pre-gprs-attach state the MS may call in with several TLLIs,
// and we dont know how to correlate them to an actual MS.
// In this case it is quite easy to lose communication with the MS, because
// when an incoming RACH+PacketResourceRequest is answered, a new PACCH
// may be assigned at random, and it may conflict with a previous assignment
// that is on-going or in-flight on AGCH.
// Also in this state I think there is simply no way to know for sure the state of the
// per-MS timers, which are needed to know how to send the Immediate Assignment.
// TODO: Maybe we should use a single PACCH timeslot for all unregistered TLLIs,
// which implies communicationg the registration state from the SGSN to L2.

// In the Registered-state we know that the new and old TLLI are the same physical MS,
// and communication is more secure.  We can assign a new PACCH for the MS.
// By Registered we mean that both the SGSN and the MS agree on the
// Registration state and the P-TMSI, which agreement is handshaked in both
// directions (3 messages) and consumated by the AttachComplete message sent by MS to SGSN.

#define TLLI_LOCAL_BIT 0x40000000
#define TLLI_MASK_LOCAL(tlli) ((tlli) & ~ TLLI_LOCAL_BIT)
static __inline__ uint32_t tlliEq(uint32_t tlli1, uint32_t tlli2) {
	// Temporarily provide a way to disable this in case it does not work:
	if (gConfig.getBool("GPRS.LocalTLLI.Enable")) {
		return TLLI_MASK_LOCAL(tlli1) == TLLI_MASK_LOCAL(tlli2);
	} else {
		return tlli1 == tlli2;
	}
}


// vvv OLD COMMENT:
// Formerly I changed the TLLI in the MSInfo struct to an oldTLLI on the command of the SGSN,
// but that is incorrect - if the MS later sends another RACH using the oldTLLI,
// we need to respond with that oldTLLI, not the newTLLI, although possibly we
// should just ignore it in that case.
// The RACH creates an anonymous packet uplink assignment for the MS, still identified
// only by the RACH time, to transmit one block, which will be a Packet Resource Request
// with a TLLI.  If that TLLI maps to an old TLLI, it is because we have already
// succeeded with the Attach Complete, and therefore it is safe to use the new TLLI.
// Therefore, an MSInfo structure has one and only one TLLI, and the sole purpose of
// TLLI is as a layer-2 transport identifier for the MS.
// ^^^ OLD COMMENT

// The MSInfo struct needs to hang around as long as the MS is in packet-transfer mode,
// which means as long as it has TBFs, or the MS is in the T3192 period when it is camped
// on the PACCH channel instead of the CCCH channel.
// If we lose a connection with an MS, we keep the dead TBF around too until we
// are sure it is no longer in use (config option, default 5 seconds),
// so we dont need the MSInfo to survive after that.  Doesn't hurt to keep it around, either.
// An unused MSInfo eventually decays and is destroyed; this delay must be longer
// than the expected use of the MSInfo by the SGSN - at least several seconds.
// The SGSN is what remembers GPRS-attached MS, and will send us both a TLLI and the
// MS capabilities (ie, multislot) in any transactions so that we can recreate the MSInfo at need.
class MSInfo : public SGSN::MSUEAdapter, public SignalQuality, public MSStat
{
	public:
	unsigned msDebugId;

	// Use of TLLI is in GSM04.08 4.7.1.5: P-TMSI handling.
	// From GSM03.03 sec 2.6: Structure of TLLI:
	// local TLLI is built by MS that has a valid P-TMSI:
	//		top 2 bits 11, lower 30 bits are low 30 bits of P-TMSI.
	// foreign TLLI is built by an MS that has a valid P-TMSI from elswhere:
	//		top 2 bits 10, lower 30 bits from P-TMSI;
	// random TLLI is built by an MS with no P-TMSI:
	//		top top 5 bits 01111, lower 27 bits random.
	// auxiliary TLLI is built by SGSN:
	//		top 5 bits 01110, lower 27 bits at SGSN discretion.
	// GSM03.03 sec 2.4: Structure of TMSI 
	// 	The 32-bit TMSI has only local significance (within VLR or SGSN) and
	// 	is created at manufacturer discretion, however, for SGSN top
	//	2 bits must be 11, and it may not be all 1s, which value is reserved
	//	to mean invalid.  We also reserve value 0 to mean unset.
	//	TMSI is stored in hex notation in 4 octets, and always ciphered.
	// GPRS-L2 doesn't care about any of the above, the SGSN knows those things.
	// In GPRS-L2 we just use whatever TLLI we are told.

	// An MSInfo structure is created as a result of an MS communication with a TLLI.
	// No TLLI, no MSInfo structure.  These things can time out and die whenever
	// they want - their lifetime only needs to be as long as the RSSI and TimingError is valid.
	// They will be recreated if the MS RAChes us again.
	// In the spec they can also be created by a page from the SGSN, but not implemented here.
	// The MSInfo struct corresponds to an SgsnInfo in the SGSN, but because
	// either can be deleted any time we dont keep pointers between them, we always
	// look them up by TLLI for communication to/from the SGSN.
	//
	// This is as per the spec:
	// The msTlli is the TLLI we (the L2 layer) always use to communicate with the MS.
	// It is initialzied from the TLLI the MS used to communicate with us.
	// It is only changed if we get a 'change tlli' command from the SGSN,
	// which happens only after a successfully completed attach procedure,
	// (which is a fully acknowledged procedure with 3 way handshake)
	// at which time msTlli becomes msOldTlli, and we must subsequently recognize
	// either msTlli (the new sgsn-assigned one) or msOldTlli (the original one)
	// for uplink communication, but use only msTlli for downlink communication.
	// 
	// This is not as per the spec:
	// After the attach is successful, we use only the assigned TLLI,
	// only one MSInfo structure, and everything is copascetic.
	// However, prior to a successful attach, I have seen the MS just use
	// several different TLLIs in uplink messages one right after the other,
	// maybe trying to find one that already works?
	// So many of these MSInfo refer to the same phone.
	// The spec deals with this by letting these things time out and die,
	// however, this results in conflicting in-flight assignments on AGCH
	// for different TLLIs that, in fact, refer to the same phone.
	// The spec does not provide any way for the L2 layer (us) to find that out.
	// So I introduced the AltTlli, which points to another MSInfo struct that
	// refers the same phone.  It MUST NOT be used for communication with the MS,
	// however, it can be used to avoid launching conflicting assignments.
	// The Sgsn sends us AltTlli as the AliasTlli in the DownlinkQPdu.
	UInt32_z msTlli;		// Identifies the MS, and is the TLLI used for downlink communication.
	UInt32_z msOldTlli;		// Also identifies the MS, and must be recognized in uplink communication.
	UInt32_z msAltTlli;		// Used to 'point' to another MSInfo struct that refers to the same MS,
							// as reported to us by the SGSN, which knows these things.
							// We save the TLLI instead of using a pointer because the other MSInfo
							// could disappear at any time.
	Bool_z msDeprecated;	// This MS has been replaced by some other, which is another way
							// of saying that the active MS's oldTlli points to this one.


	// If the MS is in packet-idle state, there should be no TBFs.
	TBFList_t msTBFs;	// TBFs for this MS, both uplink and downlink.
#define RN_MS_FOR_ALL_TBF(ms,tbf) for (RListIterator<TBF*> itr(ms->msTBFs); itr.next(tbf); ) 
	Int_z msUSFs[8];		// USF in each timeslot.
	// These are used by the RLCEngine to know when the MS has been granted a USF, ie, a chance to respond.
	Int_z msNumDataUSFGrants;	// Total number of USF grants; reset when last TBF detached.
	Int_z msAckNackUSFGrant;	// The msNumDataUSFGrants value of the last acknack.

	// Note: The uplink/downlink channels must all be in the same ARFCN that the MS is
	// camped on, and for multislot, follow strict timeslot adjacency rules.
	// The spec says that it is possible for different simultaneous TBFs for the same MS
	// to use different channel assignments, for exmaple, if the MS is sending a low-data-rate
	// TBF1 on a single channel and then wants to send a high-data-rate TBF2, it will interrupt
	// TBF2, may request a multislot allocation, and do TBF2 first.
	// Or another example, if TBF2 comes in and the TBF1 channel is on is congested, 
	// the MAC can pick a different channel for TBF2.
	// However, we are not going to support that.  The channels will be assigned to the MS
	// permanently, and all TBFs will use the same ones, which is why these lists are
	// here instead of in the TBF, where they really belong.
	// We may, however, someday change the channel assignments dynamically based on the
	// relative utilization of up and down links, for example, change from 4 down 1 up
	// to 4 up 1 down, etc., but if we do that we will have to wait until there are
	// no active TBFs, or reconfigure the existing TBFs.  Having all the channels
	// here in the MSInfo instead of scattered in different TBFs will make
	// such reconfiguration easier.
	PDCHL1UplinkList_t msPCHUps;	// uplink channels assigned to the MS; usually just one.
	PDCHL1DownlinkList_t msPCHDowns;	// downlink channels assigned to the MS; usually just one.
	bool msCanUseUplinkTn(unsigned tn);
	bool msCanUseDownlinkTn(unsigned tn);

	// This is the channel the MS is listening to for messages.
	// It is set before the msPCH assignments above.
	// How did the MS come to be listening to this channel, you wonder?
	// When a RACH comes in to the BTS, we do not know what MS it belongs to, so we pick
	// the least busy GPRS channel and tell the MS to send its request on that channel.
	// From then on the MS listens to that channel until we tell it differently
	// in a channel assignment, which should be the next thing we send to it.

	// TODO: Is the below correct?  Doesnt the MS always need to monitor PAACH which must
	// be one of its assigned channels?
	// It is possible for the msPCH assignments to not include the msPacch in several cases:
	// 1. If we deliberately give the MS a different channel assignment
	// for an uplink/downlink transfer, maybe because GPRS is underutilized and
	// we decided to close the channel.  (Channel closing not implemented in first draft.)
	// 2. Maybe we decide on a different set of channels to satisfy this particular
	// MSs multislot requirements.
	// 3. If a previously attached MS starts a new RACH request, which will assign
	// a new PCH at random (because we dont know what MS it is yet) and for some
	// reason we havent cleared the msPCH channels, maybe because our timeouts are out of phase.
	// In this weird case a BSSG downlink command might try to talk to the MS on
	// the old channels, which might even possibly work if the new assignment and the
	// old happen to be the same.  The special cases are complicated.
	// Note that if we used one-phase uplink access, this case 2 would extend in time out
	// into the uplink transfer, but we wont do that.
	PDCHL1FEC *msPacch;

	//RROperatingMode::type msMode; // Our belief about the state of MS: packet-idle, packet-transfer.


	UInt_z msIdleCounter;		// Counts how long MS is without TBFs; eventually we delete it.
	UInt_z msStalled;			// If MS is blocked, this is why, for error reporting.

	//Bool_z msUplinkRequest;		// Request from phone to establish uplink was delayed due to existing uplink tbf.
	//RLCMsgChannelRequestDescriptionIE msUplinkRequestCRD;  // The CRD for delayed request above.


	// GSM04.18 11.1.2:
	// T3141 - Started at Immediate Assignment, stopped when MS starts TBF.
	// We dont need it because we poll instead.

	// Note: Using Z100Timer is overkill for our single-threaded application;
	// could just use RLCBlockTime counters instead.
	// Counters and Timers defined in GSM04.60 sec 13.
	// We dont calculate N3105 and N3101 exactly the way the spec says,
	// but doesnt matter if they are off by 1 or 2.
	// NOTE: The blackberry sometimes waits 3 block periods before it starts
	// answering USFs, so N3101 better be bigger than that.
	UInt_z msN3101;			// Number of unacknowledged USF grants (off by one.)

	// GSM04.60 sec 13:
	// Note: The MS may take advantage of this time period by keeping the TBF open
	// after a PDU finishes, and not sending anything for a long time, then
	// sending sending additional PDUs in the same TBF later, but before the timer expires.
	GSM::Z100Timer msT3191;		// Waiting for acknowledgement of final TBF data block.

	// GSM04.60 sec 13:
	GSM::Z100Timer msT3193;		// After downlink TBF finished, MS camps on PDCH this long.
								// MS runs same timer but called T3192.

	// GSM04.60 sec 13:
	GSM::Z100Timer msT3168;		// MS camped on PDCH waiting for uplink assignment.
								// This timer is defined to be in the MS, not the BTS,
								// and we do not really need to track it as long as we
								// are sure we send the downlink assignment message
								// before this timer expires.  The timer value is in
								// the sql and broadcast in the beacon.
								// However, I am using the timer as a way of tracking
								// whether the assignment is for a RACH, rather
								// than setting some other variable.

	// GSM04.60 sec 13:
	//GSM::Z100Timer msT3169;		// Final timeout for dead tbf.
	//GSM::Z100Timer msT3195;		// Final timeout for dead tbf.
	//GSM::Z100Timer msTxxxx;		// Combined T3169, T3191, T3195 - timeout for
				// resource release after abnormal condition, during which time USF and TFI may not be reused.

	// When this MS was last granted a USF.
	// We use this when multiple MS are in contention for the uplink to make it fair.
	RLCBSN_t msLastUsfGrant;

	// Called when a USF is granted for this MS.
	// If penalize, if the MS does not answer we kill off the tbf.
	void msCountUSFGrant(bool penalize);

	// Incoming downlink data queue.
	// This queue is not between separate threads for BSSG,
	// and it is no longer for the internal sgsn either.
	//InterthreadQueue<BSSG::BSSGMsgDLUnitData> msDownlinkQueue;
	InterthreadQueue2<SGSN::GprsSgsnDownlinkPdu,SingleLinkList<> > msDownlinkQueue;
	Statistic<unsigned> msDownlinkQStat;
	Statistic<double> msDownlinkQDelay;
	Timeval msDownlinkQOldest;			// The timeval from the last guy in the queue.

	// Can this TBF use the specified uplink?
	bool canUseUplink(PDCHL1Uplink*up) {
		return msPCHUps.find(up);
	}
	// Can this TBF use the specified downlink?
	bool canUseDownlink(PDCHL1Downlink*down) {
		return msPCHDowns.find(down);
	}
	// Return the downlink channels as a bitmask for PacketDownlinkAssignment msg.
	unsigned char msGetDownlinkTimeslots(MultislotSymmetry sym);

	//PDCHL1Downlink *msPrimaryDownlink() { return msPCHDowns.front(); }
	bool msAssignChannels(); 	// Get channel(s) for this MS.
	private:
		bool msAddCh(unsigned chmask, const char *tnlist);
		bool msTrySlots(unsigned chmask,int down,int up);
		bool msAssignChannels2(int maxdown, int maxup, int sum);
	public:
	void msDeassignChannels();	// Release all channels for this MS.
	void msReassignChannels();	// Not implemented specially yet.

	//int msLastUplinkMsgBSN;	// When did we last hear from this MS?

	MSInfo(uint32_t tlli);
	// Use msDelete instead of calling ~MSinfo() directly.
	void msDelete(bool forever=0);	// If forever, do not move to expired list, just kill it.

	void msAddTBF(TBF *tbf) {
		devassert(! msTBFs.find(tbf));
		msTBFs.push_back(tbf);
	}
	void msForgetTBF(TBF *tbf) {
		devassert(msTBFs.find(tbf));
		msTBFs.remove(tbf);
	}


	// Called when a TBF goes dead.  If it was the last active uplink TBF, surrender our USFs.
	void msCleanUSFs();
	void msFailUSFs();

	unsigned msGetDownlinkQueuedBytes();
	//TBF * msGetDownlinkActiveTBF();
	private:
	int msCountTBF1(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0) const;
	int msCountTBF2(RLCDirType dir, enum TbfMacroState tbfstate, TBF**ptbf=0);
	public:
	int msCountActiveTBF(RLCDirType dir, TBF**ptbf=0);
	int msCountTransmittingTBF(RLCDirType dir, TBF**ptbf=0);
	void msService();
	void msStop(RLCDir::type dir, MSStopCause::type cause, TbfCancelMode cmode, int unsigned howlong);
	MSStopCause::type msStopCause;
	//void msRestart();
	ChannelCodingType msGetChannelCoding(RLCDirType wdir) const;
	int msGetTA() const { return GetTimingAdvance(msTimingError.getCurrent()); }
	// All MS use the same power params at the moment.
	int msGetAlpha() const { return GetPowerAlpha(); }
	int msGetGamma() const { return GetPowerGamma(); }
	void msDump(std::ostream&os, SGSN::PrintOptions options);
	void msDumpCommon(std::ostream&os) const;
	void msDumpChannels(std::ostream&os) const;
	RROperatingMode::type getRROperatingMode();
	string id() const;
	void msAliasTlli(uint32_t newTlli);
	void msChangeTlli(uint32_t newTlli);

	//void msSetUplinkRequest(RLCMsgChannelRequestDescriptionIE &wCRD) {
	//	msUplinkRequest = true;
	//	msUplinkRequestCRD = wCRD;
	//}

	// These are the functions required by the MSUEAdapter:
	uint32_t msGetHandle() { return msTlli; }
	string msid() const { return id(); }
	//void msWriteHighSide(ByteVector &dlpdu, uint32_t tlli, const char *descr) {
		//msDownlinkQueue.write(new GprsSgsnDownlinkPdu(dlpdu,tlli,descr));
	//}
	void msDeactivateRabs(unsigned rabMask) {}	// no-op in GPRS.

	bool msIsSuspended();				// Is the MS in suspended mode?
	bool msIsRegistered();				// Is the MS GPRS registered?
	bool isExtendedDynamic() { return msPCHUps.size() > msPCHDowns.size(); }
	bool msCanUseExtendedUplink();
};
extern unsigned gMSDebugId;

std::ostream& operator<<(std::ostream& os, const MSInfo*ms);

MSInfo *bssgMSChangeTLLI(unsigned oldTLLI,unsigned newTLLI);

};	// namespace
#endif
