#!/usr/bin/perl -w
#
# Version 0.2
# Patrick Hearon <patrickh@rice.edu>
#
use Getopt::Long;

# First, read settings; precedence is defaults < rcfile < args
$home = $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($<))[7] || print "No home directory found.\n";

# Establish default settings
$ROOTDIR="$home/music";
$LISTSUFFIX="m3u";
$FINDSUFFIX="mp3";
$MASTERLIST="master";
$INDEXFILE="index.html";

# Read the rcfile if it exists and is readable
if ( -f "$home/.playlistrc" && -r "$home/.playlistrc") {
    open(RCFILE, "<$home/.playlistrc") or warn "Your .playlistrc could not be opened: $!.\n";
    while(<RCFILE>) {
	chomp;
	$_ =~ s[#.*][];
	next unless($_ =~ /\w+\s*=\s*\w+/); # Skip lines that aren't of the form option=value
	($option, $value) = split(/\s*=\s*/, $_);
	${$option} = "$value";
    }
    close RCFILE;
}

# Examine command-line arguments
# Allowed arguments:
# --root, --rootdir     path to starting directory
# --ls,   --listsuffix  filename extension for lists (without preceding ".")
# --fs,   --findsuffix  filename extension to list (without preceding ".")
# --ml,   --masterlist  name of master list (relative to rootdir)
# --idx,  --indexfile   name of html version of list for each directory
&GetOptions("rootdir|root:s"  => \$ROOTDIR);
&GetOptions("listsuffix|ls:s" => \$LISTSUFFIX);
&GetOptions("findsuffix|fs:s" => \$FINDSUFFIX);
&GetOptions("masterlist|ml:s" => \$MASTERLIST);
&GetOptions("indexfile|idx:s" => \$INDEXFILE);

# Create empty master list file in rootdir
open(MASTERLIST, ">$ROOTDIR/$MASTERLIST.$LISTSUFFIX") or die "Can't create new master list $MASTERLIST.$LISTSUFFIX: $!\n";
close MASTERLIST;
FileWalk($ROOTDIR, \&BuildList, \&CreateList, \&FinishList) or die "Oops. $!\n";

##########################################################
#   CreateList(cwd)
#   Creates an empty playlist and $INDEXFILE file in the
#   current directory.
##########################################################

sub CreateList
{
    my $cwd = $_[0];
    my $path = $_[0];
    $cwd =~ s[.*/][]; # Sets cwd to just the directory name, not the full monty
    my $currlist = "$cwd.$LISTSUFFIX";
    my $htmlfile = "$path/$INDEXFILE";
    open(CURRLIST, ">$path/$currlist") or die "Can't create new list $path/$currlist: $!\n";
    open(HTMLFILE, ">$path/$INDEXFILE") or die "Can't create new index file $path/$INDEXFILE: $!\n";
    htmlInit($htmlfile, $path, "Listing of $FINDSUFFIX files in $cwd");
    close CURRLIST;
    close HTMLFILE;
}

##########################################################
#   BuildList(cwd, path-to-$FINDSUFFIXfile)
#   Adds a $FINDSUFFIX to the output files
##########################################################

sub BuildList
{
    my $cwd = $_[0];
    my $path = $_[0];
    my $filename = $_[1];
    $cwd =~ s[.*/][];
    my $currlist = "$path/$cwd.$LISTSUFFIX";
    my $htmlfile = "$path/$INDEXFILE";
    my $rootfilename = "$path/$filename";
    $rootfilename =~ s[$ROOTDIR/][]o; # Gets the path relative to the rootdir
    open(CURRLIST, ">>$currlist") or die "Can't append to $currlist: $!\n";
    open(HTMLFILE, ">>$htmlfile") or die "Can't append to $htmlfile: $!\n";
    open(MASTERLIST, ">>$MASTERLIST") or die "Can't append to $MASTERLIST: $!\n";
    if(( -f $filename ) && ( $filename =~ /\.$FINDSUFFIX$/ )) {
	print CURRLIST "$filename\n";
	print MASTERLIST "$rootfilename\n";
	print HTMLFILE "<li><a href=\"$filename\">$filename</a></li>\n";
    }
    close CURRLIST;
    close HTMLFILE;
    close MASTERLIST;
}

##########################################################
#   FinishList(cwd)
#   Adds closing tags to the html listing
##########################################################

sub FinishList
{
    my $path = $_[0];
    my $htmlfile = "$path/$INDEXFILE";
    open(HTMLFILE, ">>$htmlfile") or die "Can't append to $htmlfile: $!\n";
    print HTMLFILE "</ul>\n";
    print HTMLFILE "<hr><br>\n";
    print HTMLFILE "</body></html>\n";
    close HTMLFILE;
}

##########################################################
#   htmlInit(path-to-htmlfile, cwd, title)
#   Assumes an empty file and sets up header stuff
#   plus adding links to child and parent directories
##########################################################

sub htmlInit
{
    my $file=$_[0];
    my $path=$_[1];
    my $title=$_[2];   
    open(INDEXFILE, ">>$file") or die "Can't append to index file $file: $!\n";
    print INDEXFILE "<html><head>\n";
    print INDEXFILE "<title>$title</title>\n";
    print INDEXFILE "<body bgcolor=#FFFFFF>\n";
    print INDEXFILE "<p><center>$title</center></p><br>\n";
    if (-f "../$INDEXFILE") {
	print INDEXFILE "<p><a href=\"../$INDEXFILE\">Previous directory</a><br><hr>\n";
    }
    print INDEXFILE "<ul>\n";
    opendir DIR, $path or return 0;
    my @dirlist = grep !/^\.{1,2}$/, grep ((-d $_), readdir DIR);
    closedir DIR;
    foreach $dir (sort @dirlist) {
	print INDEXFILE "<li><a href=\"$dir/$INDEXFILE\">$dir</a></li>\n";
    }
    print INDEXFILE "</ul><hr><ul>";
    close INDEXFILE;
}

##########################################################
#    TreeWalk(cwd, func)
#    Applies func to cwd recursively
#    where it's func($cwd)
##########################################################

sub TreeWalk
{
    my $curr = $_[0];
    my $sub = $_[1];
    return 0 if (!defined($curr));
    $curr =~ s!//!/!g;
    chdir $curr or return 0;
    opendir DIR, $curr or return 0;
    my @dirlist = grep !/^\.{1,2}$/, grep ((-d $_), readdir DIR);
    closedir DIR;
    &$sub($curr);
    foreach $dir (sort @dirlist) {
	&TreeWalk("$curr/$dir", $sub) or warn "Warning: $!\n";
	chdir $curr;
    }
    return 1;
}

##########################################################
#    FileWalk(cwd, func, setup, finish)
#    Applies func to every file in every subdirectory of cwd
#    where it's func($cwd, $file);
#    Setup is run once per dir, whereas func is used once
#    per file; it's setup($cwd).
#    Likewise for finish($cwd).
##########################################################

sub FileWalk
{
    (my $cwd, local $sub, local $setup, local $finish) = ($_[0], $_[1], $_[2], $_[3]);
    sub _FileWalkHelper {
	my $cwd = $_[0];
	opendir _FILEWALKDIR, $cwd or warn "Can't open cwd: $!\n";
	@files = readdir _FILEWALKDIR;
	closedir _FILEWALKDIR;
	&$setup($cwd);
	foreach $file (sort @files) {
	    -f $file and -r $file and &$sub($cwd, $file);
	}
	&$finish($cwd);
    }
    return &TreeWalk($cwd, \&_FileWalkHelper);
}


















