// Flaschenhals. (C) 2003 by Ingo Josopait
//
// Usage: flaschenhals [tab position] [throughput in kb/sec]
//
// use the tab position to manage more than one flaschenhals task in a long pipe (e.g. for watching compressed and uncompressed 
// file sizes during a compression of a large file)
//
// Giving a throughput allows to slow down the maximal pipe throughput. This is normally not very useful, but has been
// the reason for me to write this little program.


#include <stdio.h>
#include <libio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/timex.h>
using namespace std;

#define STDIN 0                 /* standard input  file descriptor */
#define STDOUT 1                /* standard output file descriptor */


#include <time.h>


const int dt = 100000;    // usec
int tabpos = 0;


void delay(int nsec)
{
  timespec request, remain;

  request.tv_sec = 0;
  request.tv_nsec = nsec;

  nanosleep(&request, &remain);
}


int t_usec()
{
  int ret;
  ntptimeval tptr;
  ntp_gettime (&tptr);
  ret = tptr.time.tv_sec * 1000000 + tptr.time.tv_usec;

  return ret;
}

void write(__off64_t nB)
{
  static int nC=0;
  static int oldnC = -1;
  static int flp = 0;
  static int divisor = 1;

  const int MAX_FLP = 4;
  const char* units[MAX_FLP+1] = {"B", "kB", "MB", "GB", "TB"};


  nC = nB / divisor;
  if (nC == oldnC)
      return;
  if (nC > 20*1024 && flp < MAX_FLP)
  {
      ++flp;
      divisor *= 1024;
      nC = nB / divisor;
      cerr << "\r";
      for(int k=0; k<tabpos; ++k)
	  cerr << "\t";
      cerr << "       ";
  }
  oldnC = nC;
  cerr << "\r";
  for(int k=0; k<tabpos; ++k)
    cerr << "\t";
  cerr << nC << " " << units[flp] << flush;
}

int main(int argc, char** argv)
{
  bool begrenzen = 0;
  float rate;
  int kb_sec;

  if (argc < 1 || argc > 3 || ( argc == 2 && ( argv[1][0] < '0' || argv[1][0] > '9')))
    {
      cerr << "usage: " << argv[0] << " [tab position] [kb/sec]\n";
      exit(1);
    }
  int soll = t_usec();
  if (argc >= 3)
    {
      begrenzen = 1;
      kb_sec = atol(argv[2]);
      rate = (float)kb_sec * (float)1024;
      cerr << "rate=" << rate << "  kb_sec=" << kb_sec << endl;
    }
  else
    {
      begrenzen = 0;
    }
  if (argc >= 2)
    {
      tabpos = atol(argv[1]);
    }

  
  int bsize = 10000;

  char* buffer = new char[bsize];

  __off64_t nB=0;

  int b, b2;
  do
    {
      b = read(STDIN, buffer, bsize);
      b2 = write(STDOUT, buffer, b);

      //      cerr << "read: " << b2 << endl;

      nB += b;
      write(nB);
      if (begrenzen)
      {
	  float dtime = (float)b / rate;
	  
	  int usec = (int)((1000000*dtime));
	  soll += usec;
	  
	  //      cerr << "dtime = " << dtime << "   nsec = " << nsec << endl;
	  
	  int t_u = t_usec();
	  int diff = soll - t_u;
	  if (diff > 1000000 || diff < -1000000)
	  {
	      soll = t_u;
	  }
	  if (diff > dt)
	      delay(1000*usec);
      }
      if (b != b2)
	  cerr << "error: read " << b << " bytes, wrote " << b2 << " bytes!\n";
    } while (b != 0);
  delete[] buffer;
  return 0;
}

