/*
* 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 L1 radio channels, from GSM 05.02 and 05.03. */

#ifndef GPRSL1FEC_H
#define GPRSL1FEC_H

#include <GSMCommon.h>
#include <GSMConfig.h>		// for gBTS
#include <GSML1FEC.h>
#include <GSMTransfer.h>	// for TxBurst
#include <GSMLogicalChannel.h> // for TCHFACCHLogicalChannel
#include "MAC.h"
using namespace GSM;
namespace GPRS {
class TBF;

// GSM05.03 sec 5.1.4 re GPRS CS-4 says: 16 bit parity with generator: D16 + D12 + D5 + 1,
static const unsigned long sCS4Generator = (1<<16) + (1<<12) + (1<<5) + 1;

class PDCHL1FEC;
class PDCHCommon
{
	public:
	PDCHL1FEC *mchParent;

	PDCHCommon(PDCHL1FEC*wParent) { mchParent = wParent; }
	ARFCNManager *getRadio();
	unsigned TN();
	unsigned ARFCN();
	unsigned CN();
	L1Decoder *getDecoder() const;
	float FER() const;
	void countGoodFrame();
	void countBadFrame();
	PDCHL1Uplink *uplink();
	PDCHL1Downlink *downlink();
	PDCHL1FEC *parent();	// If called from mchParent, returns itself.
	TBF *getTFITBF(int tfi, RLCDirType dir);
	void setTFITBF(int tfi, RLCDirType dir, TBF *TBF);
	
	const char *getAnsweringUsfText(char *buf, RLCBSN_t bsn); // Printable USFs around BSN for debugging

	char *shortId() {	// Return a short printable id for this channel.
		static char buf[20];
		sprintf(buf,"PDCH#%u:%u",getRadio()->ARFCN(),TN());
		return buf;
	}
};

// For gprs channels, should we allocate them using getTCH(),
// which returns a TCHFACCHLogicalChannel which we have no use for,
// or should we get dedicated GPRS channels directly from TRXManager,
// which currently does not allow this.
// Answer: We are going to make the logical channels tri-state (inactive, RRactive, GPRSactive),
// and use getTCH to get them.

// There is one of these classes for each GPRS Data Channel, PDTCH.
// Downstream it attaches to a single Physical channel in L1FEC via mchEncoder and mchDecoder.
// TODO: I did this wrong.  This should be for a single ARFCN, but multiple
// upstream/downstream timeslots.
class PDCHL1FEC :
	public L1UplinkReservation,
	public PDCHCommon,
	public USFList
{
	public:
	PDCHL1Downlink *mchDownlink;
	PDCHL1Uplink *mchUplink;

	L1FEC *mchOldFec;		// The GSM TCH channel that this GPRS channel took over;
						// it has the channel parameters.

	// Temporary: GPRS will not use anything in this LogicalChannel class, and we dont want
	// the extra class hanging around, but currently the only way to dynamically
	// allocate physical channels is via the associated logical channel.
	TCHFACCHLogicalChannel *mchLogChan;

	public:
	// The TFIs are a 5 bit handle for TBFs.  The USFs are a 3 bit handle for uplink TBFs.
	TFIList *mchTFIs;// Points to the global TFIList.  Someday will point to the per-ARFCN TFIList.

	void debug_test();

	PDCHL1FEC(TCHFACCHLogicalChannel *wlogchan);

	// Release the radio channel.
	// GSM will start using it immediately.  TODO: Do we want to set a timer
	// so it is not reused immediately?
	~PDCHL1FEC();

	// Attach this GPRS channel to the specified GSM channel.
	//void mchOpen(TCHFACCHLogicalChannel *wlogchan);

	void mchStart();
	void mchStop();
	void mchDump(std::ostream&os,bool verbose);

	// Return a description of PDTCH, which is the only one we care about.
	// (We dont care about the associated SDCCH, whose frame is used in GPRS for
	// continuous timing advance.)
	// The packet channel description is the same as channel description except:
	// From GSM04.08 10.5.25 table 10.5.2.25a table 10.5.58, and I quote:
	// "The Channel type field (5 bit) shall be ignored by the receiver and
	// all bits treated as spare. For backward compatibility
	// reasons, the sender shall set the spare bits to binary '00001'."
	// This doesnt matter in the slightest, because the typeAndOffset would
	// have been TCHF_0 whose enum value is 1 anyway.
	L3ChannelDescription packetChannelDescription()
	{
		L1FEC *lf = mchOldFec;
		return L3ChannelDescription((TypeAndOffset) 1, lf->TN(), lf->TSC(), lf->ARFCN());
	}
};
std::ostream& operator<<(std::ostream& os, PDCHL1FEC *ch);

// For CS-1 decoding, just uses SharedL1Decoder.
// For CS-4 decoding: Uses the SharedL1Decoder through deinterleaving into mC.
class GprsDecoder : public SharedL1Decoder
{
	Parity mBlockCoder_CS4;
	BitVector mDP_CS4;
	public:
	BitVector mD_CS4;
	short qbits[8];
	ChannelCodingType getCS();	// Determine CS from the qbits.
	BitVector *getResult();
	GprsDecoder() :
		mBlockCoder_CS4(sCS4Generator,16,431+16),
		mDP_CS4(431+16),
		mD_CS4(mDP_CS4.head(424))
		{}
	bool decodeCS4();
	const char* descriptiveString() const { return "GprsDecoder"; }	// not very useful.
};

// CS-4 has 431 input data bits, which are always 424 real data bits (53 bytes)
// plus 7 unused bits that are set to 0, to make 431 data bits.
// The first 3 bits are usf encoded to 12 bits, to yield 440 bits.
// Then 16 bit parity bits yields 456 bits.
class GprsEncoder : public SharedL1Encoder
{
	Parity mBlockCoder_CS4;
	public:
	// Uses SharedL1Encoder::mC for result vector
	// Uses SharedL1Encoder::mI for the 4-way interleaved result vector.
	BitVector mP_CS4;	// alias for parity part of mC
	BitVector mU_CS4;	// alias for usf part of mC
	BitVector mD_CS4;	// assembly area for parity.
	GprsEncoder() :
		SharedL1Encoder(),
		mBlockCoder_CS4(sCS4Generator,16,431+16),
		mP_CS4(mC.segment(440,16)),
		mU_CS4(mC.segment(0,12)),
		mD_CS4(mC.segment(12-3,431))
		{}
	void encodeCS4(const BitVector&src);
	void encodeCS1(const BitVector &src);
	// would be nice to add "GPRS"; should be at init.
	const char* descriptiveString() const { return "GprsEncoder"; }
};



class PDCHL1Uplink : public PDCHCommon
{
	protected:
#if GPRS_ENCODER
	//SharedL1Decoder mchCS1Dec;
	GprsDecoder mchCS14Dec;
#else	// This case does not compile yet.
	L1Decoder mchCS1Dec;
#endif

	public:
	static const RLCDirType mchDir = RLCDir::Up;
	// The uplink queue:
	// There will typically only be one guy on here, and we could probably dispense
	// with the queue, but it is safer to do it this way to avoid thread problems.
	// InterthreadQueue template adds "*" so it is really a queue of BitVector*
	InterthreadQueue<RLCRawBlock> mchUplinkData;

	PDCHL1Uplink(PDCHL1FEC *wParent) : PDCHCommon(wParent) { }

	~PDCHL1Uplink() {}

	void writeLowSideRx(const RxBurst &inBurst);

	// TODO: This needs to be per-MS.
	// void setPhy(float wRSSI, float TimingError) {
		// This function is inapplicable to packet channels, which have multiple
		// MS listening to the same channel.
		//assert(0);
	//}
};


// One of these for each PDCH (physical channel), attached to L1FEC.
// Accepts Radio Blocks from anybody.
// Based on SACCHL1Encoder and TCHFACCHL1Encoder
// This does on-demand sending of RLCBlocks down to the physical channel.
// We wait until the last minute so we can encode uplink assignments in the blocks
// as close as possible to the present time.
// TODO: When we support different encodings we may have to base this on L1Encoder directly
// and copy a bunch of routines from XCCHL1Encoder?
static const int qCS1[8] = { 1,1,1,1,1,1,1,1 };
static const int qCS2[8] = { 1,1,0,0,1,0,0,0 }; // GSM05.03 sec 5.1.2.5
static const int qCS3[8] = { 0,0,1,0,0,0,0,1 }; // GSM05.03 sec 5.1.3.5
static const int qCS4[8] = { 0,0,0,1,0,1,1,0 }; // GSM0503 sec5.1.4.5; magically identifies CS-4.

class PDCHL1Downlink : public PDCHCommon
{
	protected:
#if GPRS_ENCODER
	GprsEncoder mchEnc;
	//GSM::SharedL1Encoder mchCS1Enc;
	//GSM::SharedL1Encoder mchCS4Enc;
#else
	GSM::L1Encoder mchCS1Enc;
#endif
	TxBurst mchBurst;					///< a preformatted burst template
	//TxBurst mchFillerBurst;			// unused ///< the filler burst for this channel
	int mchTotalBursts;
	//GSM::Time mchNextWriteTime, mchPrevWriteTime;
	const TDMAMapping& mchMapping;
	BitVector mchIdleFrame;

	// The mDownlinkData is used only for control messages, which can stack up.
	//InterthreadQueue<RLCDownlinkMessage> mchDownlinkMsgQ;

	public:
	static const RLCDirType mchDir = RLCDir::Up;

	void initBursts(L1FEC*);
	PDCHL1Downlink(PDCHL1FEC *wParent) :
		PDCHCommon(wParent),
		//mchCS1Enc(ChannelCodingCS1),
#if GPRS_ENCODER
		//mchCS4Enc(ChannelCodingCS4),
#endif
		mchTotalBursts(0),
		mchMapping(wParent->mchOldFec->encoder()->mapping()),
		mchIdleFrame((size_t)0)
	{
	 	initBursts(wParent->mchOldFec);
	}

	~PDCHL1Downlink() {}

	// Enqueue a downlink message.  We dont use this for downlink data - those
	// are sent by calling the RLCEngine when this queue is empty.
	//void enqueueMsg(RLCDownlinkMessage *);
	// The PDCH must feed the radio on time.  This is the routine that does it.
	void dlService();
	void transmit(RLCBSN_t bsn, BitVector *mI, const int *qbits, int transceiverflags);
	//void rollForward();
	//void mchResync();
	int findNeedyUSF();

	// Send the L2Frame down to the radio now.
	void send1Frame(BitVector& frame,ChannelCodingType encoding, bool idle);
	bool send1DataFrame(RLCDownEngine *tbfdown, RLCDownlinkDataBlock *block, int makeres,MsgTransactionType mttype,unsigned *pcounter);
	bool send1MsgFrame(TBF *tbf,RLCDownlinkMessage *msg, int makeres, MsgTransactionType mttype,unsigned *pcounter);
	void sendIdleFrame(RLCBSN_t bsn);
	void bugFixIdleFrame();
};

extern bool chCompareFunc(PDCHCommon*ch1, PDCHCommon*ch2);

}; // namespace GPRS


#endif
