/*
   File: sync.hh

   Description:
   Synchronized system of data objects based on a common timer 

   Created: March 1996, Alex Theo de Jong, NIST
*/

#ifndef __sync_hh
#define __sync_hh

#ifdef __GNUG__
#pragma interface
#endif

const int Max_Sync_Process =   4;

// Thread-Safe queue for time stamps and bytes

class StampQueue {
#ifdef TRACE
  friend class Synchronization;
#endif
 private:
  MutexLock data_lock;
  Semaphore in, out;
  int index_in, index_out;
  double* stamps;
  int* bytes;
 protected:
  // variables for update/wait
  int resync;
  double newtime;
 protected:
  int maximum, total;
  StampQueue(int size);
  ~StampQueue();
  void lock(){ data_lock.lock(); }
  void unlock(){ data_lock.unlock(); }
  int wait_in(){ return in.P(); }
  int wait_out(){ return out.P(); }
  int post_in(){ return in.V(); }
  int post_out(){ return out.V(); }
 public:
  int get(double& stamp);
  int get(double& stamp, int& bytes);
  int put(const double stamp, const int bytes=0);
};

/*
  Timer that runs independent of other threads
  Takes new time from time stamp queue and signals blocked threads
*/

class SyncTimer : public StampQueue {
  friend class SyncData;
  friend class Synchronization;
  // locking/condition for communication with other threads
  MutexLock time_lock;
  Condition time_cond;
  // Current decoder time
  double time;
 protected:
  void lock(){ time_lock.lock(); }
  void unlock(){ time_lock.unlock(); }
 public:
  SyncTimer(int size);
  int update();  // returns True when re-sync is needed
  int done();
};

/*
  Data synchronized on timer
  Takes time stamp from time stamp queue and waits until
  time is expired (compared to timer). A "done" sema is 
  set to indicate synchornization with timer
*/

class SyncData : public StampQueue {
  friend class Synchronization;
  int terminated;
  SyncTimer* timer; // decoder clock (local or encoder)
  double time;      // current time in thread
  int bytes;        // bytes <=0  -> re-sync
  // locking/condition for communication with timer thread
  MutexLock time_lock;
  Condition time_cond;
  MutexLock byte_lock;
 protected:
  void bytelock(){ byte_lock.lock(); }
  void byteunlock(){ byte_lock.unlock(); }
 public:
  SyncData(int size, SyncTimer* t);
#ifdef UPTIGHT
  int usedbytes(int b){ bytelock(); bytes-=b; byteunlock(); return bytes; }
#else
  int usedbytes(int b){ return bytes-=b; }
#endif
  // number of bytes used
  int wait();   // wait for time to pass time stamp
  int skip();   // skip last time stamp
  int update(); // wait for thread to synchronize with timer
  int done(int term=0);
};

/*
  Synchronization
  Synchronizes several data objects on a common timer. Initiates
  an independent timer thread that updates data threads with the
  new time. Threads are know by an "id" (ie. number assigned at 
  compile time): id=[1..Max_Sync_Process] (id=0, Timer).
  Synchronization type 0 (2xdata), 1 (1xdata, id=0), 2 (1xdata, id=2).
*/

class Synchronization {
  SyncData* syncs[Max_Sync_Process];
  SyncTimer timer; // decoder timer (local or encoder)
  int terminate;   // action
  int terminated;  // indicator
  athr_t id;     // thread to update timer
 protected:
  static void* init(Synchronization* s);
 public:
  Synchronization(int type, int t_qsize, int f_qsize);
  ~Synchronization(); 
  int usedbytes(int ID, int b);
  int wait(int ID);
  int skip(int ID);
  int put(const double stamp);
  int put(int ID, const double s, const int b);
  int pause();   // stop timer
  int resume(); // continu timer
  int done(int ID);
  int stop();
};

#endif // __sync_hh
