/*
* 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 TBF_H
#define TBF_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 "MSInfo.h"

namespace GPRS {
class MSInfo;
struct RLCMsgChannelRequestDescriptionIE;

// TBF - Temporary Block Flow.
// This class is responsible for doing the work of moving data to/from the MS,
// and all the directly involved messages and acknowledgements.
// The TBF class is the main class, but always includes a MsgTransaction class,
// and is itself encapsulated in either an RLCEngineUp or RLCEngineDown class.
// These did not need to be separate classes, it was just a convenient way to
// encapsulate the different functionalities clearly.

// A major design decision was how to share the channel resource.
// We share the channels because an MS with a bad connection may require
// multiple block resends, and may have up to 5 second timeouts mulitple times.
// We allow only one downlink and one uplink TBF per MS.
// But all TBFs of all connected MS, both uplink and downlink, run simultaneously
// and are serviced in round-robin fashion, so short TBFs dont get hung behind hogs,
// and the system should degrade gracefully under load.
//
// Rather than using queues for messages and data blocks, these classes generate
// all the messages and data blocks on demand.  At each RLC Block time, and for
// each GPRS channel, we look for a message or data block to send on that channel
// by calling a TBF service routine the downlink PDCH service routine.
// If the TBF has something to send on the channel at that time, it does so,
// and notifies the calling service routine that it should stop looking for a block to send.
//
// Why do we use on-demand instead of queues?  Several reasons:
// o The data to be sent may be influenced by uplink blocks up until the time it is sent.
//   For example, we may need to resend a previous block, based on an intervening
//   acknack message from the MS.
// o If there is a block to be sent that needs an RRBP reservation and can't get it (because
//   the RRBP has an extremely limited reservation range and all may be in use), it surrenders
//   its service time to some TBF that can use the downlink without a reservation.
// o Assignment messages dont even know if they are going out on the PDCH or on the
//   CCCH queue until their transmit time arrives, at which time they consult the T3192/T3193
//   timers to find out.
// By generating all data and messages blocks on demand, we also ensure maximum utilization
// of the downlink resource regardless of the load.
// Note that TBFs may be traveling on multiple channels for multislot MS.

// The TBF defines a little state machine, which marks the progress of the TBF.
// This has nothing to do with the RACH responder, which happens before a TBF ever gets started.
// The RACH repsponder grants only single-block uplink reservations, which we promptly
// forget about (except reserving that timeslot), and service only if a message
// actually arrives in the granted uplink block, at which time a TBF is created.
// And if we dont get the message, nobody ever knows;
// it is the responsibility of the MS to run a timer and try RACHing again.
//
// Uplink for MS in PacketIdle:
// 		o MS sends RACH
//		o BTS sends ImmediateAssignment of single block on CCCH.
//			no timers started; if MS does not receive it, oh well...
//		o MS sends PacketResourceRequest on PDCH, then listens to PDCH for period T3168.
//			note that MS ignores downlink assignment during this period.
// 		label1:
//		o BTS allocates uplink TBF in state DataReadyToConnect, waits for internal resources.
//		label2:
//		o BTS sends PacketUplinkAssignment with poll for ControlAcknowledgment,
//		TBF moves to state DataWaiting1.  Note there is no timer - the ControlAcknowledgement
//		is scheduled for a particular RLCBSN.
//		TODO: If T3168 expired before reaching this state, dont bother.
//		o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
//		or: MS does not send response - if T3168 or retries not expired goto label2
//		o MS starts sending TBF data.
// Uplink for MS in PacketTransfer mode.
//		o MS sends PacketResourceRequest on PDCH. It can use an RRBP poll for this purpose.
//		I still havent figured out if we can let the MS do this during T3192 wait.
//		Unclear if MS starts T3168, but we certainly dont.
//		BTS allows multiple simultaneous uplinks, so just goto label1;
//		Not sure about special case where we are still waiting for response
//		from first PacketUplinkAssignment.
// Downlink when MS in PacketIdle:
//		This can only happen if we have heard from the MS recently so we
//		already have a TLLI to identify it.  It occurs when the SGSN sends a downlink TBF
//		but the MS has timed out and is back in PacketIdle mode, listening to CCCH.
//		This is distinct from a Paging Request which is initiated by the SGSN
//		when it is not sure if the MS is listening to this BTS or not.
//		label3:
//		o BTS sends ImmediateAssignment downlink message with poll.
//			we dont need to start timer T3141 because we poll.
//		o MS sends ControlAcknowledgement, TBF moves to DataTransmit state.
//		or: MS does not send response - if retries not expired goto label3
//		o BTS starts sending TBF data; TBF in DataTransmit state.
// Downlink for MS in PackeTransfer mode or T3192 wait period.
//		Can happen if MS is doing an uplink TBF or if MS camped on PDCH during
//		T3192 period after downlink completes.
//		label4:
//		o BTS sends PacketDownlinkAssignment message with RRBP poll.
//		o MS sends ControlAcknowledgement or other message in response to poll,
//		TBF moves to DataTransmit state.
//		or: MS does not send response - if retries not expired goto label4
//		
class TBFState
{
	public:
	enum type {
		Unused, // 0 Reserved.

		DataReadyToConnect,
			// Waiting to call tbf->mtAttach...() successfully.
			// It is waiting on resources like TFI or USF.
			// We (currently) only allow one downlink TBF per MS, although the MS can send multiple
			// simultaneous uplink TBFs, and nothing we can do about that.
			// When it connects (gets the resources reserved),
			// it calls MsgTransaction->sendMsg, which enqueues the message
			// (for either PACCH or CCCH), increments mSendTrys.
			// TBF state changes to DataWaiting1.

			// If the MS is in packet-idle mode, need to send the message on CCCH,
			// otherwise on PACCH.  See MSMode.

		DataWaiting1,
			// Waiting on MsgTransaction for MS to respond to uplink/downlink message,
			// Reply is in the form of RRBP granted PacketControlAcknowledgement.
			// If it is a multislot assignment that went on CCCH, we need yet
			// another MsgTransaction to do the timeslot reconfigure, so go to DataWaiting2,
			// otherwise go directly to DataTransmit.
			// (Because the CCCH Immediate Assignment supports only single-slot.)
			// Otherwise go directly to DataWaiting2.

		DataWaiting2,
			// Send the Packet Timing Advance required for downlink immediate assignment on CCCH.
			// Multislot TODO
			// Send the Multislot assignment.
			// Reply is in the form of RRBP granted PacketControlAcknowledgement.
			// On reply, go to state DataTransmit.
			// if gBSNNext <= mtExpectedAckBSN:
			//		We are waiting for the MS to send the response.  Do nothing.
			// else:
			//		if the MS did not set mAckYet (by sending us a message,
			//			probably Packet Control Acknowledgment):
			//			if too many mSendTrys, give up, goto stateDead.
			//			else resend the message and stay in this state.
			//		else the MS did set mAck:
			//			If the message is:
			//			Packet TBF release, kill this TBF.
			//			FinalAckNack: All uplink data successful, kill this TBF.
			//			Otherwise it is data up/down: activate the TBF, goto stateActive

		DataReassign,
			// Not currently used.

		DataTransmit,
			// MS and BTS are in PacketTransfer Mode
			// A data transfer TBF is trying to do its thing using the RLCEngine.
			// If it is a packet uplink assignment, set some timer, and we start setting USF
			// in downlink blocks to allow the MS to send us uplink blocks.
			// If it is a packet downlink assignment, start some timer and the serviceloop
			// will start sending downlink blocks when there is nothing else to do.
			// If it is packet TBF release, destroy this TBF.
			// Periodically the RLCEngine will send AckNack blocks in unacknowledged mode.
			// (Only the final AckNack message is sent in acknowledged mode.)

		DataFinal,
			// Only used for uplink TBFs now.
			// We have received all the uplink data, but we are waiting
			// for the MS to respond to the uplinkacknack message.
			// It wont stop sending data until it gets it, so we make sure.

		TbfRelease,
			// TBF is undergoing PacketTBFRelease procedure as the result of an abnormal termination.
			// We send the PacketTBFRelease message and then wait in this state until we get the acknowledgement.
			// When the get the response we will retry the TBF.

		Finished,
			// TBF is completely finished, but we keep it around until
			// all its reservations expire before detaching.

		Dead,
			// Similar to Finished, but this TBF is dead because
			// we lost contact with the MS or some timer expired.
			// It is *not* detached yet, ie, it hangs on to its resources.
			// We cannot reuse the resources for 5 (config parameter) seconds.
			// We could also act preemptively to send TBF destruction messages, which if answered
			// would allow us to get rid of the TBF, not that we care that much.
		Deleting
			// This state is entered via mtDetach().
			// This resources for this TBF have been (or are in the midst of being) released.
			// We use this ephemeral state to make deletion simpler.
		};
	static const char *name(int value);
};
std::ostream& operator<<(std::ostream& os, const TBFState::type &type);

#if TBF_IMPLEMENTATION
const char *TBFState::name(int value)
{
	switch ((type)value) {
	case Unused: return "TBFState::Unused";
	case DataReadyToConnect: return "TBFState::DataReadyToConnect";
	case DataWaiting1: return "TBFState::DataWaiting1";
	case DataWaiting2: return "TBFState::DataWaiting2";
	case DataReassign: return "TBFState::DataReassign";
	case DataTransmit: return "TBFState::DataTransmit";
	//case DataStalled: return "TBFState:DataStalled";
	case TbfRelease: return "TBFState::TbfRelease";
	case Finished: return "TBFState::Finished";
	case DataFinal: return "TBFState::DataFinal";
	case Dead: return "TBFState::Dead";
	case Deleting: return "TBFState::Deleting";
	}
	return "TBFState undefined!";	// Makes gcc happy
}
std::ostream& operator<<(std::ostream& os, const TBFState::type &type)
{
	os << TBFState::name(type);
	return os;
}
#endif

// These are the message transaction types.
// Each TBFState only uses one type of message transaction, so we could use
// the TBFState as the message transaction type, but the code is clearer
// if the message types are seprate from the TBF states.
// When we change state there may be outstanding messages that belong to the previous state,
// especially on error conditions.
// Most of these states are used only by uplink or downlink tbfs but not both;
// the inapplicable states are never used.
enum MsgTransactionType {
	MsgTransNone,		// Means nothing pending.
	MsgTransAssign1,	// For ack to first assignment msg (on ccch or pacch.)
	MsgTransAssign2,	// For ack to optional second assignment msg (always on pacch.)
	MsgTransReassign,	// For ack to reassignment if required.
	MsgTransDataFinal,	// For ack to final transmitted block. Used for both up and downlink.
						// In downlink, the block with the FBI indicator. N3103 in uplink, N3105 in downlink
	MsgTransTransmit,	// For acknack message during DataTransmit mode. Used for both uplink and downlink
						// In downlink N3105, in uplink for the non-final-ack.
	// There are two different transaction states for Assign messages because we may send two:
	// if the first is on CCCH and we want multislot mode, we have to send a second
	// assignment on pacch.
	MsgTransTA,			// For ack to Timing Advance msg.
	MsgTransTbfRelease,	// For ack to TbfRelease msg.
	MsgTransMax			// Not a transaction - indicates number of Transaction Types.
};

// This facility is used only for messages belonging to a TBF, which includes RRBP reservations
// and the Poll reservation for an assignment message sent on AGCH.
// It is not used for RACH polls, since we dont know who they belong to, and would be
// meaningless anyway because they do not correspond to a TBF somewhere changing states.
// We only track one outstanding reservation at a time for each TBF.
// Generally we send a message and then wait for a PacketControlAcknowledgement.
class MsgTransaction
{	private:
	BitSet mtMsgAckBits; 		// Type of acks received.
	BitSet mtMsgExpectedBits;	// The types messages we are waiting for.
	Mutex mtLock;	// The CCCH handler calls mtSetAckExpected from another thread, so we have to lock appropriately.

	public:
	RLCBSN_t mtExpectedAckBSN[MsgTransMax];	// When we expect to get the acknowledgement from the MS.
	// This is supposed to be in the MS, but I moved it to the TBF because
	// some TBFs become non-responsive individually while others are still moving,
	// so we dont want to kill off the MS if a single TBF dies.
	UInt_z mtN3105;		// Counts RRBP data reservations that MS ignores.
	UInt_z mtN3103;		// Counts final downlinkacknacks RRBP that MS ignores.
	UInt_z mtAssignCounter;	// Count Assignment Messages.
	UInt_z mtReassignCounter;	// Count reassigment messages.
	UInt_z mtCcchAssignCounter;	// Number of assignments sent on CCCH.
	UInt_z mtTbfReleaseCounter;

	// Wait for the next message.
	void mtMsgSetWait(MsgTransactionType mttype) {
		{	ScopedLock lock(mtLock);
			devassert(mtExpectedAckBSN[mttype].valid());
			mtMsgAckBits.clearBit(mttype);
			mtMsgExpectedBits.setBit(mttype);
		}
		GPRSLOG(4) << "mtMsgSetWait"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
	}
	void text(std::ostream &os) const;

	// Set the BSN when the TBF is expecting a message, but note that the
	// MS may send uplink data blocks before this time.
	void mtSetAckExpected(RLCBSN_t when, MsgTransactionType mttype) {
		GPRSLOG(4) << "mtSetAckExpected"<<LOGVAR(when)<<LOGVAR(mttype);
		ScopedLock lock(mtLock);
		mtExpectedAckBSN[mttype] = when;
		mtMsgSetWait(mttype);
	}

	// Called to indicate that a message for this TBF arrived.
	void mtRecvAck(MsgTransactionType mttype) {
		GPRSLOG(4) << "mtRecvAck"<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
		ScopedLock lock(mtLock);
		mtMsgAckBits.setBit(mttype);
		mtMsgExpectedBits.clearBit(mttype);
		mtN3105 = 0;	// Not all of these are for mtN3105, but doesnt hurt to always reset it.
	}

	bool mtGotAck(MsgTransactionType mttype, bool clear) {
		bool result;
		{	ScopedLock lock(mtLock);
			result = mtMsgAckBits.isSet(mttype);
			if (result && clear) {
				mtMsgExpectedBits.clearBit(mttype);
				mtExpectedAckBSN[mttype] = -1;
			}
		}
		GPRSLOG(4) << "mtGotAck "<<(result?"yes":"no")<<LOGVAR(mttype)<<LOGVAR(mtMsgExpectedBits);
		return result;
	}

	// Is any reservation currently outstanding?
	bool mtMsgPending();
	bool mtMsgPending(MsgTransactionType mttype);	// Is this msg still outstanding?

	//MsgTransaction() { }
};

// We will allocate a TBF both for data uplink/downlink transfers, and for single
// messages to the MS which require an ACK, even though strictly speaking that is not a TBF.
// A TBF is not used for a single block packet [uplink] access.
class TBF :
	public MsgTransaction		// Sends messages reliably.
{
	private:
	TBFState::type mtState;		// State of this TBF.  Dont set this directly, call setState().

	public:
	unsigned mtDebugId;
	MSInfo *mtMS;
	RLCDirType mtDir;
	Int_i<-1> mtTFI;				// Assigned TFI, or -1.

	// There is some question about whether we need ACKs (PacketControlAcknowledgement) at all.
	// For packet downlink, without the ACK we would not notice until the MS
	// did not respond to RRBPs long enough that we figured it out.
	// For packet uplink, without the ACK we could look at responses from this MS,
	// and if they are bad for awhile, restart this, but we cant really be sure
	// that the old TFI is released first.
	// So the ACKs make the state machine much safer in both cases.
	

	Bool_z mtUnAckMode;			// a.k.a. RLCMode.  If 1, send in unacknowledged mode.
								// Note: as of 6-2012 unacknowledged mode is not implemented.
	Bool_z mtAttached;			// Flag for mtAttach()/mtDetach() status.
	Bool_z mtAssignmentOnCCCH;	// Set if assignment was sent on CCCH.
	Bool_z mtPerformReassign;	// Reissue an uplink TBF assignment to 'change priority' geesh.
	//Bool_z mtIsRetry;			// Is this our second attempt to send this TBF?
	Bool_z mtTASent;			// For debugging, true if we sent an extra Timing Adv message.
	GprsTimer mtDeadTime;		// When a dead TBF can finally release resources.
	MSStopCause::type mtCause;	// Why the TBF died.
	//Float_z mtLowRSSI;			// Save the lowest RSSI seen for reporting purposes.
	uint32_t mtTlli;			// The tlli of an uplink TBF.  It is != mtMS->msTlli only
								// in the special case of a second AttachRequest occuring
								// after the TLLI reassignment procedure.

	// Persistence timers, used for both uplink and downlink.
	//GprsTimer mtKeepAliveTimer;	// Time to next keep alive.
	GprsTimer mtPersistTimer;	// How long TBF persists while idle.

	std::string mtDescription;	// For error reporting, what was in this TBF?

	Timeval mtStartTime;		// For reporting.  default init is to current time.


	// Statistics for flow control, needed only in downlink direction.
	//TODO: RLCBSN_t mtStartTime;	// When we started sending it.

	TBF(MSInfo *wms, RLCDirType wdir);
	// The virtual keyword tells C++ to call the derived destructors too.
	// Otherwise it may not.  It is foo bar.
	virtual ~TBF();

	// Can this TBF use the specified downlink?
	// It depends on the MS allocated channels.
	bool canUseDownlink(PDCHL1Downlink*down) { return mtMS->canUseDownlink(down); }

	// Can this TBF use the specified uplink?
	bool canUseUplink(PDCHL1Uplink*up) { return mtMS->canUseUplink(up); }

	void mtSetState(TBFState::type wstate);
	TBFState::type mtGetState() { return mtState; }

	// These are the states that count toward an MS RROperatingMode being
	// in PacketTransfer mode instead of PacketIdle mode.
	// We leave DataWaiting1 out.
	// First of all, we call this when we are doing a sendAssignment
	// and the tbf on whose behalf we are inquiring is in DataWaiting1,
	// so we get stuck here.
	// Second of all, we dont really know if the MS is listening
	// or not in DataWaiting1 mode because we have not heard back from
	// it after the sendAssignment.  Maybe we need yet another mode.
	bool isTransmitting() {
		switch (mtState) {
			case TBFState::DataWaiting2:
			case TBFState::DataTransmit:
			case TBFState::DataReassign:
			//case TBFState::DataStalled:
			case TBFState::DataFinal:
				return true;
			default:
				return false;
		}
	}
	// Used to determine if there is already a TBF running so we should not start another.
	// It is very important to include DataReadyToConnect because those indicate
	// that an assignment is already in progress, and we dont want to start another.
	bool isActive() {	// The TBF is trying to do something.
		switch (mtState) {
			case TBFState::DataReadyToConnect:
			case TBFState::DataWaiting1:
			case TBFState::DataWaiting2:
			case TBFState::DataTransmit:
			case TBFState::DataReassign:
			//case TBFState::DataStalled:
			case TBFState::DataFinal:
			case TBFState::TbfRelease:	// Counts until it is acknowledged as released.
				return true;
			case TBFState::Dead:
			case TBFState::Finished:	// TBF may still wait for res, but we dont count it.
			case TBFState::Deleting:
			case TBFState::Unused:
				return false;
		}
		return false;	// Unreached, but makes gcc happy.
	}
	bool mtServiceDownlink(PDCHL1Downlink *down);
	bool mtSendTbfRelease(PDCHL1Downlink *down);
	void mtServiceUnattached();
	void mtCancel(MSStopCause::type cause, TbfCancelMode release);
	void mtCancel(MsgTransactionType cause, TbfCancelMode release) {
		// Canceled due to expiry of MsgTransactionType timer.
		mtCancel((MSStopCause::type) cause, release);
	}
	void mtRetry();
	void mtFinishSuccess();
	std::string tbfDump(bool verbose) const;

	// For downlink we specify the channelcoding in the qbits of every block,
	// so we can change channelcoding dynamically between CS-1 and CS-4.
	// For uplink, the BTS specifies the encoding the MS will use in both
	// the uplink assignment and in every uplinkacknack message.
	// The ChannelCodingMax is used for retries to throttle back to a more secure codec.
	ChannelCodingType mtChannelCodingMax;	// The max channel coding (0-3) allowed for this TBF.
	ChannelCodingType mtCCMin, mtCCMax;		// Saved for reporting purposes.
	ChannelCodingType mtChannelCoding() const; 	// Return 0 - 3 for CS-1 or CS-4 for data transfer.

	// Note that this TBF is a base class of either an RLCEngineUp or RLCEngineDown,
	// depending on the TBF direction.
	// These functions are defined in RLCEngineUp and RLCEngineDown:
	virtual bool engineService(PDCHL1Downlink *down) = 0;
	virtual unsigned engineDownPDUSize() const {return 0;}
	virtual void engineRecvDataBlock(RLCUplinkDataBlock* block, int tn) {}
	virtual void engineRecvAckNack(const RLCMsgPacketDownlinkAckNack *msg) {}
	virtual float engineDesiredUtilization() = 0;
	virtual void engineGetStats(unsigned *pSlotsTotal, unsigned *pSlotsUsed, unsigned *pGrants) const = 0;
	virtual int engineGetBytesPending() = 0;
	virtual void engineDump(std::ostream &os) const = 0;
	virtual bool stalled() const = 0;
	//RLCDownEngine const* getDownEngine() const;	// Cant use this to change anything in RLCDownEngine
	RLCDownEngine * getDownEngine();
	static TBF *newUpTBF(MSInfo *ms,RLCMsgChannelRequestDescriptionIE &mCRD, uint32_t tlli, bool onRach);

	// In a multislot configuration one of the slots is the primary slots and is
	// used exclusively for all messages, and all other channels are used only for data.
	// The primary slot must have both uplink and downlink timeslots assigned,
	// and is currently identical to msPacch.  It is also the first channel in the
	// downlink list.
	bool isPrimary(PDCHL1Downlink *down);
	bool wantsMultislot();	// Does this tbf want to be multislot?

	// Attach to a channel.  Return true if we succeeded.
	bool mtAttach();
	bool mtNonResponsive();		// Is this TBF non responsive?
	// Detach from the channel.  Release our resources.
	void mtDetach();	// Internal use only - call mtCancel or mtFinishSuccess
	void mtDeReattach();	// Internal use only - call mtCancel or mtFinishSuccess
	// If forever, do not move to expired list, just kill it.
	void mtDelete(bool forever=0);	// Internal use only - call mtCancel or mtFinishSuccess
	//void setRadData(RadData &wRD);
	//void talkedUp() { mtMS->talkedUp(); }
	void talkedDown() { mtMS->talkedDown(); }
	uint32_t mtGetTlli();
	const char *tbfid(bool verbose);

	private:
	bool mtAllocateTFI();
	bool mtAllocateUSF();
	void mtFreeTFI();
};
extern unsigned gTBFDebugId;

std::ostream& operator<<(std::ostream& os, const TBF*tbf);
#if TBF_IMPLEMENTATION
std::ostream& operator<<(std::ostream& os, const TBF*tbf)
{
	if (tbf) {
		os << " TBF#" << tbf->mtDebugId <<" ";
	} else {
		os << " TBF(null ptr) ";
	}
	return os;
}
#endif

extern bool sendAssignment(PDCHL1FEC *pacch,TBF *tbf, std::ostream *os);

}	// namespace GPRS
#endif
