/**@file Common-use GSM declarations, most from the GSM 04.xx and 05.xx series. */
/*
* Copyright 2008-2011 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	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.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/



#ifndef GSMCOMMON_H
#define GSMCOMMON_H

#include <stdlib.h>
#include <sys/time.h>
#include <ostream>
#include <vector>

#include <Threads.h>
#include <Timeval.h>
#include <BitVector.h>




namespace GSM {

/**@namespace GSM This namespace covers L1 FEC, L2 and L3 message translation. */

/** GSM Training sequences from GSM 05.02 5.2.3. */
extern const BitVector gTrainingSequence[];
extern const BitVector gEdgeTrainingSequence[];

/** C0T0 filler burst, GSM 05.02, 5.2.6 */
extern const BitVector gDummyBurst;

/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequence;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;


/**@name Modulus operations for frame numbers. */
//@{
/** The GSM hyperframe is largest time period in the GSM system, GSM 05.02 4.3.3. */
const uint32_t gHyperframe = 2048UL * 26UL * 51UL;

/** Get a clock difference, within the modulus, v1-v2. */
int32_t FNDelta(int32_t v1, int32_t v2);

/**
       Compare two frame clock values.
       @return 1 if v1>v2, -1 if v1<v2, 0 if v1==v2
*/
int FNCompare(int32_t v1, int32_t v2);


//@}


/**
	GSM frame clock value. GSM 05.02 4.3
	No internal thread sync.
*/
class Time {

	private:

	int mFN;				///< frame number in the hyperframe
	int mTN;			///< timeslot number

	public:

	Time(int wFN=0, int wTN=0)
		:mFN(wFN),mTN(wTN)
	{ }


	/** Move the time forward to a given position in a given modulus. */
	void rollForward(unsigned wFN, unsigned modulus)
	{
		assert(modulus<gHyperframe);
		while ((mFN % modulus) != wFN) mFN=(mFN+1)%gHyperframe;
	 }

	/**@name Accessors. */
	//@{
	int FN() const { return mFN; }
	void FN(unsigned wFN) { mFN = wFN; }
	unsigned TN() const { return mTN; }
	void TN(unsigned wTN) { mTN=wTN; }
	//@}

	/**@name Arithmetic. */
	//@{

	Time& operator++()
	{
		mFN = (mFN+1) % gHyperframe;
		return *this;
	}

    Time& decTN(unsigned step=1)
    {
		assert(step<=8);
		mTN -= step;
		if (mTN<0) {
			mTN+=8;
			mFN-=1;
			if (mFN<0) mFN+=gHyperframe;
		}
        return *this;
    }

	Time& incTN(unsigned step=1)
	{
		assert(step<=8);
		mTN += step;
		if (mTN>7) {
			mTN-=8;
			mFN = (mFN+1) % gHyperframe;
		}
		return *this;
	}

	Time& operator+=(int step)
	{
		// Remember the step might be negative.
		mFN += step;
		if (mFN<0) mFN+=gHyperframe;
		mFN = mFN % gHyperframe;
		return *this;
	}

	Time operator-(int step) const
		{ return operator+(-step); }

	Time operator+(int step) const
	{
		Time newVal = *this;
		newVal += step;
		return newVal;
	}

	Time operator+(const Time& other) const
    {
        unsigned newTN = (mTN + other.mTN) % 8;
		uint64_t newFN = (mFN+other.mFN + (mTN + other.mTN)/8) % gHyperframe;
        return Time(newFN,newTN);
    } 

	int operator-(const Time& other) const
	{
		return FNDelta(mFN,other.mFN);
	}

	//@}


	/**@name Comparisons. */
	//@{

	bool operator<(const Time& other) const
	{
		if (mFN==other.mFN) return (mTN<other.mTN);
		return FNCompare(mFN,other.mFN)<0;
	}

	bool operator>(const Time& other) const
	{
		if (mFN==other.mFN) return (mTN>other.mTN);
		return FNCompare(mFN,other.mFN)>0;
	}

	bool operator<=(const Time& other) const
	{
		if (mFN==other.mFN) return (mTN<=other.mTN);
		return FNCompare(mFN,other.mFN)<=0;
	}

	bool operator>=(const Time& other) const
	{
		if (mFN==other.mFN) return (mTN>=other.mTN);
		return FNCompare(mFN,other.mFN)>=0;
	}

	bool operator==(const Time& other) const
	{
		return (mFN == other.mFN) && (mTN==other.mTN);
	}

	//@}



	/**@name Standard derivations. */
	//@{

	/** GSM 05.02 3.3.2.2.1 */
	unsigned SFN() const { return mFN / (26*51); }

	/** GSM 05.02 3.3.2.2.1 */
	unsigned T1() const { return SFN() % 2048; }

	/** GSM 05.02 3.3.2.2.1 */
	unsigned T2() const { return mFN % 26; }

	/** GSM 05.02 3.3.2.2.1 */
	unsigned T3() const { return mFN % 51; }

	/** GSM 05.02 3.3.2.2.1. */
	unsigned T3p() const { return (T3()-1)/10; }

	/** GSM 05.02 6.3.1.3. */
	unsigned TC() const { return (FN()/51) % 8; }

	/** GSM 04.08 10.5.2.30. */
	unsigned T1p() const { return SFN() % 32; }

	/** GSM 05.02 6.2.3 */
	unsigned T1R() const { return T1() % 64; }

	//@}
};


std::ostream& operator<<(std::ostream& os, const Time& ts);

}; 	// namespace GSM


#endif

// vim: ts=4 sw=4
