#!/usr/bin/perl -w

use strict;
use ConfigFile "read_config_file";
use CDDB_get qw(get_cddb);
#use Data::Dumper;
use Getopt::Long qw/:config no_ignore_case/;
use Text::Unaccent;

my $Conf;

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

sub title_to_filename($) {
    my $title = unac_string $Conf->{charset}, $_[0];
    $title =~ tr/[A-Z]/[a-z]/;
    $title =~ s/[\*\#\s.,;\?\/\'\"\`!&:()_-]+/_/g;
    $title =~ s/^_+//;
    $title =~ s/_+$//;

    return $title;
}

sub fetch_rip_info {

    my %cd = get_cddb({CDDB_HOST => "freedb.freedb.org",
# FIXME?               CDDB_PORT => "8080",
                       CDDB_MODE => "cddb",
                       CD_DEVICE => $Conf->{'device'} || "/dev/cdrom",
                       PROTO_VERSION => 5,
                       input     => 0});
                      
    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} ||
	die "E: Can't open $Conf->{rip_file}: $!\n";
    @$ret = split /\n/, <RIP>;
    close RIP;
    return $ret;
}

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

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 parse_info($) {
    my $info = shift;
    
    my $ret;
    my @line;
    foreach (@$info) {
	@line = split /\|/;
        my $info = {
	    TRACK_NUMBER => $line[0],
	    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,
	};
	push @$ret, $info;
    }
    return $ret;
}

sub gen_rip_file {
    my $cmd = $Conf->{rip_file_cmd} . ">" . $Conf->{rip_file};
    debug $cmd;
    qx/$cmd/ if !defined $Conf->{dummy};
}

sub write_m3u($) {
    my $info = shift;
    my $m3u = title_to_filename $info->[0]->{DISC_TITLE};
    open M3U, "> $m3u.m3u" || die "E: Can't write $m3u: $!\n";
    print M3U map {
	$_->{TRACK_FILE}, ".", $Conf->{file_extension}, "\n"
	} (@$info);
    close M3U;
}

sub write_cdda_info($) {
    my $info = shift;
    my $performer = $Conf->{disc_performer} || "";
    open CDDA, ">" . $Conf->{cdda_file} ||
	die "E: Can't open $Conf->{cdda_file}: $!\n";
    print CDDA <<EOF;
CD_DA
CD_TEXT {
    LANGUAGE_MAP {
	0 : EN
	}
    LANGUAGE 0 {
	TITLE "$info->[0]{DISC_TITLE}"
	PERFORMER "$performer"
	}
}

EOF
    my $t;
    foreach (@$info) {
	($t = $_->{TRACK_TITLE}) =~ s/([\"])/\\$1/g;
	print CDDA <<"EOF";
TRACK AUDIO
CD_TEXT {
    LANGUAGE 0 {
	TITLE "$t"
	PERFORMER "$_->{ARTIST}"
    }
}
FILE "$_->{TRACK_FILE}.wav" 0

EOF
;
    }
    close CDDA;
}

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 read_conf {
    return read_config_file shift;
}

sub expand_cmd($$$) {
    my $cmd = shift;
    my $info_line = shift;
    my $track = shift;
    my $str;
    
    $str = $info_line->{TRACK_TITLE};
    $str =~ s/(\")/\\$1/go;
    $cmd =~ s/<title>/$str/go;

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

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

    $str = $info_line->{DISC_TITLE};
    $str =~ s/(\")/\\$1/go;
    $cmd =~ s/<album>/$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 $opt = shift;

    my $cmd;
    my $track = 0;
    foreach (@$info) {
	$cmd = expand_cmd($Conf->{"$opt" . "_cmd"}, $_,
			  ++$track);
	debug $cmd;
	qx/$cmd/ if !defined $Conf->{dummy};
    }
}

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

sub print_help {
    my $err_code = shift || 0;
    print <<"EOF";
cd-ripper [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)
    --cddb-artist	 -a		Use CDDB artist
    --prefix		 -p  <prefix>	Move file to <prefix>
    --file-extension	 -e  <ext>	Use <ext> as extension when
					genrating M3U file
    --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-m3u		 -w		Write m3u playlist file
    --write-rip-info	 -W		Save the rip info file
    --make-dirs		 -m		Make directories
    --change-dir	 -g		Change to directory
    --help		 -h		Display this help

User defined actions:
EOF
    foreach (keys %$Conf) {
	next if ! m/_[cC][mM][dD]$/o;
	s/_/-/go;
	s/-[cC][mM][dD]$//o;
	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},
		   "file-extension|e=s" => \$Conf->{file_extension},
		   "write-m3u|w" => \$Conf->{write_m3u},
		   "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},
		   "cddb-artist|a" => \$Conf->{cddb_artist},
		   "prefix|p=s" => \$Conf->{prefix},
		   );
    my $user_opt;
    foreach (keys %$Conf) {
	next if ! m/_[cC][mM][dD]$/o;
	s/_[cC][mM][dD]$//o;
	$user_opt = $_;
	s/_/-/go;
	$options{$_} = \$Conf->{$user_opt};
    }
    GetOptions(%options) || print_help 1;
}

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";
    
    $Conf=read_conf $conf_file;
    get_options;
#    print Dumper $Conf;
    
    print_help if defined $Conf->{help};
    
    my $info =  defined $Conf->{read_rip_from_file} ?
	read_rip_info : fetch_rip_info;
    
#    exec_cmd parse_info $info, "ogg-tag";
#    exit;
    
    
    $Conf->{path} = defined $Conf->{disc_performer} ?
	title_to_filename $Conf->{disc_performer} : ".";
    $info->[0] =~ m/^[^|]+\|([^|]+)|.*$/;
    $Conf->{path} .= "/" . title_to_filename $1;
    
#    move_to_cleaner_file parse_info $info if
#	defined $Conf->{clean_filename};
    
    gen_rip_file if defined $Conf->{gen_rip_file};
    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};
    write_m3u $info if defined $Conf->{write_m3u};
    
    foreach (split /\s+/, $Conf->{order}) {
	next if ! defined $Conf->{$_};
	exec_cmd $info, $_ if defined $Conf->{$_ . "_cmd"};
	exec_cmd_once $_ if defined $Conf->{$_ . "_CMD"};
    }
    exit 0;
}

main;
