#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

#define MAX_LEN   (2000)

static double RAT( unsigned long long a, unsigned long long b )
{
  if( b == 0 )
	return 0.0;
  else
	return ( ( double ) a ) / b;
}

static unsigned long long tdiff(struct timeval *t1, struct timeval *t2)
{
  return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec);
}

int main( int argc, char **argv )
{
  const char alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  unsigned long long i;
  char name[ MAX_LEN + 1 ];
  int min;
  int base;
  struct timeval start;
  struct timeval instant;
  unsigned long prev;
  unsigned long N;
  int ch;
  int pad;
  unsigned long cycle;
  int dodirs;
  int writelen;
  int reverse;
  char *buf;

  N = 0;
  pad = 0;
  dodirs = 0;
  cycle = 20000;
  writelen = 0;
  buf = 0;
  reverse = 0;
  while( ( ch = getopt( argc, argv, "dn:p:c:w:r" ) ) != -1 )
	{
	  switch( ch )
		{
		case 'n':
		  N = atol( optarg );
		  break;
		case 'p':
		  pad = atoi( optarg );
		  break;
		case 'c':
		  cycle = atol( optarg );
		  break;
		case 'd':
		  dodirs = 1;
		  break;
		case 'w':
		  writelen = atoi( optarg );
		  break;
		case 'r':
		  reverse = 1;
		  break;
		default:
		  exit( 0 );
		}
	}

  base = strlen( alphabet );
  memset( name, base + 10, MAX_LEN + 1 );
  prev = 0;
  gettimeofday( &instant, 0 );
  start = instant;

  if( writelen > 0 )
	{
	  if( dodirs )
		{
		  fprintf( stderr, "Cannot write into directories\n" );
		  exit( 1 );
		}
	  buf = ( char * )malloc( writelen );
	  if( buf == NULL )
		{
		  perror( "malloc" );
		  exit( 1 );
		}
	}

  for( i = 0 ; N && i < N ; ++ i )
	{
	  int j;
	  int c;
	  int fd;
	  int shift;
	  char fname[ MAX_LEN + 1 ];

	  for( j = MAX_LEN - 1, c = 1 ; ( j >= 0 ) && c ; -- j )
		{
		  c = 0;
		  if( name[ j ] == base + 10 )
			{
			  name[ j ] = 1;
			  min = j;
			}
		  else if( name[ j ] == base - 1 )
			{
			  c = 1;
			  name[ j ] = 0;
			}
		  else
			{
			  name[ j ] ++;
			}
		}
	  if( c == 1 )
		{
		  exit( 1 );
		}
	  if( pad )
		{
		  shift = pad - ( MAX_LEN - min );
		  if( shift < 0 )
			{
			  shift = 0;
			}
		  for( j = 0 ; j < shift ; ++ j )
			{
			  fname[ j ] = '#';
			}
		}
	  else
		{
		  shift = 0;
		}
	  for( j = min ; j < MAX_LEN ; ++ j )
		{
		  fname[ j - min + shift ] = alphabet[ ( int ) name[ j ] ];
		}
	  fname[ MAX_LEN - min + shift ] = 0;
	  if( reverse ) 
		{
		  int len;

		  len = MAX_LEN - min + shift;
		  for( j = 0 ; j < len / 2 + 1 ; ++ j )
			{
			  char swap;

			  swap = fname[ j ];
			  fname[ j ] = fname[ len - j - 1 ];
			  fname[ len - j - 1 ] = swap;
			}
		}
	  if( dodirs )
		{
		  fd = mkdir( fname, 0744 );
		}
	  else
		{
		  fd = open( fname, O_CREAT | O_WRONLY, 0444 );
		}
	  if( fd == -1 )
		{
		  perror( "open" );
		  printf( "%li files created\n", i );
		  exit( 2 );
		}
	  if( !dodirs )
		{
		  if( writelen > 0 )
			{
			  int result;

			  if( write( fd, buf, writelen ) != writelen )
				{
				  perror( "write" );
				  exit( 3 );
				}
			}
		  close( fd );
		}
	  if( ( i % cycle ) == 0 )
		{
		  struct timeval now;

		  gettimeofday( &now, 0 );
		  printf( "%lli\t files: %lli (%f/%f), %s\n", i, 
				  tdiff( &now, &instant ),
				  RAT( i * 1000000, tdiff( &now, &start ) ), 
				  RAT( ( i - prev ) * 1000000, tdiff( &now, &instant ) ),
				  fname );
		  gettimeofday( &instant, 0 );
		  prev = i;
		}
	}
  if( buf != NULL )
	free( buf );
}
