/*
* Copyright 2008, 2012 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU 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 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 General Public License for more details.

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

*/



/*
	Compilation switches
	TRANSMIT_LOGGING	write every burst on the given slot to a log
*/

#include "DriveLoop.h"
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"

#include <sys/types.h>
#include <sys/socket.h>

/** Define this to be the slot number to be logged. */
//#define TRANSMIT_LOGGING 1

/** FIFO thread loop */
class FIFOServiceLoopThread : public Thread {
public:
  FIFOServiceLoopThread() : Thread("FIFOServiceLoopThread") {}
  Thread::ReturnStatus shutdown();

protected:
  virtual void runThread();
};

/** control message handler thread loop */
class ControlServiceLoopThread : public Thread {
public:
  ControlServiceLoopThread() : Thread("ControlServiceLoopThread") {}
  Thread::ReturnStatus shutdown();

protected:
  virtual void runThread();
};

/** transmit queueing thread loop */
class TransmitPriorityQueueServiceLoopThread : public Thread {
public:
  TransmitPriorityQueueServiceLoopThread() : Thread("TransmitPriorityQueueServiceLoopThread") {}
  Thread::ReturnStatus shutdown();

protected:
  virtual void runThread();
};

/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
  
private:
  DriveLoop *mDriveLoop;

  int mBasePort;                  ///< Base port address for all our ports
  std::string mTRXAddress;        ///< Address of the BTS TRX control interface
  UDPSocket mDataSocket;	  ///< socket for writing to/reading from GSM core
  UDPSocket mControlSocket;	  ///< socket for writing/reading control commands from GSM core

  VectorQueue  *mTransmitPriorityQueue;   ///< priority queue of transmit bursts received from GSM core
  VectorFIFO*  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts 

  friend class FIFOServiceLoopThread;
  FIFOServiceLoopThread mFIFOServiceLoop;      ///< thread to push/pull bursts into transmit/receive FIFO
  friend class ControlServiceLoopThread;
  ControlServiceLoopThread mControlServiceLoop;   ///< thread to process control messages from GSM core
  friend class TransmitPriorityQueueServiceLoopThread;
  TransmitPriorityQueueServiceLoopThread mTransmitPriorityQueueServiceLoop;///< thread to process transmit bursts from GSM core

  int mChannel;                           ///< channelizer attach number between 0 and 'M-1'

  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
  double txFullScale;                     ///< full scale input to radio
  double rxFullScale;                     ///< full scale output to radio

  /** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
  void unModulateVector(signalVector wVector); 
#endif

  /** modulate and add a burst to the transmit queue */
  void addRadioVector(BitVector &burst,
		      int RSSI,
		      GSM::Time &wTime);

  /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
  void pushRadioVector(GSM::Time &nowTime);

  /** Pull and demodulate a burst from the receive FIFO */ 
  SoftVector *pullRadioVector(GSM::Time &wTime,
			   int &RSSI,
			   int &timingOffset);
   
  /** send messages over the clock socket */
  void writeClockInterface(void);

  void pullFIFO(void);                 ///< blocking call on receive FIFO 

  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation

  int mSamplesPerSymbol;               ///< number of samples per GSM symbol

  bool mOn;			       ///< flag to indicate that transceiver is powered on
  bool mRunning;                       ///< flag to indicate control loop is running
  bool mPrimary;                       ///< flag to indicate C0 channel 
  double mTxFreq;                      ///< the transmit frequency
  double mRxFreq;                      ///< the receive frequency
  double mFreqOffset;                  ///< RF frequency offset
  int mPower;                          ///< the transmit power in dB
  double mEnergyThreshold;             ///< threshold to determine if received data is potentially a GSM burst
  GSM::Time prevFalseDetectionTime;    ///< last timestamp of a false energy detection
  unsigned mMaxExpectedDelay;            ///< maximum expected time-of-arrival offset in GSM symbols

  GSM::Time    channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
  signalVector *channelResponse[8];    ///< most recent channel estimate of all timeslots
  float        SNRestimate[8];         ///< most recent SNR estimate of all timeslots
  signalVector *DFEForward[8];         ///< most recent DFE feedforward filter of all timeslots
  signalVector *DFEFeedback[8];        ///< most recent DFE feedback filter of all timeslots
  float        chanRespOffset[8];      ///< most recent timing offset, e.g. TOA, of all timeslots
  complex      chanRespAmplitude[8];   ///< most recent channel amplitude of all timeslots

  static int mTSC;                     ///< the midamble sequence code

public:

  /** Transceiver constructor 
      @param wBasePort base port number of UDP sockets
      @param TRXAddress IP address of the TRX manager, as a string
      @param wSamplesPerSymbol number of samples per GSM symbol
      @param wTransmitLatency initial setting of transmit latency
      @param radioInterface associated radioInterface object
  */
  Transceiver(int wBasePort, const char *TRXAddress,
	      DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
	      int wSamplesPerSymbol = SAMPSPERSYM,
	      int wChannel = 0, bool wPrimary = true);

  /** Destructor */
  ~Transceiver();

  /** start the Transceiver */
  void start();

  /** shutdown (teardown threads) the Transceiver */
  void shutdown();

protected:

  /** drive reception and demodulation of GSM bursts */ 
  void driveReceiveFIFO();

  /** drive transmission of GSM bursts */
  void driveTransmitFIFO();

  /** drive handling of control messages from GSM core */
  void driveControl();

  /**
    drive modulation and sorting of GSM bursts from GSM core
    @return true if a burst was transferred successfully
  */
  bool driveTransmitPriorityQueue();

  void reset();

  /** return transceiver on/off status */ 
  bool on() { return mOn; }

  /** return control loop operational status */ 
  bool running() { return mRunning; }

  /** return the drive loop pointer */
  DriveLoop *getDriveLoop() { return mDriveLoop; }
  
  /** set priority on current thread */
  void setPriority() { mRadioInterface->setPriority(); }
};
