#!/usr/bin/perl
# GoldenPod version 0.6
# Copyright (C) Eskild Hustvedt 2005, 2006
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

# The modules we want to use
use strict;				# Make my coding strict
use warnings;				# Warn me!
use Fatal qw/ open chdir mkdir /;	# So I don't have to type "or die" too much :)
use File::Basename;			# Needed to find out our directory and name
use Cwd;				# Needed for getcwd
use Getopt::Long;			# Commandline parsing
use File::Copy;				# We need to copy files!
# Allow bundling of options with GeteOpt
Getopt::Long::Configure ("bundling", 'prefix_pattern=(--|-)');

# Declare initial variables
my (
    $PreviousDownloads,   $Config,         $Verbose,
    $NoDownload,          $FirstOnly,      $CreatedDir,
    $DownloadedSomething, $NoLog,          $NeedToDownload,
    $NoCat,               $CopyFilesTo,    $FileNumber,
    $CopyFiles_Delete,    $ConfigDir,      $WorkingDir,
    $ProgramLog,          $PodcastLog,     $DefaultVerbosity,
    $UserConfigDir,       $ConfIsNotEmpty, $PodcastFilter,
    $IgnorePattern,       $RemoveOldFiles, $DryRun_Mode
);    # Scalars
my (%DownloadQueue, %URLs, %AlreadyDownloaded, %NoDownload, %PodNames); # Hashes

# Set some global values
my $CurlVerbosity = "--silent --show-error";	# Default is that curl is silent but displays errors (logged, not STDOUT)
my $Version = "0.6";			# My version
my $CurlGlobal = "-C -k -L -A 'GoldenPod $Version - http://goldenpod.nongnu.org/'";# Global curl options
# Curl options:
# -C to continue when possible
# -k for insecure (allow ssl servers with odd certificates)
# -L to follow location hints
# -A sets the user agent string

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Help function declerations
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# The function that actually outputs the help
# This is just because I'm too lazy to type the printf every time
# and this function makes it more practical.
# Syntax is simply: &PrintHelp("shortoption", "longoption", "description")
sub PrintHelp {
	printf "%-4s %-16s %s\n", "$_[0]", "$_[1]", "$_[2]";
}
sub Help {
	my $Command = basename($0);
	$PodcastLog =~ s/$ENV{HOME}/~/;
	&Version;
	print "\nUsage: $Command";
	print "\n  or : $Command [OPTIONS]\n\n";
	&PrintHelp("", "--version", "Display version information and exit");
	&PrintHelp("-h", "--help", "This help screen");
	# Inform the user about the default based upon the value of $DefaultVerbosity
	if ($DefaultVerbosity) {
		&PrintHelp("-v", "--verbose", "Be verbose (default)");
		&PrintHelp("-s", "--silent", "Be silent");
	} else {
		&PrintHelp("-v", "--verbose", "Be verbose");
		&PrintHelp("-s", "--silent", "Be silent (default)");
	}
	&PrintHelp("", "--dumpinfo", "Print the files goldenpod would work with");
	&PrintHelp("", "--stats", "Print some simple statistics");
	&PrintHelp("-l", "--nolog", "Don't create a message logfile when in non-verbose mode.");
	&PrintHelp("-u", "--dry-run", "Display what would be done but don't do it. Implies --verbose");
	&PrintHelp("-w", "--no-download", "Act as if all pending podcasts are downloaded");
	&PrintHelp("", "", "but don't actually download anything. Implies --verbose");
	&PrintHelp("-f", "--first-only", "Only download the first file in any feed.");
	&PrintHelp("", "", "Permanently ignore the others.");
	&PrintHelp("-c", "--copy [path]", "Copy the last N downloaded files to path.");
	&PrintHelp("", "", "N is either 4 or the number supplied to --files.");
	&PrintHelp("-n", "--files N", "Copy N files instead of 4 (use with --copy or --rmold)");
	&PrintHelp("-d", "--delete", "Delete all other files in the target --copy");
	&PrintHelp("", "", "directory");
	&PrintHelp("-o", "--rmold", "Delete N old podcasts where N is 4 or the number");
	&PrintHelp("", "", "supplied to --files");
	&PrintHelp("-i", "--ignore-pattern", "Ignore filenames matching the regexp pattern");
	&PrintHelp("", "", "supplied when downloading, copying or deleting podcasts");
}

sub Version { print "GoldenPod $Version\n by Eskild Hustvedt\nGoldenPod comes with ABSOLUTELY NO WARRANTY!\n"; };

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Declerations for helper routines
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Routine that checks wether a specific command is in PATH
sub InPath {
        foreach (split /:/, $ENV{PATH}) {
                if (-x "$_/@_") {
                        return 1;
                }
        }
        return 0;
}

# Routine that checks if a directory is empty
sub DirIsEmpty {
        opendir(TESTDIR, $_[0]);
        my @TestDir = readdir(TESTDIR);
        closedir(TESTDIR);
        unless (scalar @TestDir > 2) {
                return 1;
        }
        return 0;
}

# Routine that makes changes to URLs so that they are safe
# (fix for GPSA-072006:1)
sub QuoteURL ($) {
	my $URL = $_[0];
	$URL =~ s/'/\\'/;
	return("'$URL'");
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# NoDownload routine
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# NoDownload routine - Add all items in %NoDownload to the logfile
sub PerformNoDownload {
	print "\nWriting older podcasts to the logfile..." if $Verbose and !$FirstOnly;
	open(LOGFILE, ">>$PodcastLog");
	foreach (keys (%NoDownload)) {
		print LOGFILE "$_\n" unless $AlreadyDownloaded{$_};
	}
	print " done\n" if $Verbose and !$FirstOnly;
	close(LOGFILE);
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Helper routines for --copy and --rmold
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Routine that gives the value supplied to --files or 4
sub HowManyFiles {
	my $FileCount;
	$FileCount = $FileNumber if $FileNumber;
	$FileCount = "4" unless $FileCount;
	# Unless it's an integer we can't continue
	if ($FileCount =~ /\D/) {
		die "Error: The option passed to --files ($FileCount) is not an integer number.\n";
	}
	return $FileCount;
}

# Routine that finds and sorts files in the current directory and returns
# an array containing them
sub SortedFileList {
	my (@SortedFileList, %FileCopyList);
        # Create a hash of possible filenames
        foreach my $FileName (<*>) {
		# Is it a link? If it isn't then we don't bother testing it
		next unless -l $FileName;
                # If the link points to something that doesn't exist them we omit it.
                if (-e (readlink $FileName)) {
			# We don't care about directories
			unless (-d $FileName) {
	                        $FileCopyList{readlink($FileName)} = 1;
			}
                }
        }
        # Create a sorted array of filenames
        # map { [ $_, -M $_||0 ] } keys(%FileCopyList); = that means make a two dimensional array so that $_->[0]="filename.txt" and $_->[1] is the -M value
        # sort { $a->[1] <=> $b->[1] } = sort the array we just thought of by the $_->[1] index, ie the -M times
        # map { $_->[0] } take the sorted array and convert it back to a plain list of filenames by reading out the $_->[0] values.  These are now in the right order as they are sorted
        @SortedFileList = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, -M $_ ] } keys(%FileCopyList);
	return @SortedFileList;
}

# Routine that verifies links in directories and deletes those that are pointing to nowhere.
# This routine expects to be run from the catalogue/All directory and will return to that once done.
sub CleanCatalogue {
	print "Cleaning up the catalogue...";
	chdir "..";
	foreach my $CurrentDirectory (<*>) {
		foreach my $CurrentFile (<$CurrentDirectory/*>) {
			next unless -l $CurrentFile;
			unless (-e readlink($CurrentFile)) {
				unlink("$CurrentFile");
			}
		}
	}
	chdir "All";
	print "done\n";
}

# Routine that removes empty directories
# DirIsEmpty
sub RemoveEmptyDirs {
	foreach my $Directory (<*>) {
		next unless -d $Directory;
		rmdir($Directory) if DirIsEmpty($Directory);
	}
}

# Routine that rewrites the playlists, expects to be run from the catalogue base dir.
# Call this routine before you delete directories to remove old playlists that are no
# longer used.
sub RewritePlaylists {
	print "Rewriting playlists...";
	foreach (<*>) {
		# Skip the All directory
		next if $_ eq "All";
		# This variable will be 1 if we wrote something to the playlist
		my $WrotePlaylistContent = 0;
		# Change to the directory
		chdir("$_");
		# Remove the old playlist if it exists
		unlink("$_.m3u") if -e "$_.m3u";
		# Skip directory if it's empty
		chdir("..") and next if DirIsEmpty(".");
		# Open our new playlist
		open(PLAYLIST, ">$_.m3u");
		# Create the playlist based upon the output of SortedFileList
		foreach my $CurrentFile (SortedFileList) {
			# Skip playlists
			next if $CurrentFile =~ /\.m3u$/;
			# Add to the playlist
			print PLAYLIST readlink($CurrentFile);
			$WrotePlaylistContent = 1;
		}
		close(PLAYLIST);
		unlink("$_.m3u") unless $WrotePlaylistContent;
		chdir "..";
	}
	print "done\n";
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Subroutines for --copy and --rmold
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Routine that deletes old podcasts
sub DeleteOldPodcasts {
	# Declare variables
	my (@FileList, %Files, @RemoveTheseFiles);
	my $NumberOfFiles = HowManyFiles;
	# Make sure ./catalogue/All exists and chdir to it
	die "The ./catalogue/All/ directory did not exist!\nAre you sure you have downloaded some podcasts?\n" unless -e "./catalogue/All/";
	chdir "./catalogue/All/";
	# Figure out which files are the oldest podcasts
	@FileList = reverse SortedFileList;
	my $PodcastCount = @FileList;
	# We don't allow the user to delete the last remaining podcast.
	die "You only have one podcast - if you really want to delete it you will have to do it manually" if $PodcastCount eq "1";
	# Make sure we don't delete more than the total amount of podcasts minus one
	if ($NumberOfFiles >= $PodcastCount) {
		die "You only have one podcast - if you really want to delete it you will have to do it manually" if $PodcastCount eq "1";
		warn "$NumberOfFiles is higher than the total amount of podcasts ($PodcastCount). Using ", $PodcastCount-1, " instead\n";
		$NumberOfFiles = $PodcastCount-1;
	}
	# Delete the files
	my $DeletedFiles = "0";
	while ($NumberOfFiles > $DeletedFiles) {
		my $TargetBase = basename($FileList[0]);
		unless ($IgnorePattern and $TargetBase =~ /$IgnorePattern/) {
			# If we're in dry run mode then we don't want to actually do anything.
			if ($DryRun_Mode) {
				print "Would delete $TargetBase\n";
			} else {
				print "Deleting $TargetBase\n";
				unlink("$FileList[0]");
			}
			$DeletedFiles++;
		}
		shift @FileList;
		last unless $FileList[0];
	}
	# Stop here if we're in dry run mode
	exit if $DryRun_Mode;
	# Remove dead links in the catalogue
	CleanCatalogue;
	# Rewrite playlists
	chdir "..";
	RewritePlaylists;
	# Remove empty directories
	print "Removing empty directories...";
	RemoveEmptyDirs;
	chdir "..";
	RemoveEmptyDirs;
	print "done\n";
	exit
}
# Copy routine.
sub CopyFiles {
	# Declare variables
	my (@FileList, %Files, @CopyTheseFiles, %DontDeleteThese);
	my $NumberOfFiles = HowManyFiles;
	# Do a few directory checks before moving on.
	die "The ./catalogue/All/ directory did not exist!\nAre you sure you have downloaded some podcasts?\n" unless -e "./catalogue/All/";
	die "$CopyFilesTo does not exist!\n" 			unless -e $CopyFilesTo;
	die "$CopyFilesTo is not a directory!\n"		unless -d $CopyFilesTo;
	die "I can't write to the directory $CopyFilesTo!\n"	unless -w $CopyFilesTo;
	
	# Figure out which files are the latest podcasts
	chdir "./catalogue/All/";
	# A sorted array of files
	@FileList = SortedFileList;
	# Create an array of the files we should copy
	my $CopiedFiles = 0;
	while (defined($FileList[$CopiedFiles]) and $CopiedFiles < $NumberOfFiles) {
		my $TargetBase = basename($FileList[$CopiedFiles]);
		# Check if we want to skip files matching a specific regexp
		unless ($IgnorePattern and $TargetBase =~ /$IgnorePattern/) {
			push(@CopyTheseFiles, $FileList[$CopiedFiles]);
			$DontDeleteThese{$TargetBase} = 1;
			$CopiedFiles++;
		} else {
			shift(@FileList);
		}
	}
	# Delete routine (delete files unless we would have copied it)
	if ($CopyFiles_Delete) {
		# Time to delete files in the target directory
		# Babysitting the user :)
		die "Not allowed to delete in the directory \"$CopyFilesTo\"\n" if $CopyFilesTo =~ m,^($ENV{HOME}(/|/Documents/*.*)*$|^/(usr|var|dev|etc|lib|sbin|sys|boot|proc)/*),;
		# Delete the files
		foreach (<$CopyFilesTo/*>) {
			my $TargetBase = basename($_);
			# This one is merely cosmetic
			$_ =~ s#//#/#g;
			# If it is in the $DontDeleteThis hash or is a directory we skip it.
			next if $DontDeleteThese{$TargetBase} or -d $_;
			# If we're in dry run mode we don't want to actually do anything
			if ($DryRun_Mode) {
				print "Would delete $_\n";
			} else {
				print "Deleting $_\n";
				unlink("$_") or warn "Deleting of $_ failed: $!\n";
			}
		}
	}
	# Copy the files
	foreach (@CopyTheseFiles) {
		my $TargetBase = basename($_);
		print "Skipping pre-existing \"$TargetBase\"\n" if $Verbose and -e "$CopyFilesTo/$TargetBase";
		next if -e "$CopyFilesTo/$TargetBase";
		# If we're in dry run mode we don't want to actually do anything
		if ($DryRun_Mode) {
			print "Would copy $TargetBase\n";
		} else {
			print "Copying $TargetBase...\n";
			copy("$_", "$CopyFilesTo") or die "Copying failed: $!\n";
		}
	}
	# And finally, attempt to run sync once.
	if (InPath "sync" and not $DryRun_Mode) {
		print "Synchronizing disks... " if $Verbose;
		system("sync");
	}
	print "All done\n" if $Verbose;
	exit
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Subroutines for writing and reading configuration files
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Function to initialize the global config file from $BaseDir
sub InitGlobalConfig {
	# Create the directory if it isn't already there
	&InitConfigDir unless -e $UserConfigDir;
	# Load the config
	my $ConfigReturn = do { 
		$Settings::WorkingDir = undef;
		$Settings::DefaultVerbosity = undef;
		$Settings::PodcastFilter = undef;
		$Settings::IgnorePattern = undef;
		package Settings;
		do "$UserConfigDir/goldenpod.conf"
	};
	# Check errors
	unless (defined $ConfigReturn) {
		my $Error;
		$Error = $@ ? $@ : $!;
		die "Failed to parse the configuration file $UserConfigDir/goldenpod.conf: $Error\n";
	}
	# Set config variables (\$IgnorePattern is set later)
	$WorkingDir = $Settings::WorkingDir;
	$DefaultVerbosity = $Settings::DefaultVerbosity;
	$PodcastFilter = $Settings::PodcastFilter;
}

# Function to create ~/.goldenpod (or any other $BaseDir)
sub InitConfigDir {
	mkdir $UserConfigDir or die "Unable to create the directory $UserConfigDir: $!" unless -e $UserConfigDir;
	# If /etc/goldenpod-podcasts.conf exists the copy that to ~/.goldenpod/podcasts.conf
	if ( -e "/etc/goldenpod-podcasts.conf" ) {
		warn "Copying /etc/goldenpod-podcasts.conf to $UserConfigDir/podcasts.conf";
		copy("/etc/goldenpod-podcasts.conf", "$UserConfigDir/podcasts.conf") or warn "Copying of /etc/goldenpod-podcasts.conf failed: $!";
	}
	# If we don't have ~/.goldenpod/podcasts.conf (no /etc/goldenpod.conf or failure copying it)
	# then write an empty one.
	unless ( -e "$UserConfigDir/podcasts.conf" ) {
		open(PODCASTS_CONF, ">$UserConfigDir/podcasts.conf");
		print PODCASTS_CONF "# Put your podcast feed URLs in this file seperated by newlines\n# All lines starting with # are ignored";
		close(PODCASTS_CONF);
	}
	# Write the default configuration file
	open(PRIMARY_CONFIG, ">$UserConfigDir/goldenpod.conf");
	print PRIMARY_CONFIG "# GoldenPod configuration file\n# Do NOT put podcast feed URLs in this file! Put them in podcasts.conf\n\n# The directory the podcasts will be downloaded to\n";
	print PRIMARY_CONFIG "# This is only used if there is no ./podcasts.conf or ./bp.conf\n# If one those are present WorkingDir will be ./";
	print PRIMARY_CONFIG "\n\$WorkingDir = \"$ENV{HOME}/Podcasts\";\n\n# The default verbosity, anything other than 0 means verbose\n\$DefaultVerbosity = \"1\";\n";
	print PRIMARY_CONFIG "\n# Set this to a regular expression you want goldenpod to ignore when downloading\n# or copying podcasts. --ignore-pattern overrides this and --rmold only obeys --ignore-pattern, not this configuration setting.\n\$IgnorePattern = \"\";\n";
	print PRIMARY_CONFIG "\n# Set this to 1 if you want GoldenPod only to download audio feeds\n\$PodcastFilter = \"0\";";
	close(PRIMARY_CONFIG);
}

# Function to detect which mode we are in
sub ModeDetection {
	my ($Mode, $LocalDir);
	# First check if we can use files in ./
	if (-e "./bp.conf" or -e "./podcasts.conf") {
		$Mode = "local";
		$LocalDir = Cwd::getcwd;
	}

	# If we can't use files in ./ let's check something else
	unless ($Mode) {
		# Check for files in the directory containing the goldenpod executeable (if that dir is -w)
		my $BaseDirectory = dirname($0);
		if (-w $BaseDirectory) {
			if (-e "$BaseDirectory/bp.conf" or -e "$BaseDirectory/podcasts.conf") {
				$Mode = "local";
				$LocalDir = "$BaseDirectory";
			}
		}
		# If all else fails (not that it's a bad thing really) we use ~/.goldenpod
		$Mode = "global" unless $Mode;
	} else {
		# If we're in local mode because PWD is ~/.goldenpod then work in global
		# mode instead.
		$Mode = "global" if $LocalDir eq "$ENV{HOME}/.goldenpod";
	}

	# Initialize config
	$UserConfigDir = "$ENV{HOME}/.goldenpod";
	&InitGlobalConfig;

	# If we're working in local mode, set some specific settings.
	if ($Mode eq "local") {
		$ConfigDir = $LocalDir;
		$WorkingDir = $LocalDir;
	} else {
		$ConfigDir = $UserConfigDir;
	}

	# Do base initialization
	$ProgramLog = "$ConfigDir/goldenpod.log";	# When we are silent we log to this file
	# Use podcasts.log if it exists, use podcast.log if podcasts.log doesn't exist
	# but podcast.log doesn't, use podcasts.log if neither exists.
	if (-e "$ConfigDir/podcasts.log" or !-e "$ConfigDir/podcast.log") {
		$PodcastLog = "$ConfigDir/podcasts.log";
	} else {
		$PodcastLog = "$ConfigDir/podcast.log";
	}
	# Detect config file.
	if (-e "$ConfigDir/podcasts.conf" or !-e "$ConfigDir/bp.conf") {
		$Config = "$ConfigDir/podcasts.conf";
	} else {
		$Config = "$ConfigDir/bp.conf"
	}
	# Be verbose by default if the user wants to
	&Arg_Verbose if $DefaultVerbosity;
	mkdir $WorkingDir unless -e $WorkingDir;
	chdir $WorkingDir;
	die "I cannot write to $WorkingDir!\n" unless -w $WorkingDir;
}

# Detect our mode
ModeDetection();

# Argument functions
sub Arg_Verbose { $Verbose = "1"; $NoLog = "1" unless $NoLog; $CurlVerbosity = "-#";}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Initialize program
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Parse commandline arguments
GetOptions ( 'verbose|v' => \&Arg_Verbose,
	'version' => sub { &Version; exit 0},
	'help|h' => sub { &Help; exit 0; },
	'no-download|nodownload|w' => sub { &Arg_Verbose; $NoDownload = "1" },
	'dry-run|dryrun|u' => sub { &Arg_Verbose; $DryRun_Mode = "1"},
	'first-only|firstonly|first|f' => \$FirstOnly,
	'nolog|l' => sub { $NoLog = "2"},
	'copy|c=s'=> \$CopyFilesTo,
	'files|n=s' => \$FileNumber,
	'delete|d' => \$CopyFiles_Delete,
	'rmold|o' => \$RemoveOldFiles,
	'silent|s' => sub { $Verbose = 0; $NoLog = "0" unless $NoLog == "2"; $CurlVerbosity = "--silent --show-error"; },
	'ignore-pattern|ignorepattern|i=s' => \$IgnorePattern,
	# Display some simple statistics
	'stats' => sub { die "The \"catalogue/All\" directory did not exist, are you sure you have downloaded anything?\n" unless -e "./catalogue/All";
		chdir "./catalogue/All/";
		my @PodcastFileList = SortedFileList;
		print "\nYou have ", scalar @PodcastFileList, " files\n";
		print "The newest file is: ", basename($PodcastFileList[0]),"\n";
		print "The oldest file is: ", basename($PodcastFileList[-1]),"\n";
		# Find filesizes
		my $USED_DISKSPACE = 0;
		foreach (<*>) {
			# Skip the file if -s doesn't return anything useful.
			if (-s $_) {
	        		$USED_DISKSPACE = $USED_DISKSPACE+-s $_;
			}
		}
		$USED_DISKSPACE = $USED_DISKSPACE/1024/1024;
		print "The files are using ", sprintf ("%.0f", "$USED_DISKSPACE"), " MB of diskspace\n";
		exit 0 },
	# Display information about which config files and settings we would use
	'dumpinfo' => sub { &Version;
		print "\nI would read the configuration file $UserConfigDir/goldenpod.conf\n";
		print "I would read podcasts from $Config\n";
		print "I would log the program messages to $ProgramLog\n" unless $Verbose or $NoLog;
		print "I would log the downloaded podcasts to $PodcastLog\n";
		print "I would write to $WorkingDir\n";
		exit 0 }
) or die "Run ", basename($0), " --help for help\n";

# print \n if we're in verbose mode
print "\n" if $Verbose;

# Conflicting commandline arguments
die "Conflicting options: --first-only and --no-download. Please read --help\n" if $FirstOnly and $NoDownload;
die "Conflicting options: --copy and --rmold. You can't use both at the same time.\n" if $CopyFilesTo and $RemoveOldFiles;
# Useless usage of some options
warn "Useless use of --delete without --copy\n" if $CopyFiles_Delete and not $CopyFilesTo;
warn "Useless use of --files without --copy or --rmold\n" if $FileNumber and not $CopyFilesTo and not $RemoveOldFiles;
warn "Useless use of --nolog in verbose mode\n" if $Verbose and $NoLog == "2";

# Make sure $IgnorePattern is correct and set it to $Settings::IgnorePattern
if ($IgnorePattern) {
	eval { qr/$IgnorePattern/ } or die "The regexp supplied to --ignore-pattern is invalid.\n";
}

# We test for $RemoveOldfiles here because it should never use the $IgnorePattern from the config file
DeleteOldPodcasts if $RemoveOldFiles;

# if --ignore-pattern was not supplied but $IgnorePattern is set in the config file.
unless ($IgnorePattern) {
	if ($Settings::IgnorePattern) {
		eval { qr/$Settings::IgnorePattern/ } or die "The regexp \$IgnorePattern in the configuration file is invalid!\n";
		$IgnorePattern = $Settings::IgnorePattern;
	}
}

# If we're copying then run the &CopyMyFiles subroutine
CopyFiles if $CopyFilesTo;
# Make sure we have curl
die "GoldenPod requires curl to work, you don't seem to have curl installed!\nPlease install curl, then re-run GoldenPod\n" unless (InPath "curl");
# Add CurlVerbosity to CurlGlobal
$CurlGlobal = "$CurlGlobal $CurlVerbosity";

die "The configuration file \"$Config\" does not exist!\nPlease read the manpage included to get instructions on how to set one up.\n" unless -e $Config;

# Unless we're verbose, write stuff to $ProgramLog
open(STDOUT, ">>$ProgramLog") unless $NoLog;
open(STDERR, ">>$ProgramLog") unless $NoLog;

# If we're in logging mode then log the date and time started
print "Started at " . localtime(time) unless $NoLog;
# If we're not verbose and $NoLog is 2 (-l) then write stuff to /dev/null ;)
open(STDOUT, ">/dev/null") if $NoLog and $NoLog == "2";
open(STDERR, ">/dev/null") if $NoLog and $NoLog == "2";

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Download and parse the feeds
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Open the configuration file
open(CONFIG, "$Config");

# Read the configuration file and fetch feeds
foreach my $podcast (<CONFIG>) {
	chomp $podcast;
	next if $podcast =~ /^\s*#/;	# Ignore lines starting with # (comments)
	next if $podcast =~ /^\s*$/;	# Ignore empty lines
	my $PodcastTitle;
	print "Fetching $podcast\n" if $Verbose;
	$ConfIsNotEmpty = 1;	# The config isn't empty! Yay!
	# Open a filehandle to curl (curl outputs to stdout)
	$podcast = QuoteURL($podcast);
	open(MYRSS, "curl $CurlGlobal ".$podcast." |");
	my $FirstDone = "0" if $FirstOnly;
	# Parse the feed
	while (<MYRSS>) {
		chomp $_;
		# Get the title of the podcast
		unless ($PodcastTitle) {
			if (/<title>([A-Za-z0-9 ]+).*<\/title>/) {
				$PodcastTitle = $1;
				# Run it through a filter
				$PodcastTitle =~ s/\s+/_/g;
				1 while $PodcastTitle =~ s/[\-\_]+(episodes?|promo|mp3|ogg|feed)*$//i;	 # Remove various junk
				$PodcastTitle =~ s/[\-\_].$//; # Remove -_ in the end of the name
			}
		}
		# This is a hack to make sure we parse RSS feeds without newlines correctly
		s/>/>\n/g;
		foreach (split(/\n/,$_)) {
			chomp $_;
			# Do the real parsing and add to @urls
			# We want to extract all url='s
			# 
			# XML specification allows spces between url, = and content, so we allow that here.
			if (/url\s*=\s*[\"\']([^\"\']+)[\"\']/)
	    		{
				# Filter away non-audio feeds if the user wants it.
				if ($PodcastFilter) {
					next unless basename($1) =~ /.*\.(ogg|mp3|m4a|wave?|flac|wmv|ape|tta|aac|mp2|mpa|ra|ram|aif|au|mpu).*/i;
				}
				# Filter away stuff in $IgnorePattern
				if ($IgnorePattern) {
					next if basename($1) =~ /$IgnorePattern/;
				}
				# If we're in --first-only then add it to @urls unless
				# $FirstDone is true, add it to @NoDownload if it is.
				# If we aren't, then just add it to @urls.
				if ($FirstOnly) {
					$URLs{$1} = 1 unless $FirstDone;
					$NoDownload{$1} = 1 if $FirstDone;
					$FirstDone = "1";
				} else { $URLs{$1} = 1; }
				# Add the title string to the PodNames array (if we have found the title)
				my $BaseName = basename($1);
				$PodNames{$BaseName} = $PodcastTitle;
			}
		}
	}
	close MYRSS;
}

# If the config is empty, die.
die "The podcast list in $Config is empty!\n" unless $ConfIsNotEmpty;

# Close the configuration file
close CONFIG;

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Find out if we need to download anything
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Create the %AlreadyDownloaded hash.
if ( -e $PodcastLog) {
	# Open the logfile containing previously downloaded files
	open(LOGFILE, "$PodcastLog");
	# Add them to a hash
	%AlreadyDownloaded = map { chomp; $_=>1 } <LOGFILE>;
	# Close the logfile
	close LOGFILE;
}


# If we're in a dry run then copy the contents of the URLs hash to
# NoDownload.
%NoDownload = %URLs if $NoDownload;
# If we're in a dry run then create a logfile with all current options
# and exit
&PerformNoDownload and exit 0 if $NoDownload;

# Make sure only new podcasts are downloaded
foreach my $url (keys %URLs)
{
	$DownloadQueue{$url} = 1 unless $AlreadyDownloaded{$url};
	$NeedToDownload++ unless $AlreadyDownloaded{$url};
}
unless (%DownloadQueue) {
	print "Nothing to download :o\n" unless $Verbose;
	exit 0
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Download the podcasts and create the catalogue
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Okay, we really need to download something
# If we're in verbose mode, clear the screen and display how many files
# we need to download.
if (InPath "clear") {
	system("clear") if $Verbose;
}
print "$NeedToDownload new podcasts.\n\n" if $NeedToDownload > 1;
print "$NeedToDownload new podcast.\n\n" if $NeedToDownload == 1;
# Open the logfile for writing
open(LOGFILE, ">>$PodcastLog");

# Get the current date
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$year += 1900;					# The year doesn't have the format we want
$mon++; $mon = "0$mon" unless $mon >= 10;	# Make the month be 1-12 instead of 0-11 and add a 0 prefix to 1-9
my $date = "$year-$mon-$mday"; 			# The date in the international date format
# Create the ./$date directory if it doesn't exist
$CreatedDir = "1" unless -e "./$date";
mkdir "./$date" unless -e "./$date";

# Change working directory to $date
chdir "./$date";
# Download the podcasts
foreach (sort keys %DownloadQueue) {
	unless ($DryRun_Mode) {
		print "Downloading $_\n";
		# Curl returns nonzero on failure
		my $URL = QuoteURL($_);
		my $DownloadStatus = system("curl $CurlGlobal -O $URL");
		print LOGFILE "$_\n" unless $DownloadStatus;
		$DownloadStatus = $DownloadStatus >> 8;
		warn "Download of $_ failed! Curl exited with return value $DownloadStatus\n" if $DownloadStatus;
		# If we're in --first-only we add it to $AlreadyDownloaded{$_} so that NoDownload doesn't add it
		if ($FirstOnly) {
			$AlreadyDownloaded{$_} = 1 if $DownloadStatus;
		}
		# Set DownloadedSomething to true if the download succeeded.
		$DownloadedSomething = "1" unless $DownloadStatus;
	} else {
		print "Would download $_\n";
	}
}
chdir "..";

# Remove the directory we created if all downloads failed, if at least one
# download was successfull then we link ./latest to ./$date and add it to
# the catalogue.
if ($CreatedDir && !$DownloadedSomething) {
	unlink glob "$date/* $date/.*";
	rmdir "./$date";
} elsif ($DownloadedSomething) {
	# Filename filter
	# Remove junk after .EXTension and convert %20 to _
	chdir "./$date";
	foreach (<*?*>) {
		my $OldName = $_;
		s/\?.*//g;
		s/%20/_/g;
		rename("$OldName", "$_");
	}
	chdir "..";
	# Make the ./latest symlink point to $date
	unlink "./latest";
	symlink "./$date", "./latest";

	# Create our catalogue (podcasts sorted in named directories)
	mkdir "./catalogue" unless -e "./catalogue";
	mkdir "./catalogue/All/" unless -e "./catalogue/All/";
	chdir "./$date";
	# For every file, make sure it has a catalogue entry.
	foreach (<*>) {
		my $OrigName = "$_";
		# We don't want to do anything to playlists
		unless (/\.m3u/) {
			my ($Existed, $PodBaseName);
			# Get the base name of the podcast
			if ($PodNames{$OrigName}) {
				$PodBaseName = $PodNames{$OrigName};
			} else {
				# If we couldn't get the podcast name from the feed then try even
				# harder here.
				/[0-9]*([a-zA-Z\-\_]*)[0-9]*/;	# Get alphabetical characters and -_.
				$_ = "$1";
				1 while s/[\-\_]+(show|promo)*$//i; 		# Remove various junk
				s/[\-\_].$//;					# Remove -_ in the end of the name
				$PodBaseName = "\u$_";				# Make the first character be uppercase
				$PodBaseName = "Unknown" unless $PodBaseName;	# Bah :(
			}
			chdir "../catalogue";
			mkdir "./$PodBaseName" unless -e "./$PodBaseName";
			chdir "./$PodBaseName";
			# We don't want to do anything if it already exists
			unless (-e "./$OrigName") {
				# Try to get the extension
				my $NameExtension = $OrigName;
				$NameExtension =~ s/.*(\.\w)/$1/;
				# Symlink the files and write the playlist
				symlink "../../$date/$OrigName", "./$OrigName";
				unlink "./latest$NameExtension" if -e "./latest$NameExtension";
				symlink "../../$date/$OrigName", "./latest$NameExtension";
				open(PLAYLIST, ">>./$PodBaseName.m3u");
				print PLAYLIST "$OrigName\n";
				close(PLAYLIST);
			}
			# Add it to All too if needed
			unless (-e "../All/$OrigName") {
				chdir "../All/";
				# Symlink it
				symlink "../../$date/$OrigName", "./$OrigName";
			}
			chdir "../../$date";
		}
	}
	# NoDownload if in --first-only
	&PerformNoDownload if $FirstOnly;
}
