#!/usr/bin/perl -w

use strict;
use Config::General;
use CDDB_get qw(get_cddb);
use Getopt::Long qw/:config no_ignore_case/;
use Text::Unaccent;
use Data::Dumper;
use diagnostics;

my $Conf;

sub debug($) {
	return if !defined $Conf->{verbose} || ! $Conf->{verbose};
	print "D: ", $_[0], "\n";
}

sub warning($) {
	warn "W: ", $_[0], "\n";
}

sub make_dirs  {
	my $cmd = "mkdir -p " . $Conf->{path};
	debug $cmd;
	qx/$cmd/ if !defined $Conf->{dummy};
}

sub change_dir {
	debug "Changing to directory: " . $Conf->{path};
	chdir $Conf->{path};
}

sub print_help {
	my $err_code = shift || 0;
	print <<"EOF";
cwcdr version 2.0.0
Copyright (c) 2002, 2003, 2004 Sbastien Gross

$0 [options]

Configuration options:
   --rip-file		-r  <file>	Rip info file location
   --device		-D  <device>	CD-Rom device
   --charset		-C  <charset>	Charset used for file tags
   --year		-y  <year>	Set disc year
   --performer		-P  <name>	Set disc performer (CD-Text)
   --prefix		-p  <prefix>	Move file to <prefix>
   --tracks		-t  <tracks>	Rip only selected tracks
   					separate tracks with \":\"
					use \"-\" for range
   --read-rip-from-file	-F		Use rip info file
					instead of fetching info
   --dummy		-u		Dummy mode run
   --verbose		-V		Run in verbose mode

Actions:
   --display-rip-info	-d		Display rip info
   --write-rip-info	-W		Save the rip info file
   --write-cdda-info	-I		Write CDDA info file
   --make-dirs		-m		Make directories
   --change-dir		-g		Change to directory
   --daemon		-n		Run in daemon mode
   --help		-h		Display this help

Looped user defined actions:
EOF
	foreach (keys %{$Conf->{commands}}) {
		s/_/-/go;
		print "   --$_\n";
	}
	print "\nOne run user defined actions:\n";
	foreach (keys %{$Conf->{one_run_commands}}) {
		s/_/-/go;
		print "   --$_\n";
	}
	exit $err_code;
}

sub get_options {
	my %options = (
		"rip-file|r=s" => \$Conf->{rip_file},
		"verbose|V" => \$Conf->{verbose},
		"device|D=s" => \$Conf->{device},
		"year|y=i" => \$Conf->{year},
		"charset|C=s" => \$Conf->{charset},
		"read-rip-from-file|F" => \$Conf->{read_rip_from_file},
		"display-rip-info|d" => \$Conf->{display_rip_info},
		"write-rip-info|W" => \$Conf->{write_rip_info},
		"help|h" => \$Conf->{help},
		"write-cdda-info|I" => \$Conf->{write_cdda_info},
		"dummy|u!" => \$Conf->{dummy},
		"performer|P=s" => \$Conf->{disc_performer},
		"make-dirs|m" => \$Conf->{make_dirs},
		"change-dir|g" => \$Conf->{change_dir},
		"prefix|p=s" => \$Conf->{prefix},
		"tracks|t=s" => \$Conf->{_tracks},
   		"daemon|n!" => \$Conf->{daemon},
	);
	foreach (keys %{$Conf->{commands}}) {
		s/_/-/go;
		$options{$_} = \$Conf->{exec}->{$_};
	}
	foreach (keys %{$Conf->{one_run_commands}}) {
		s/_/-/go;
		$options{$_} = \$Conf->{one_run_exec}->{$_};
	}

	GetOptions(%options) || print_help 1;
	if(defined $Conf->{_tracks}) {
		foreach my $t (split /:/, $Conf->{_tracks}) {
			push @{$Conf->{tracks}}, $t =~ m/(\d+)-(\d+)/ ? ($1..$2) : int $t;
		}
	}
	delete $Conf->{_tracks};
}

sub read_config($) {
	my $file = shift;
	my $c = new Config::General(
		-ConfigFile => $file,
		-InterPolateVars => 1
		);
	$Conf = {$c->getall()};
	# Allow to escape $
	foreach (keys %{$Conf->{commands}}) {
		$Conf->{commands}->{$_} =~ s/\\\$/\$/g;
	}
	foreach (keys %{$Conf->{one_run_commands}}) {
		$Conf->{one_run_commands}->{$_} =~ s/\\\$/\$/g;
	}
}

sub fetch_rip_info {
	my %cd = get_cddb($Conf->{cddb});

	my $artist = $Conf->{disc_performer} = $cd{'artist'} || undef;
	$artist =~ s/\r//g if $artist;
	$artist ||= "";
	my $year = $Conf->{year} || $cd{'year'} || 0;
	$year =~ s/\r//g if $year;
	my $cdtitle = $cd{'title'} || "";
	$cdtitle =~ s/\r//g if $cdtitle;

	my ($trackno, $ret) = (0, []);
	for (@{$cd{'track'}}){
		my $trackname =  $cd{'track'}->[$trackno] || "";
		$trackname =~ s/\r//g if $trackname;
		push @$ret, join ("|", ++$trackno, $artist, $cdtitle, $year, $trackname);
	}
	return $ret;
}

sub read_rip_info {
	my $ret;
	local $/=undef;
	open(RIP, "<" . $Conf->{rip_file}) || do {
		warn "W: Can't open $Conf->{rip_file}: $!\n";
		return undef;
	};
	@$ret = split /\n/, <RIP>;
	close RIP;
	return $ret;
}

sub display_rip_info($) {
	print(join("\n", @{(shift)}));
}

sub write_rip_info {
	my $info = shift;
	my $file = $Conf->{rip_file};
	open(RIP, ">$file") || die "E: can't write $file: $!\n";
	foreach (@$info) { print RIP $_, "\n"; }
	close RIP;
}

sub title_to_filename($) {
	my $title = unac_string $Conf->{charset}, $_[0];
	$title =~ tr/[A-Z]/[a-z]/;
	$title =~ s//ss/g;
	$title =~ s/[^a-z0-9]+/_/g;
	$title =~ s/^_+//;
	$title =~ s/_+$//;

	return $title;
}

sub parse_info($) {
	my $info = shift;
	my $ret;
	my @line;
	my $l = 1;
	foreach (@$info) {
		@line = split /\|/;
		my $info = {
			TRACK_NUMBER => int($line[0] || $l),
			ARTIST => $line[1],
			DISC_TITLE => $line[2],
			TRACK_YEAR => $line[3],
			TRACK_TITLE => $line[4],
			TRACK_FILE => title_to_filename $line[4],
			ORIGINAL_TRACK_FILE => $#line > 4 ? $line[5] : undef,
		};
		$l++;
		push @$ret, $info;
	}
	@$ret = sort {$a->{TRACK_NUMBER} <=> $b->{TRACK_NUMBER}} @$ret;
	return $ret;
}

sub expand_cmd($$$) {
	my $cmd = shift;
	my $info_line = shift;
	my $track = sprintf "%02d", shift;
	my $str;
	my $performer = $Conf->{disc_performer} || $info_line->{ARTIST};
	
	$str = $info_line->{TRACK_TITLE};
	$str =~ s/(\")/\\$1/go;
	$cmd =~ s/<title>/$str/go;
	$str = title_to_filename $str;
	$cmd =~ s/<TITLE>/$str/go;

	$str = $info_line->{ARTIST};
	$str =~ s/(\")/\\$1/go;
	$cmd =~ s/<artist>/$str/go;
	$str = title_to_filename $str;
	$cmd =~ s/<ARTIST>/$str/go;

	$str = $info_line->{TRACK_YEAR};
	$str =~ s/(\")/\\$1/go;
	$cmd =~ s/<date>/$str/go;
	$str = title_to_filename $str;
	$cmd =~ s/<DATE>/$str/go;

	$str = $info_line->{DISC_TITLE};
	$str =~ s/(\")/\\$1/go;
	$cmd =~ s/<album>/$str/go;
	$str = title_to_filename $str;
	$cmd =~ s/<ALBUM>/$str/go;

	$performer =~ s/(\")/\\$1/go;
	$cmd =~ s/<performer>/$performer/go;
	$performer = title_to_filename $performer;
	$cmd =~ s/<PERFORMER>/$str/go;

	if (defined $info_line->{ORIGINAL_TRACK_FILE}) {
		$str = $info_line->{ORIGINAL_TRACK_FILE};
		$str =~ s/(\")/\\$1/go;
		$cmd =~ s/<file_orig>/$str/go;
		$str =~ m/^(.*)\.([^\.]+)/o;
		$str = lc $2;
		$cmd =~ s/<ext>/$str/go;
	}

	my $prefix = $Conf->{prefix} || ".";
	$cmd =~ s/<prefix>/$prefix/go;
	$cmd =~ s/<tracknumber>/$track/go;
	$cmd =~ s/<file>/$info_line->{TRACK_FILE}/go;
	return $cmd;
}

sub exec_cmd($$) {
	my $info = shift;
	my $cmd = shift;
	my $cmd_exp;
	my $tracks = defined $Conf->{tracks} ? $Conf->{tracks} : [1 .. scalar @$info];
	foreach my $t (@$tracks) {
		$cmd_exp = expand_cmd($cmd, $info->[$t-1], $t);
		debug $cmd_exp;
		qx/$cmd_exp/ if !defined $Conf->{dummy};
	}
}

sub exec_cmd_once($) {
	my $cmd = shift;
	debug $cmd;
	print Dumper $cmd;
	qx/$cmd/ if !defined $Conf->{dummy};
}

sub write_cdda_info($) {
	my $info = shift;
	open(CDDA, ">" . $Conf->{cdda_file}) ||
		die "E: Can't open $Conf->{cdda_file}: $!\n";
	# info->[0] is used for artist only
	print CDDA expand_cmd($Conf->{cdda}->{header}, $info->[0], undef), "\n\n";
	my $tracks = defined $Conf->{tracks} ? $Conf->{tracks} : [1 .. scalar @$info];
	foreach my $t (@$tracks) {
		print CDDA expand_cmd($Conf->{cdda}->{track}, $info->[$t-1], $t), "\n\n";
	}
	close CDDA;
}

sub daemon() {
	debug "Opening $Conf->{pipe_file}";
	open(P, "< $Conf->{pipe_file}") ||
		die "Could not open $Conf->{pipe_file}: $!\n";
	my $file;
	my $info;
	while(1) {
		$file = <P>;
		next if !defined $file;
		chomp $file;
		last if $file eq "quit";
		($Conf->{path} = $file) =~ s/^(.*\/)?(:?[^\/]+)$/$1/;
		change_dir;
		if(! -f $Conf->{rip_file}) {
			warning "Can't open $file: $!";
			next;
		}
		$info = parse_info read_rip_info;
		foreach(keys %{$Conf->{exec}}) {
			next if ! defined $Conf->{exec}->{$_};
			s/-/_/go;
			debug $_;
			exec_cmd $info, $Conf->{commands}->{$_}; 
		}
		sleep $Conf->{pipe_wait};
	}
	close P;
	exit 0;
}

sub main {
	my $conf_file;
	map { $conf_file = $_ if -f $_ }
		("/etc/cwcdr.conf", "cwcdr.conf", "$ENV{HOME}/.cwcdr");

	die "No config file found\n" if ! defined $conf_file;
	debug "Using $conf_file";

	read_config($conf_file);
	get_options;
	print_help if defined $Conf->{help};

	daemon if defined $Conf->{daemon};

#	print Dumper $Conf;exit;
	foreach(keys %{$Conf->{one_run_exec}}) {
		next if ! defined $Conf->{one_run_exec}->{$_};
		s/-/_/go;
		debug $_;
		exec_cmd_once $Conf->{one_run_commands}->{$_}; 
	}

	my $info = defined $Conf->{read_rip_from_file} ?
		read_rip_info : fetch_rip_info;
	$Conf->{path} = defined $Conf->{disc_performer} ?
		title_to_filename $Conf->{disc_performer} : ".";
	$info->[0] =~ m/^[^|]+\|([^|]+)|.*$/;
	$Conf->{path} .= "/" . title_to_filename $1;

	make_dirs if defined $Conf->{make_dirs};
	change_dir if defined $Conf->{change_dir};
	
	write_rip_info $info if defined $Conf->{write_rip_info};
	display_rip_info $info if defined $Conf->{display_rip_info};
	$info = parse_info $info;
	write_cdda_info $info if defined $Conf->{write_cdda_info};
	foreach(keys %{$Conf->{exec}}) {
		next if ! defined $Conf->{exec}->{$_};
		s/-/_/go;
		debug $_;
		exec_cmd $info, $Conf->{commands}->{$_}; 
	}

#	print Dumper $Conf;
#	print Dumper fetch_rip_info;
#	print_help if defined $Conf->{help};
}

main;

# vim:ts=2:
