#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

__inline long long tvToLL( struct timeval t ) {
	return t.tv_usec + (1000000LL * t.tv_sec);
}

__inline long long llmin( long long a, long long b ) {
	if ( a < b ) return a;
	return b;
}

long long interpBitsPerSecond( char* arg ) {
	char* pos;
	int toret = strtol( arg, &pos, 10 );
	if ( (pos == arg) || (pos == NULL) ) {
		return -1;
	}
	if ( *pos == '\0' ) {
		return toret*8;
	}
	switch ( pos[0] ) {
		case 'k':
		case 'K':
			if ( pos[1] == 'b' ) {
				return toret * 1024;
			} else {
				return toret * 1024*8;
			}
			break;
		case 'm':
		case 'M':
			if ( pos[1] == 'b' ) {
				return toret * 1024*1024;
			} else {
				return toret * 1024*1024*8;
			}
			break;
		case 'g':
		case 'G':
			if ( pos[1] == 'b' ) {
				return toret * 1024*1024*1024;
			} else {
				return toret * 1024*1024*1024*8;
			}
			break;
		default:
			return toret;
	}
}

int main( int argc, char** argv ) {
	long long bps = 1024*8;
	int chunk = 1024;
	void* buffer;
	struct timespec tenth = { 0, 1000000000/30 }, toSleep, remainder;
	struct timeval nowtv;
	long long now, then;
	int err = 1;
	// bresenham
	long long epsilon = 0;
	long long total = 0;
	int i;
	int dryrun = 0;
	int verbose = 0;
	
	for ( i = 1; i < argc; i++ ) {
		if ( !strcmp( argv[i], "-d" ) ) {
			dryrun = 1;
			verbose = 1;
		} else if ( !strcmp( argv[i], "-v" ) ) {
			verbose = 1;
		} else if ( !strcmp( argv[i], "-r" ) ) {
			long long tbps;
			i++;
			tbps = interpBitsPerSecond( argv[i] );
			if ( tbps > 0 ) {
				bps = tbps;
			} else {
				fprintf( stderr, "bogus rate \"%s\"\n", argv[i] );
				exit(1);
			}
		} else if ( !strcmp( argv[i], "-c" ) ) {
			// set chunk size
			i++;
			chunk = atoi( argv[i] );
		} else if ( !strcmp( argv[i], "-t" ) ) {
			// set tick rate
			i++;
			tenth.tv_nsec = 1000000000/atoi( argv[i] );
		} else {
			long long tbps = interpBitsPerSecond( argv[i] );
			if ( tbps > 0 ) {
				bps = tbps;
			} else {
				fprintf( stderr, "bogus arg \"%s\"\n", argv[i] );
				exit(1);
			}
		}
	}
	if ( dryrun ) {
		err = chunk;
	}
	if ( verbose ) {
		fprintf(stderr, "chunk %d, rate %lld Bps\n", chunk, bps/8 );
	}
	gettimeofday( &nowtv, NULL );
	now = tvToLL( nowtv );
	then = now;

	buffer = malloc( chunk );
	while ( err > 0 ) {
		if ( (epsilon > 0) && (!dryrun) ) {
			err = read( STDIN_FILENO, buffer, chunk );
			if ( err < 0 ) {
				perror("read");
				exit(1);
			} else if ( err > 0 ) {
				err = write( STDOUT_FILENO, buffer, err );
				if ( err <= 0 ) {
					perror("write");
					exit(1);
				}			
			} else if ( verbose ) {
				fprintf(stderr,"read closes normally\n");
			}
		}
		gettimeofday( &nowtv, NULL );
		now = tvToLL( nowtv );
		if ( epsilon > 0 ) {
			total += err;
			if ( dryrun && 0 ) {
				fprintf(stderr,"now %-15lld epsilon %-15lld\n", now/1000000, epsilon );
			}
			if ( verbose ) {
				fprintf(stderr,"moved %9d bytes, total %lld\n",  err, total );
			}
			epsilon -= err * 8 * 1000000;//((now - then) * chunk) / 1000000;
		}
		/* Step by at most a tick. This caps max rate and doesn't let read stalls
			accrue bandwidth time to use. Also, doesn't let short sleeps run ticks too fast
			thus inflating bandwidth. */
		epsilon += (llmin( (tenth.tv_nsec / 1000), now - then ) * bps);
		then = now;
		toSleep = tenth;
		nanosleep( &toSleep, &remainder );
	}
	return 0;
}
