/*
   I got tired of `workbone' not being able to shuffle and other things.

   -- Roy Keene [021119991215] rkeene@netfueldesign.com
*/

#include <unistd.h>
#include <getopt.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#ifndef NO_SETPRIORITY
#include <sys/time.h>
#include <sys/resource.h>
#endif
#ifndef CDROM_DEV
#define CDROM_DEV "/dev/cdrom"
#endif
#include "cdrom.h"
/* Perhaps this is overdoing it? */
#include "random.h"
#include "cdplay.h"

#include <linux/cdrom.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>


extern char *optarg;
extern int optind, optopt, opterr;
int cdrom=-1;
int dsp=-1;

void shutdown(void) {
  if (cdrom!=-1) {
    cd_stop(cdrom);
    close(cdrom);
  }
  exit(0);
}

#ifdef MAKE_EJECT
int matchexec(char *path, char *cmp) {
  int i,x,y,z;
  x=strlen(path);
  y=strlen(cmp);
  if (x<y) z=x; else z=y;
  for (i=0;i<z;i++) {
    if (path[x-i]=='/' || path[x-i]=='\\') { return(0); } 
    if (path[x-i]!=cmp[y-i]) { return(1); }
  }
  return(0);
}
#endif

int printhelp(int fullhelp, char argv0[128]) {
   printf("usage: %s [-aheslqdbSV] [-v volume] [-t track#] [-f seconds] [-r seconds]\n",argv0);
   if (fullhelp==0) { return(0); }
   printf("   -V\tPrints version (%s).\n",VERSION);
   printf("   -h\tThis help screen.\n");
   printf("   -a\t*Raw play for people with broken analog cables.\n");
   printf("   -e\tEject the CD-ROM from the drive.\n");
   printf("   -s\tShuffle tracks randomly.\n");
   printf("   -l\tLoop tracks.\n");
   printf("   -S\tStop CD from playing.\n");
   printf("   -q\tQuiet (no tty output).\n");
   printf("   -d\tDisplay current Track, time, and volume.\n");
   printf("   -b\tStart in background\n");
   printf("   -vX\tSet the volume to X\n");
   printf("   -tX\tGoto track X.\n");
   printf("   -fX\t*Fast forward X seconds.\n");
   printf("   -rX\t*Rewind X seconds.\n");
   printf("%s with no arguments plays the CD all the way through in logical\n    order, once.\n",argv0);
   printf("\"*\"s indicate options that do not yet work.\n");
   return(0);
}

int main(int argc, char *argv[]) {
   char opt, *buf;
   char *track_buffer;
   struct cdrom_read_audio block_read;
   int start_track=0, num_tracks=0;
   int loop=0, shuffle=0, play=1, track=0, quiet=0;
   int action=CDPLAY_NOACTION, volume=0;
   int prio=20;
   int tracklist[128];
   int usedlist[128]=
     {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
   int i,x=0;
#ifdef DEBUG
   printf("cdplay.c: main(argc, argv)\n");
#endif

#ifdef MAKE_EJECT
#ifdef DEBUG
   printf("cdplay.c: main: argv[0]=\"%s\"\n",argv[0]);
#endif DEBUG
   if (!matchexec(argv[0],"eject")) { 
     signal(SIGINT, (void *)shutdown);
     signal(SIGHUP, (void *)shutdown);
     cdrom=open_cdrom(CDROM_DEV);
     if (cdrom<0) {
       printf("Unable to open %s\n",CDROM_DEV);
       return(-1);
     }
     cd_eject(cdrom); 
     close(cdrom);
     return(0);
   }
#endif MAKE_EJECT

   while ((opt=getopt(argc,argv,"adbhVeSqslt:f:r:v:"))!=-1) {
      switch(opt) {
         case 'b':
            if (fork()!=0) return(0);
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
            chdir("/tmp");
         case 'q':
            quiet=1;
            break;
         case 'v':
            action=CDPLAY_SETVOL;
            volume=atoi(optarg);
            break;
         case 'V':
           	printf("cdplay version %s.\n", VERSION);
            return(0);
         case 'S':
            play=0;
            break;
         case 'e':
            action=CDPLAY_EJECT;
            break;
         case 's':
            shuffle=1;
            break;
         case 'l':
            loop=1;
            break;
         case 't':
            i=0;
            track_buffer=strtok(optarg,",");
            while (1) {
              if (track_buffer==NULL) break;
              tracklist[i]=atoi(track_buffer);
              i++;
              track_buffer=strtok(NULL, ",");
            }
            start_track=0;
            num_tracks=i;
            if (num_tracks==1) { track=atoi(optarg); } else { track=-1; }
            break;
         case 'd':
            action=CDPLAY_DISPLAY;
            break;
         case 'a':
            prio=-20;
            action=CDPLAY_RAWPLAY;
            break;
         case 'h':
            printhelp(1, argv[0]);
            return(0);
         default: 
            printhelp(0, argv[0]);
            return(-1);
      }
   }      
#ifndef NO_SETPRIORITY
   setpriority(PRIO_PROCESS, 0, prio);
#endif
   signal(SIGINT, (void *)shutdown);
   signal(SIGHUP, (void *)shutdown);
   signal(SIGTERM, (void *)shutdown);
   cdrom=open_cdrom(CDROM_DEV);
   if (cdrom<0) {
     printf("Unable to open %s\n",CDROM_DEV);
     return(-1);
   }
#ifdef DEBUG
  	printf("cdplay.c: main: action=%i\n",action);
#endif
   switch(action) {
     case CDPLAY_DISPLAY:
       if (cd_isplaying(cdrom)) 
         printf("Current track is\t%i\nVolume is       \t%i\n",cd_currenttrack(cdrom),cd_getvolume(cdrom));
       else
         printf("CD is stopped.\n");
       close(cdrom);
       return(0);
     case CDPLAY_EJECT:
       cd_stop(cdrom);
       cd_eject(cdrom); 
       close(cdrom);
       return(0);
     case CDPLAY_SETVOL:
       cd_setvolume(cdrom, volume);
       if (cd_isplaying(cdrom)) return(0);
       break;
     case CDPLAY_RAWPLAY:
#ifndef NFRAMES
#define NFRAMES 1
#endif
       buf=malloc(2352*NFRAMES);
       ioctl(cdrom, CDROMSTART);
       block_read.addr.lba=1;
       block_read.addr_format=CDROM_LBA;
       block_read.nframes=NFRAMES;
       block_read.buf=buf;
       dsp=open("/dev/dsp",O_WRONLY);
       while (1) {
          block_read.addr.lba++;
          ioctl(cdrom, CDROMREADAUDIO, &block_read);
          write(dsp, buf, 2352*NFRAMES);
       }
       break;
   }
#ifdef DEBUG
   printf("cdplay.c: main: num_tracks=%i\n",num_tracks=cd_num_tracks(cdrom, 0));
   printf("cdplay.c: main: start_track=%i\n",start_track=cd_num_tracks(cdrom,1));
#else
   if (track!=-1) {
     num_tracks=cd_num_tracks(cdrom, 0);
     start_track=cd_num_tracks(cdrom,1);
   }
#endif
   if (argc==1) {
     cd_play(cdrom,start_track);
     close(cdrom);
     return(0);
   }


   if (play==0) { cd_stop(cdrom); close(cdrom); return(0); }

   if (track>0) {
      if (track<start_track || track>=(start_track+num_tracks)) {
         printf("No such track (%i)!\n",track);
         return(-1);
      }
      if (loop==0) {
        cd_playtrack(cdrom, track);
        close(cdrom);
        return(0);
      } else {
        while(1) {
          CDPLAY_LOOP_PLAY(cdrom, track);
          while (cd_currenttrack(cdrom)==track)
            usleep(CDPLAY_CHECK_DELAY);
        }
      }
   }

   if (track!=-1) {
      for (i=start_track;i<(start_track+num_tracks);i++) {
         tracklist[i]=i;
      }
   }


   x=1;
   while(loop+x>0) {

      if (shuffle==1) {
         for (i=start_track;i<(start_track+num_tracks);i++) {
            track=0;
            while(track==0) {
               x=(generate_random_number(num_tracks-1)+1);
               if (usedlist[x]==-1) {
                  tracklist[i]=x;
                  usedlist[x]=0;
                  track=1;
               }
            }
         }
      }
      for (i=start_track;i<(start_track+num_tracks);i++) {
         if (!quiet) printf("Playing track %i  \n",tracklist[i]);
         CDPLAY_LOOP_PLAY(cdrom, tracklist[i]);
         while (cd_currenttrack(cdrom)==tracklist[i])
           usleep(CDPLAY_CHECK_DELAY);
         if (!quiet) {
           printf("\033[1A");
           fflush(stdout);
         }
         if (shuffle==1) usedlist[i]=-1;
      }
      x=0;
   }
   close(cdrom);
   return(0);
}
