/*
 * Copyright: GNU Public License 2 applies
 *
 *   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 2, 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, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * READALL (C) 1998 Heiko Eissfeldt heiko@colossus.escape.de
 *
 * last changes:
 *   May 98 - first version
 *
 */

#if defined (HAVE_UNISTD_H) && (HAVE_UNISTD_H == 1)
#include <sys/types.h>
#include <unistd.h>
#endif

#if defined(_GNU_C_SOURCE) || defined(__USE_GNU)
#define USE_GETOPT_LONG
#endif

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#if defined (HAVE_FCNTL_H) && (HAVE_FCNTL_H == 1)
#include <fcntl.h>
#endif
#if defined (TIME_WITH_SYS_TIME)
#include <sys/time.h>
#include <time.h>
#else
#if defined (HAVE_SYS_TIME_H) && (HAVE_SYS_TIME_H == 1)
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#if defined (HAVE_LIMITS_H) && (HAVE_LIMITS_H == 1)
#include <limits.h>
#endif
#if defined (HAVE_SYS_IOCTL_H) && (HAVE_SYS_IOCTL_H == 1)
#include <sys/ioctl.h>
#endif

#if defined (HAVE_SYS_WAIT_H) && (HAVE_SYS_WAIT_H == 1)
#include <sys/wait.h>
#endif
#include <saveargs.h>
#include <scsitransp.h>

#include "mycdrom.h"
#include "interface.h"  /* low level cdrom interfacing */
#include "readall.h"
#include "resample.h"
#include "toc.h"
#include "ringbuff.h"
#include "global.h"


static const char * optstring = "D:A:I:O:o:n:l:v:S:"
                         "NqBVh";

#ifdef USE_GETOPT_LONG
#include <getopt.h>	/* for get_long_opt () */
struct option options [] = {
	{"device",required_argument,NULL,'D'},
	{"auxdevice",required_argument,NULL,'A'},
	{"interface",required_argument,NULL,'I'},
	{"offset",required_argument,NULL,'o'},
	{"sectors-per-request",required_argument,NULL,'n'},
	{"buffers-in-ring",required_argument,NULL,'l'},
	{"verbose-level",required_argument,NULL,'v'},
	{"no-write",no_argument,NULL,'N'},
	{"quiet",no_argument,NULL,'q'},

	{"speed-select",required_argument,NULL,'S'},

	{"bulk",no_argument,NULL,'B'},
	{"verbose-SCSI",no_argument,NULL,'V'},
	{"help",no_argument,NULL,'h'},
	{NULL,0,NULL,0}
};

#endif /* USE_GETOPT_LONG */

/* global variables */
global_t global;


/* report a fatal error, clean up and exit */
void FatalError (const char *szMessage, ...)
{
  va_list marker;

  va_start(marker, szMessage);

  vfprintf (stderr, szMessage, marker);

  va_end(marker);

  exit (1);
}


static void usage( void )
{
  fprintf( stderr,
"readall [-c chans] [-s] [-m] [-b bits] [-r rate] [-a divider] [-S speed]\n"
"         [-t track[+endtrack]] [-i index] [-o offset] [-d duration] [-F] [-G]\n"
"         [-x] [-q] [-w] [-v] [-R] [-P] [-X] [-Y] [-Z] [-y threshold] [-z]\n"
"	  [-e] [-n sectors] [-N] [-H] [-l buffers] [-D device] [-I interface]\n"
"         [-O audiotype] [-B] [-E output-endianess] [-A auxdevice] [audiofile]\n"
"Version "VERSION"\n"
"readall copies parts from audio cd's directly to wav or au files.\n"
"It requires a supported cdrom drive to work.\n"
"options: -D device   : set the cdrom or scsi device (as Bus,Id,Lun).\n"
"         -A auxdevice: set the aux device (typically /dev/cdrom).\n"
"         -I interface: specify the interface for cdrom access.\n"
"                     : (generic_scsi or cooked_ioctl).\n"
"         -S speed    : set the cdrom drive to a given speed during reading\n"
"         -n sectors  : read sectors per request.\n"
"         -l buffers  : use a ring buffer with 'l' elements.\n"
"         -o offset   : start 'offset' sectors behind start track/index.\n"
"                       one sector equivalents 1/75 second.\n"
"         -d duration : set recording time in seconds or 0 for whole track.\n"
"         -v level    : print informations on current cd (level: 0-63).\n"
#ifdef MD5_SIGNATURES
"         -M count    : calculate MD-5 checksum for 'count' bytes.\n"
#endif
#ifdef USE_GETOPT_LONG
"         -V          : print version information.\n"
"         -h          : this help screen.\n"  
#endif
          );
  exit( 1 );
}

static void init_globals(void)
{
  strncpy(global.dev_name, CD_DEVICE, sizeof(global.dev_name));	/* device name */
  strncpy(global.aux_name, AUX_DEVICE, sizeof(global.aux_name));/* auxiliary cdrom device */
  global.cooked_fd  = -1;	/* cdrom file desc */
  global.no_file  =  0;		/* flag no_file */
  global.no_infofile  =  0;	/* flag no_infofile */
  global.quiet	  =  0;		/* flag quiet */
  global.verbose  =  3 + 32 + 64;	/* verbose level */
  global.iloop    =  0;		/* todo counter */
  global.buffers = 2;           /* buffers to use */
  global.nsectors = NSECTORS;   /* sectors to read in one request */
  global.speed = DEFAULT_SPEED; /* use default */ 
}

#if !defined (HAVE_STRCASECMP) || (HAVE_STRCASECMP != 1)
#include <ctype.h>
static int strcasecmp(const char *s1, const char *s2)
{
  while (s1 && s2 && tolower(*s1) == tolower(*s2)) {
    s1++;
    s2++;
  }
  if (s1 == NULL && s2 == NULL) return 0;
  if (s1 == NULL) return -1;
  if (s2 == NULL) return +1;
  return tolower(*s1) - tolower(*s2);
}
#endif

static unsigned long SectorBurst;

#if      defined _POSIX_PRIORITY_SCHEDULING
#include <sched.h>
#endif

/* and finally: the MAIN program */
int main( int argc, char *argv [] )
{
  long lSector = 0;
  long BeginAtSample;
  long lSector_p1;
  long lSector_p2;
  long sector_offset = 0;
  unsigned long SamplesToWrite; 
  int just_the_toc = 0;
  char fname[200];
  int c;
#ifdef USE_GETOPT_LONG
  int long_option_index=0;
#endif /* USE_GETOPT_LONG */
  char * env_p;

  save_args(argc, argv);

  /* init global variables */
  init_globals();

  env_p = getenv("CDR_DEVICE");
  if (env_p != NULL) {
    strncpy( global.dev_name, env_p, sizeof(global.dev_name) );
    global.dev_name[sizeof(global.dev_name)-1]=0;
  }

  /* command options parsing */
#ifdef USE_GETOPT_LONG
  while ( (c = getopt_long (argc,argv,optstring,options,&long_option_index)) 
		!= EOF)
#else
  while ( (c = getopt(argc, argv, optstring)) != EOF )
#endif /* USE_GETOPT_LONG */
	{
    switch (c) {
      case 'D':    /* override device */
        strncpy( global.dev_name, optarg, sizeof(global.dev_name) );
        global.dev_name[sizeof(global.dev_name)-1]=0;
	break;
      case 'A':    /* override device */
        strncpy( global.aux_name, optarg, sizeof(global.aux_name) );
        global.aux_name[sizeof(global.aux_name)-1]=0;
	break;

      case 'S':    /* override drive speed */
	global.speed = strtoul( optarg, NULL, 10);
	break;
      case 'o':    /* override offset */
	sector_offset = strtol( optarg, NULL, 10);
	break;
      case 'n':    /* read sectors per request */
	global.nsectors = strtoul( optarg, NULL, 10);
	break;

      case 'v':    /* tell us more */
	global.verbose = strtol( optarg, NULL, 10);
	break;
      case 'q':    /* be quiet */
	global.quiet = 1;
	global.verbose = 0;
	break;
      case 'V':
	jes_verbose = 1;
	break;
#ifdef USE_GETOPT_LONG
      case 'h':
	usage();
	break;
#endif
      default:
        usage();
    }
  }

  /* check all parameters */
  if (global.verbose < 0 || global.verbose > SHOW_MAX) {
    fprintf(stderr, "Error: incorrect verbose level setting: %d\n",global.verbose);
    usage();
  }
  if (global.verbose == 0) global.quiet = 1;

  /* all options processed. Now a file name may follow */
  if ( optind < argc ) {
    if (!strcmp(argv[optind],"-")) {
      /* pipe mode */
      global.audio = dup (fileno(stdout));
      strncpy( global.fname_base, "standard output", sizeof(global.fname_base) );
    } else {
      /* filename given */
      strncpy( global.fname_base, argv[optind], sizeof(global.fname_base)-8 );
    }
    global.fname_base[sizeof(global.fname_base)-1]=0;
  }

  /* setup interface and open cdrom device */
  /* request semaphores and shared memory */
  SetupInterface( );

  /* paranoia reigns supreme */
#if defined(HAVE_SYNC) && (HAVE_SYNC == 1)
  sync();
#endif

  /* try to get some extra kicks */
#if defined(HAVE_NICE) && (HAVE_NICE == 1)
  nice(-20);
#endif

  lSector += sector_offset;

  if (global.speed != 0 && SelectSpeed != NULL)
     SelectSpeed(global.speed);

  if ( !global.no_file ) {
      strcpy(fname,global.fname_base);
  }

  global.audio = creat(fname, 0600);
  while (1) {
      /* how many sectors should be read */
      SectorBurst = global.nsectors;

      ReadCdRom( bufferCdda, lSector, SectorBurst );

      if ( SaveBuffer ( bufferCdda, CD_FRAMESIZE_RAWER / 4, NULL) ) {
	break;
      }
fprintf(stderr, "sectors 0 - %lu\n", lSector);
      lSector += SectorBurst;
  }
  exit(0);

  return 0;
}
