#!/usr/bin/perl -W 
use strict;
use warnings;
use diagnostics;
use IPC::Shareable (':lock');
use Parallel::ForkManager;
use TapeChanger::MTX;
use File::LockDir;
our $$;
our $lockfile = '/tmp/.changer.lck';
my $vers = 0.10;
my %options = (
     create    => 'yes',
     exclusive => 0,
     mode      => 0644,
     destroy   => 'yes',
 );

$SIG{INT} = \&catch_int;
 sub catch_int {
     print "$$ - Ieeee! I was murdered. \n";
     cleanup();
     die "Some day I will get revenge.\n";
 }

# Script to rip MP3s on a SCSI Jukebox


sub help {
  print <<EOL;
Usage: $0 <command> [arg...]
  --startslot <slot>  slot to start ripping
	ex. --startslot 0
  --endslot   <slot>  slot to stop ripping
	ex. --endslot 10
  --drive <drvive>  || --drive <drive> --drive <drive> 
	ex. --drive 0
	ex. --drive 0 --drive 1 --drive 2 ...
EOL
exit 0;
}


# Setup some defaults
#$startslot = 0;
#$endslot = 10;
#@opt_drive = (0, 1, 2, 3, 4);
my $startslot;
my $endslot;
my %drives;
my %slots;
our @opt_drive;
my $debug;

sub getopts {
 use Getopt::Long;
  Getopt::Long::Configure ("bundling", "noignorecase");
    GetOptions (
# Super Settings
               'help|h|?' => \&help,
               'version|v' => sub { print "$$ - chrip version $vers\n"; exit; },
               # 'debug|d' => sub { $noise = 2; },
#	 	my $debug;
#               'D' => \$debug,
               # 'conf!' => \$use_conf,
               # 'nice=i' => \$nice,
               # 'noise|verbose|n' => \$noise,
               # 'step|s=i' => \$step,
               # 'track|single|t=i' => \$indi,
               # 'munge' => \$munging,
	      'startslot|s=i' => \$startslot, 
	      'endslot|e=i' => \$endslot, 
 	      'drive=i@'
);
		return($startslot, $endslot, @opt_drive);
}

sub checkconfig {
my($startslot, $endslot) = @_;

if ( -c '/dev/changer' ) {
	print "$$ - Changer device exists, good.\n";
	open LOCKFILE, ">>$lockfile" || die "couldn't open LOCKFILE\n";
               print LOCKFILE $$;
        close LOCKFILE;

	}
	else {
		print "$$ - Changer device /dev/changer does not exist. exiting! \n";
		exit 1;
		}

# Setup some defaults if not specified
if ($startslot < 1) {
       print "$$ - Starting slot $startslot is not valid storage element slots start with one, incrementing by one.\n";
                   }

$startslot = $startslot || 1;
$endslot = $endslot || TapeChanger::MTX->numslots;;

if ($startslot > $endslot) {
	print "$$ - Starting slot $startslot is greater than ending slot $endslot, starting slot must be less than ending slot. exiting! \n";
	exit 1;
	}
	return 0;
}
checkconfig(getopts());

#@opt_drive = @opt_drive || (0, 1, 2, 3, 4);

sub printopts {
my($startslot, $endslot, @opt_drive) = @_;
my $drive;
  my $i;
  print "$$ - Starting slot will be $startslot.\n";
  print "$$ - Ending slot will be $endslot.\n";
my $numdrives = TapeChanger::MTX->numdrives;
    foreach $i (@opt_drive) {
#	print "$$ - $i\n";
        $drive = "$i";
	if ($drive < $numdrives) {
	print "$$ - Drive $drive configured.\n";
				 }
	else {
	print "$$ - Specified drive $drive has a higher number than exists in the juke, specified drives mush exist in jukebox. exiting! \n";
	exit 1;
	     }

	if ( -l "/dev/drive$drive") {
        print "$$ - Drive device exists, good.\n";
        }
        else {
                cleanup();
                die "$$ - Drive device /dev/drive$drive does not exist. exiting! \n";
                }

#	print "$$ - Checking that specified drive $drive will come ready, this could take up to 60 seconds \n";
#	my $checkdrive = TapeChanger::MTX->checkdrive($drive);
#		if ($checkdrive = 1) {
#		print "$$ - Drive $drive checked out ok. \n";
#				     }
#		else {
#		print "$$ - Drive $drive failed, please contact your systems administrator. exiting! \n";
#		exit 1;
#		     }

        }

}

printopts (getopts());

sub createslothash {
print "$$ - Setting up table of slots, please wait...\n";
#my %slots;
tie %slots, 'IPC::Shareable', 'sh', { %options } or
    die "server: sh tie failed\n";
%slots = TapeChanger::MTX->minislothash;
print "$$ - Slot hash is setup \n";
#%slots = TapeChanger::MTX->slothash;
my $slot;
#foreach $slot (sort keys %slots) {
	#print "$$ - Slot $slot - Type: $slots{$slot}[0] Contents: $slots{$slot}[1] Tag: $slots{$slot}[2] \n";
	#print "$$ - Slot $slot - Contents: $slots{$slot} \n";
#	}
}
createslothash();

sub createdrivehash {
print "$$ - Setting up table of drives, please wait...\n";
#my %drives;
tie %drives, 'IPC::Shareable', 'dh', { %options } or
    die "server: dh tie failed\n";
%drives = TapeChanger::MTX->minidrivehash;
#%drives = TapeChanger::MTX->drivehash;
print "$$ - Drive hash is setup \n";
#my $drive;
#foreach $drive (sort keys %drives) {
        #print "$$ - Drive $drive - Type: $drives{$drive}[0] Contents from slot: $drives{$drive}[1] Tag: $drives{$drive}[2] \n";
#        print "$$ - Drive $drive - Contents: $drives{$drive}\n";
#        }
}
createdrivehash();
# fisher_yates_shuffle( \@array ) : generate a random permutation
# of @array in place
sub fisher_yates_shuffle {
    my $array = shift;
    my $i;
    for ($i = @$array; --$i; ) {
        my $j = int rand ($i+1);
        next if $i == $j;
        @$array[$i,$j] = @$array[$j,$i];
    }
}
sub pickdrive {
my($startslot, $endslot, @opt_drive) = @_;
	my $i;
	my $drive;
	my $available;
	fisher_yates_shuffle( \@opt_drive );
	foreach $i (@opt_drive) {
        	$drive = "$i";
		unless (defined($drives{$i})) {
#			print "$$ - Drive $drive is open.\n";
			      $available = $drive;
				next;
					    }
				 }
		
	if (defined $available) {
		#print "$$ - Drive $drive is free to use.\n";
		return $available;
			}
	else {
		#print "$$ - There are no free drives.\n";
	        return '-1';
       		     }
}

#sub pickdrive {
#my($startslot, $endslot, @opt_drive) = @_;
#        my $i;
#        my $drive;
#        fisher_yates_shuffle( \@opt_drive );
#        foreach $i (@opt_drive) {
#                $drive = "$i";
#                if (defined($drives{$i})) {
#        print "$$ - There are no free drives.\n";
#        return '-1';
#                                            }
#                else {
#       print "$$ - Drive $drive is free to use.\n";
#my      $available = $drive;
#        return $available;
#                     }
#        }
#}




sub updateslothash {
my ($srcslot,$status) = @_;
my %options = (
     create    => 'yes',
     exclusive => 0,
     mode      => 0644,
     destroy   => 'yes',
 );
print "$$ - Connecting to slot table.\n";

tie %slots, 'IPC::Shareable', 'sh', { %options } or
     die "server: tie failed\n";
print "$$ - Slot table connected.\n";

	print "$$ - Updating status for slot $srcslot to $status.\n";
	if ($srcslot) {
	$slots{$srcslot} = $status;
	print "$$ - Slot status is now: $slots{$srcslot}\n";
			}
	else {
	$slots{$srcslot} = undef;
	print "$$ - Drive status is now: Undefined";
	print "$$ - This is abnormal, please report it to your systems administrator.\n";
	     }
}

sub updatedrivehash {
my ($device,$srcslot) = @_;
my %options = (
     create    => 'yes',
     exclusive => 0,
     mode      => 0644,
     destroy   => 'yes',
 );
print "$$ - Connecting to drive table.\n";
tie %drives, 'IPC::Shareable', 'dh', { %options } or
     die "server: tie failed\n";
print "$$ - Drive table connected.\n";

	print "$$ - Updating status for drive $device \n"; 
	if ($srcslot) {
        $drives{$device} = $srcslot;
	print "$$ - Drive status is now: $drives{$device}\n";
			}
	else {
	$drives{$device} = undef;
	print "$$ - Drive status is now: Empty\n";
	     }

}

sub dolock {

if ( -c '/dev/changer' ) {
        print "$$ - Changer device exists, good.\n";
        }
        else {
		cleanup();
		die "$$ - Changer device /dev/changer does not exist. exiting! \n";
                }

if (nflock($lockfile, '2')) {
#open LOCKFILE, ">>$lockfile" || die "couldn't open LOCKFILE\n";
#               print LOCKFILE $$;
#        close LOCKFILE;
        print "$$ - Changer is free, locking.\n";
        return '1';
}

else {
 print "$$ - Changer is locked, try again.\n";
        return '-1';
        }   
}


#sub dolock {    
#my $lockfile = '/tmp/.changer.lck';
#if ( -f $lockfile ) { 
#        print "$$ - Changer is locked, try again.\n";
#        return '-1';
#        }       
#        else {  
#        open LOCKFILE, ">>$lockfile" || die "couldn't open LOCKFILE\n";
#                print LOCKFILE scalar localtime(), "\n";
#        close LOCKFILE;
#        print "$$ - Changer is free, locking.\n";
#        return '1';
#         }
#}


sub dounlock {
if ( -c '/dev/changer' ) {
        print "$$ - Changer device exists, good.\n";
        }
        else {
                cleanup();
                die "$$ - Changer device /dev/changer does not exist. exiting! \n";
                }

	nunflock($lockfile);
	print "$$ - Removing lock on changer. \n";
 }

#sub dounlock {
#my $lockfile = '/tmp/.changer.lck';
#        unlink($lockfile);
#        print "$$ - Deleting lock file \n";
# }

sub checksrcslot {
my $contents;
#	my($number) = shift;
my ($number,) = @_;
        $contents = $slots{$number};
                if ( $contents eq 'Full' ) {
		return $number;
                }
}
sub randsleep {
my ($randomamt,$fixamt) = @_;
my $sleep = rand($randomamt) + $fixamt;
       # print "$$ - Sleeping for $sleep\n";
        sleep $sleep;
}

sub mailload {
my ($number,) = @_;
my $mailslot = '701';
if (checksrcslot($mailslot)) {
	print "$$ - The mailslot is full, loading";
	unless (TapeChanger::MTX->transfertape( $mailslot, $$number))
                {
                die "couldn't load disc in mailslot $mailslot to slot $number.\n";
                }
	
	
				}

}

sub wedgewatcher {
my %ttys;
  my @ttys;
  foreach (`/usr/bin/ps -ef`)
  {
    my ($user, $tty) = (split)[0,5];
    push @ttys, $tty;
    print "User: $user TTY: $tty \n"; 
    #push @ttys, $tty if $user eq 'tkil' && !$seen{$tty}++;
   }
}

sub ripit {
my ($srcslot,$device) = @_;
my $retry = '-1';
my $maxretry = '99';
while ($retry < $maxretry) {
my $locked = dolock();
	if ($locked >= 1) {
		if ( -l "/dev/drive$device") {
        		print "$$ - Drive device exists, good.\n";
        				    }
        	else {
                	cleanup();
                	die "$$ - Drive device /dev/drive$device does not exist. exiting! \n";
                }
		print "$$ - Updating slothash and drivehash.\n";
		updateslothash($srcslot,'Empty');
                updatedrivehash($device,$srcslot);
		unless (TapeChanger::MTX->miniloadtape( $srcslot, $device))
		{
                die "couldn't load disc in slot $srcslot to drive $device.\n";
                }
		print "$$ - Sleeping waiting for disc to be loaded and drive to come ready.\n";
		sleep 45;
		unless (system("/usr/bin/cdparanoia -Q -d /dev/drive$device")) {
		print "$$ - Still waiting for drive to come ready.\n";
			}
		sleep 1;
		dounlock();
                $retry = $maxretry + 1;
                }
                else {
                updatedrivehash($device,$srcslot);
		updateslothash($srcslot,'Empty');
		print "$$ - Claimed this drive for myself. \n";
                print "$$ - Changer is locked by another process, retry $retry. \n";
		randsleep(7,30);
                $retry++;
                }
	}
		print "$$ - \n";
		print "$$ - Drive $device will be used.\n";
		print "$$ - Running tear on drive $device\n";

		my $cd = "CD from slot $srcslot written to drive $device.\n";
		my $logfile = "/tmp/$srcslot.job"; 
		open LOGFILE, ">>$logfile" || die "couldn't open LOGFILE $logfile\n"; 
		print LOGFILE scalar localtime(), "\n"; 
		print LOGFILE "$cd";
		alarm(7501);	
		#our $child = open(TEAR, "cd /data/music; /usr/local/bin/tear --nocddb --verbose --bitrate 320 --cdrom /dev/drive$device |") or die "couldn't run program: $!\n";
		our $child = open(TEAR, "cd /data/music; /usr/local/bin/tear --verbose --bitrate 320 --cdrom /dev/drive$device |") or die "couldn't run program: $!\n";
		while(<TEAR>) {
			my $output .= $_;
			print LOGFILE "$output";
			}
		alarm(0);
		close(TEAR);
		print LOGFILE scalar localtime(), "\n"; 
		close LOGFILE;
		# Pretending tear is running
		#randsleep(5,600);
$SIG{ALRM} = sub {
print "Alarm!\n";
print "Killing my tear child: $child. \n";
kill 9, $child;
print "Sleeping waiting for child to die. \n";
sleep 61;

unless (system("ps -p $child")) {
print "Still sleeping waiting for child to die. \n";
sleep 10;
kill 9, $child;
}
};

$retry = '-1';
$maxretry = '99';
while ($retry < $maxretry) {
my $locked = dolock();
        if ($locked >= 1) {
		if ( -l "/dev/drive$device") {
        		print "$$ - Drive device exists, good.\n";
        					}
        	else {
                	cleanup();
                	die "$$ - Drive device /dev/drive$device does not exist. exiting! \n";
                }

		unless (TapeChanger::MTX->miniunloadtape( $srcslot, $device))
		{
		die "couldn't unload disc in drive $device to slot $srcslot.\n";
		}
      		print "$$ - Sleeping waiting for disc to be unloaded.\n";
                sleep 45;
		updateslothash($srcslot,'Full');
                updatedrivehash($device);
		dounlock();
		print "$$ - I am done, bah-by.\n";
                $retry = $maxretry + 1;
                }
                else {
                $retry++;
                print "$$ - Changer is locked by another process, retry $retry. \n";
		randsleep(5,30);
                }
        }
}

sub main {
my($startslot, $endslot) = @_;
my $srcslot;
$srcslot=$startslot;
#for ($srcslot >= $startslot; $srcslot <= $endslot;) {
my $pm = new Parallel::ForkManager(5); 
for (;$srcslot <= $endslot;) {

		if (checksrcslot($srcslot)) {
			#print "$$ - The disc in slot $srcslot will be burned. \n";
			my $pickdrive;
			undef($pickdrive);
			$pickdrive = pickdrive(getopts());
			#print "$$ - Pickdrive says: $pickdrive\n";
				if ($pickdrive >= 0) {
				 $pm->run_on_start(
    			sub { 
    				print "$$ - Child has started incrementing source slot from $srcslot.\n";
					undef($pickdrive);
                                        $srcslot++; 
                                        print "$$ - Slot $srcslot will be next. \n";
					randsleep(5,60);
			    }
				);
					$pm->start and next; # do the fork
					ripit($srcslot,$pickdrive);
					$pm->finish;
						}
				else		{
					#print "$$ - No drives are available, sleeping.\n";
					undef($pickdrive);
					randsleep(5,5);
						}
		}
		else {
			print "$$ - Slot $srcslot is empty, it will not be burned. \n";
			$srcslot++;
		     }
		}
		  print "$$ - Waiting for Children...\n";
                 $pm->wait_all_children;
                 print "$$ - Everybody is out of the pool!\n";
}

sub cleanup {
print "$$ - Cleaning up shared memory segments.\n";
IPC::Shareable->clean_up_all;
dounlock();
print "$$ - Removing lockfile.\n";
unlink($lockfile);
print "$$ - Done. Goodby... \n";

}

main(getopts());
cleanup();

# my $loaded = TapeChanger::MTX->loadedtape;
#      print "$$ - Currently loaded: $loaded\n" if ($loaded);
# my $numdrives = TapeChanger::MTX->numdrives;
#	print "$$ - Number of drives: $numdrives\n" if ($numdrives);





