#!/usr/bin/perl
# GoldenPod graphical configuration
# Copyright (C) Eskild Hustvedt 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

use strict;			# Force strict coding
use warnings;			# Tell perl to warn about things
use Gtk2 '-init';		# It's a Gtk2 app
use Gtk2::SimpleList;		# We need the simplelist for the config of podcasts.conf
use Gtk2::Gdk::Keysyms;		# So that we can bind esc to cancel
use Cwd;			# We need Cwd::Realpath to find out which directory we live in
use File::Basename;		# We meed dorname to help Cwd::Realpath finding the directory
use Getopt::Long;		# Commandline parsing

my $Version = "0.6";
my $RCSRev = '$Id: gpconf,v 1.16 2006/07/29 13:16:35 zero_dogg Exp $';

# Gtk2 objects
my (
	$InitialMessage,	$BrowseEnt,	$MainWindow,
	$MainTable,		$PatternIgnored, $PodcastList
);

# The configuration options
my (
	$WorkingDir,	$DefaultVerbosity,	$IgnorePattern,
	$PodcastFilter
);

# Other global values
my (
	@PLComments,	$SkipGtkTest,
);

# The configuration directory for GoldenPod
my $UserConfigDir = "$ENV{HOME}/.goldenpod";
# The podcast config file
my $PodcastFile = "$UserConfigDir/podcasts.conf";

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Helper routines
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# Load the configuration file
sub InitConfig {
	unless ( -e "$UserConfigDir/goldenpod.conf" ) {
		$WorkingDir = "$ENV{HOME}/Podcasts";
		$DefaultVerbosity = 1;
		$PodcastFilter = 0;
		$IgnorePattern = "";
		return(1);
	}
	# 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 
	$WorkingDir = "$Settings::WorkingDir";
	$DefaultVerbosity = "$Settings::DefaultVerbosity";
	$PodcastFilter = "$Settings::PodcastFilter";
	$IgnorePattern = "$Settings::IgnorePattern";
}

# Save the changed configuration file
sub SaveConfig {
	$IgnorePattern = $PatternIgnored->get_text;
	# Write the configuration file
	open(PRIMARY_CONFIG, ">$UserConfigDir/goldenpod.conf");
	print PRIMARY_CONFIG "# GoldenPod configuration file (written by gpconf)\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 = \"$WorkingDir\";\n\n# The default verbosity, anything other than 0 means verbose\n\$DefaultVerbosity = \"$DefaultVerbosity\";\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 = \"$IgnorePattern\";\n";
	print PRIMARY_CONFIG "\n# Set this to 1 if you want GoldenPod only to download audio feeds\n\$PodcastFilter = \"$PodcastFilter\";";
	close(PRIMARY_CONFIG);
}

# Save the podcast list
sub SavePodcastList {
	
	my $CurrentItem = 0;
	# Open the file for writing
	open(PODCAST_LIST, ">$PodcastFile");
	foreach (@PLComments) {
		$CurrentItem++;
		print PODCAST_LIST "$_\n";
		print PODCAST_LIST "\n" if $CurrentItem == 2;
	}
	$CurrentItem = 0;
	while (1) {
		if (defined(@{$PodcastList->{data}[$CurrentItem]})) {
				print PODCAST_LIST "@{$PodcastList->{data}[$CurrentItem]}\n";
			} else {
				last
		}
		$CurrentItem++;
	}
	close(PODCAST_LIST);
}

# The routine called when the OK button is pressed
sub OkayRoutine {
	SaveConfig;
	Gtk2->main_quit;
}

# Tests if we have the correct gtk2 version and errors out if we don't
sub Gtk2Test {
	unless (Gtk2->CHECK_VERSION (2,4,0)) {
		my $Dialog = Gtk2::MessageDialog->new(undef, "modal", 'error', 'ok', "Error: I need at least version 2.4.0 of Gtk2 but you only have version " . join (".", Gtk2->GET_VERSION_INFO) . "\n");
		$Dialog->run;
		die("Error: I need at least version 2.4.0 of Gtk2 but you only have version " . join (".", Gtk2->GET_VERSION_INFO) . "\n");
	}
}

# Detects and returns the path of the image file supplied, or undef
# Can take multiple arguments in which case it returns the first one it finds
sub DetectImage {
	my $I_Am_At = dirname(Cwd::realpath($0));
	foreach my $Image (@_) {
		foreach my $Dir ("$I_Am_At/art", $I_Am_At, "/usr/share/goldenpod", "/usr/local/goldenpod", "/usr/local/share/goldenpod", "/usr/share/icons/large", "/usr/share/icons", "/usr/share/icons/mini") {
			if (-e "$Dir/$Image") {
				return("$Dir/$Image");
			}
		}
	}
	return(undef);
}

# Handler of quitting using escape
sub EscapeQuitHandler ($$) {
	my ($widget, $event) = @_;
	if ($event->keyval == $Gtk2::Gdk::Keysyms{Escape}) {
		$widget->destroy;
	}
}

# 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]";
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# GUI functions
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

# About dialog
sub AboutGPconf {
	my $AboutDialog = Gtk2::AboutDialog->new;
	my $LogoImage = DetectImage("GoldenPod.Text-256.png", "GoldenPod-128.png", "GoldenPod.Header.png", "GoldenPod-64.png", "GoldenPod-32.png", "GoldenPod-16.png");
	if ($LogoImage) {
		my $PixBuf = Gtk2::Gdk::Pixbuf->new_from_file($LogoImage);
		$AboutDialog->set_logo($PixBuf);
	}
	$AboutDialog->set_authors("Eskild Hustvedt <zerodogg AT skolelinux DOT no>");
	$AboutDialog->set_artists("Jason Holland");
	$AboutDialog->set_copyright("Copyright (C) Eskild Hustvedt 2006");
	$AboutDialog->set_name("GoldenPod configuration");
	$AboutDialog->set_website("http://goldenpod.nongnu.org/");
	$AboutDialog->set_comments("A graphical configuration program for the\nGoldenPod commandline podcatcher");
	$AboutDialog->set_skip_pager_hint(1);
	$AboutDialog->set_skip_taskbar_hint(1);
	$AboutDialog->signal_connect("key_release_event" => \&EscapeQuitHandler);
	$AboutDialog->run;
}

# The routine that prompts the user for the directory to download
# podcasts to
sub GetWorkingDir {
	my $FileChooser = Gtk2::FileChooserDialog->new ('Please select the directory you wish to download podcasts to', $MainWindow, 'select_folder', 
							'gtk-cancel' => 'cancel',
							'gtk-ok' => 'ok'); 
	if (-d $WorkingDir and -r $WorkingDir) {
		$FileChooser->select_uri("file://$WorkingDir");
	}
	$FileChooser->set_modal(1);
	# Connect private handlers for destroy and delete-event for the file chooser
	$FileChooser->signal_connect("destroy" => sub { $FileChooser->destroy; });
	$FileChooser->signal_connect("delete-event" => sub { $FileChooser->destroy;});

	if ($FileChooser->run eq 'ok') {
		$WorkingDir = $FileChooser->get_filename;
		$BrowseEnt->set_text($WorkingDir);
	}
	$FileChooser->destroy;
}

# The routine that manages the podcasts.conf
sub PodcastManager {
	# Create the window
	my $ListWindow = Gtk2::Window->new();
	$ListWindow->set_title("GoldenPod feed configuration");
	$ListWindow->set_position('center');
	$ListWindow->set_modal(1);
	$ListWindow->signal_connect("destroy" => sub { $ListWindow->destroy;});
	$ListWindow->signal_connect("delete-event" => sub { $ListWindow->destroy;});
	$ListWindow->signal_connect("key_release_event" => \&EscapeQuitHandler);
	$ListWindow->set_skip_pager_hint(1);
	$ListWindow->set_skip_taskbar_hint(1);
	$ListWindow->set_default_size(500,300);

	my $ScrolledSubWindow = Gtk2::ScrolledWindow->new();
	$ScrolledSubWindow->show();

	# Create the initial hbox
	my $LWHbox = Gtk2::HBox->new (0, 6);
	$ListWindow->add($LWHbox);
	
	# Create the vbox used for the right-side buttons
	my $LWVbox = Gtk2::VBox->new (0, 4);
	
	# Create the podcast list widget and the list itself
	$PodcastList = Gtk2::SimpleList->new (
		'Feed URL'	=> 'text' );
	open(PODCONF, "<$PodcastFile");
	
	foreach (<PODCONF>) {
		chomp;
		if (/^\s*#/) {
			push (@PLComments, $_);
			next;
		}
		next if /^\s*$/;
		push (@{$PodcastList->{data}}, $_);
	}
	close(PODCONF);
	# Allow the list to be edited
	$PodcastList->set_column_editable (0,1);
	# We want the user to be able to remove more than one at a time
	$PodcastList->get_selection->set_mode ('multiple');
	$ScrolledSubWindow->add($PodcastList);
	$ScrolledSubWindow->set_policy('automatic', 'automatic');
	
	# Add the list and vbox widgets to the hbox widget
	$LWHbox->pack_start($ScrolledSubWindow,1,1,0);
	$LWHbox->pack_end($LWVbox,0,0,0);
	
	# tooltips
	my $Tooltip = Gtk2::Tooltips->new();
	
	# Add a add button
	my $AddButton = Gtk2::Button->new_from_stock('gtk-add');
	$AddButton->show();
	$LWVbox->pack_start($AddButton, 0, 0, 0);
	$Tooltip->set_tip($AddButton, "Add a new feed");
	$AddButton->signal_connect('clicked' => sub {
			# Push an empty value onto the array
			push(@{$PodcastList->{data}}, "");
			# Unselect currently selected items
			$PodcastList->unselect($PodcastList->get_selected_indices);
			# Select the new one
			$PodcastList->select("$#{$PodcastList->{data}}");
		} );
	
	# Add a remove button
	my $RemoveButton = Gtk2::Button->new_from_stock("gtk-remove");
	$RemoveButton->show();
	$LWVbox->pack_start($RemoveButton, 0, 0, 0);
	$Tooltip->set_tip($RemoveButton, "Remove the selected feed(s)");
	$RemoveButton->signal_connect('clicked' => sub {
			# splice all selected items off of the array
			splice @{$PodcastList->{data}}, $_, 1 for reverse $PodcastList->get_selected_indices;
		} );
	
	# Add a cancel button
	my $QuitButton = Gtk2::Button->new_from_stock("gtk-cancel");
	$QuitButton->show();
	$LWVbox->pack_end($QuitButton, 0, 0, 0);
	$Tooltip->set_tip($QuitButton, "Discard the current changes");
	$QuitButton->signal_connect('clicked' => sub {
			# Destroy the list window and then show the main window
			$ListWindow->destroy();
			#$MainWindow->show();
		});
	
	# Add a save button
	my $OKButton = Gtk2::Button->new_from_stock("gtk-save");
	$OKButton->show();
	$LWVbox->pack_end($OKButton, 0, 0, 0);
	$Tooltip->set_tip($OKButton, "Save changes to the podcast configuration file");
	$OKButton->signal_connect('clicked' => sub {
			# Destroy the list window
			$ListWindow->destroy;
			# Save the podcast list
			SavePodcastList;
		});
	
	# Show it all :D
	$LWVbox->show();
	$PodcastList->show();
	$LWHbox->show();
	$ListWindow->show();
}

# The main GUI function
sub MainGUI {
	# First load the config
	InitConfig;
	
	# Create the window
	$MainWindow = Gtk2::Window->new('toplevel');
	$MainWindow->set_title("GoldenPod ($Version) configuration");
	$MainWindow->set_border_width(5);
	$MainWindow->set_position('center');

	# Icon
	my $MainWindowIcon = DetectImage("GoldenPod-64.png","GoldenPod-32.png", "GoldenPod-16.png");
	if ($MainWindowIcon) {
		$MainWindow->set_default_icon_from_file("$MainWindowIcon");
	}
	
	# Handle closing
	$MainWindow->signal_connect("destroy" => \&OkayRoutine);
	$MainWindow->signal_connect("delete-event" => \&OkayRoutine);
	$MainWindow->set_resizable(0);
	
	# Create the vbox for the main window
	my $MainVBox = Gtk2::VBox->new();
	$MainVBox->show();
	$MainWindow->add($MainVBox);

	# Create a configuration frame
	my $ConfigFrame = Gtk2::Frame->new('Configuration');
	$ConfigFrame->set_border_width(3);
	$ConfigFrame->show();

	# Create a VBox to place inside the configuration frame
	my $ConfigVBox = Gtk2::VBox->new();
	$ConfigVBox->show();
	$ConfigFrame->add($ConfigVBox);
	
	# Create the configuration hboxes to place inside the ConfigVBox
	my $PodManHBox = Gtk2::HBox->new();
	my $WorkingDirHBox = Gtk2::HBox->new();
	my $IgnorePatternHBox = Gtk2::HBox->new();
	my $VerbosityHBox = Gtk2::HBox->new();
	my $FilterHBox = Gtk2::HBox->new();
	# Add them to the ConfigVBox
	$ConfigVBox->pack_start($FilterHBox, 0, 0, 0);
	$ConfigVBox->pack_start($VerbosityHBox, 0, 0, 0);
	$ConfigVBox->pack_start($IgnorePatternHBox,0 ,0, 0);
	$ConfigVBox->pack_start($WorkingDirHBox,0 ,0, 0);
	$ConfigVBox->pack_start($PodManHBox,0 ,0, 0);
	# Show them
	$FilterHBox->show();
	$VerbosityHBox->show();
	$IgnorePatternHBox->show();
	$WorkingDirHBox->show();
	$PodManHBox->show();
	
	# Create the main hboxes
	my $CommandHBox = Gtk2::HBox->new();
	my $HeaderHBox = Gtk2::HBox->new();
	# Show them
	$HeaderHBox->show();
	$CommandHBox->show();
	# Add the HeaderHBox, ConfigFrame and CommandHBox to the MainVBox
	$MainVBox->pack_start($HeaderHBox, 0, 0, 0);
	$MainVBox->pack_start($ConfigFrame, 0, 0, 0);
	$MainVBox->pack_start($CommandHBox,0 ,0, 0);
	
	# Ok button
	my $OKButton = Gtk2::Button->new_from_stock('gtk-close');
	$OKButton->signal_connect("clicked" => \&OkayRoutine);
	$OKButton->show();
	$CommandHBox->pack_end($OKButton, 0, 0, 0);
	
	my $OKTooltip = Gtk2::Tooltips->new;
	$OKTooltip->set_tip($OKButton, "Save changes and exit");
	
	# About button
	my $AboutButton = Gtk2::Button->new_from_stock('gtk-about');
	$AboutButton->signal_connect("clicked" => \&AboutGPconf);
	$AboutButton->show();
	$CommandHBox->pack_end($AboutButton, 0, 0, 0);
	
	# Podcast management
	# We want to use a stock icon
	my $PodcastManageBtn_Icon = Gtk2::Image->new_from_stock("gtk-properties", "button");
	$PodcastManageBtn_Icon->show();
	
	my $PodcastManageBtn = Gtk2::Button->new("_Manage podcast feeds");
	$PodcastManageBtn->set_image($PodcastManageBtn_Icon);
	$PodcastManageBtn->signal_connect("clicked" => \&PodcastManager);
	$PodcastManageBtn->show();
	$PodManHBox->pack_start($PodcastManageBtn, 0, 0, 0);
	
	my $PodcastTooltip = Gtk2::Tooltips->new;
	$PodcastTooltip->set_tip($PodcastManageBtn, "Manage the list of podcast feed URL's (podcasts.conf)");
	
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Welcome message
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	my $HeaderImage = DetectImage ("GoldenPod.Header.png");
	if ($HeaderImage) {
		$InitialMessage = Gtk2::Image->new_from_file($HeaderImage);
	} else {
		$InitialMessage = Gtk2::Label->new("Welcome to the graphical GoldenPod\nconfiguration program (version $Version)");
	}
	$InitialMessage->show();
	$HeaderHBox->pack_start($InitialMessage, 0, 0, 0);
	
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Working directory
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	my $BrowseLabel = Gtk2::Label->new("Target directory");
	$BrowseLabel->show();
	$WorkingDirHBox->pack_start($BrowseLabel, 0, 0, 0);
	
	$BrowseEnt = Gtk2::Entry->new();
	$BrowseEnt->set_text($WorkingDir);
	$BrowseEnt->show();
	$BrowseEnt->set_editable(0);
	$WorkingDirHBox->pack_start($BrowseEnt, 0, 0, 0);
	
	my $BrowseBTImage = Gtk2::Image->new_from_stock("gtk-find", "button");
	$BrowseBTImage->show();
	my $BrowseBT = Gtk2::Button->new("B_rowse...");
	$BrowseBT->signal_connect('clicked' => \&GetWorkingDir );
	$BrowseBT->set_image($BrowseBTImage);
	$BrowseBT->show();
	$WorkingDirHBox->pack_start($BrowseBT,0 ,0 ,0);
	
	my $BrowseTooltip = Gtk2::Tooltips->new();
	$BrowseTooltip->set_tip($BrowseEnt, "The directory to download podcasts to");
	
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Default verbosity
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	my $Verbosity = Gtk2::CheckButton->new_with_label("Be verbose by default");
	if ($DefaultVerbosity) {
		Gtk2::ToggleButton::set_active($Verbosity, 1);
	}
	# Connect the signal
	$Verbosity->signal_connect("toggled" => sub { if ($Verbosity->get_active) {
								$DefaultVerbosity = 1;
							} else {
								$DefaultVerbosity = 0;
							} } );
	$Verbosity->show();
	$VerbosityHBox->pack_start($Verbosity, 0, 0, 0);

	my $VerbosityTooltip = Gtk2::Tooltips->new;
	$VerbosityTooltip->set_tip($Verbosity, "Check this if you want the commandline GoldenPod to be verbose by default");
	
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Ignore pattern
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	my $IPLabel = Gtk2::Label->new("Ignored pattern");
	$IgnorePatternHBox->pack_start($IPLabel, 0, 0, 0);
	$IPLabel->show();
	
	$PatternIgnored = Gtk2::Entry->new();
	Gtk2::Entry::set_text($PatternIgnored, $IgnorePattern);
	$PatternIgnored->show();
	$IgnorePatternHBox->pack_start($PatternIgnored, 0, 0, 0);
	
	my $PatternTooltip = Gtk2::Tooltips->new;
	$PatternTooltip->set_tip($PatternIgnored, "Set this to a (perl) regular expression you want goldenpod to ignore when downloading or copying podcasts");

	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Podcast filter
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	my $Filter = Gtk2::CheckButton->new_with_label("Only download audio feeds");
	if ($PodcastFilter) {
		Gtk2::ToggleButton::set_active($Filter, 1);
	}
	# Connect the signal
	$Filter->signal_connect("toggled" => sub { if ($Filter->get_active) {
		       					$PodcastFilter = 1;
						} else {
							$PodcastFilter = 0;
						} } );
	$Filter->show();
	$FilterHBox->pack_start($Filter, 0, 0, 0);
	
	my $FilterTooltip = Gtk2::Tooltips->new;
	$FilterTooltip->set_tip($Filter, "Check this if you want GoldenPod to only download audio from the feeds (and not download anything else it finds in them");
	
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Show it all
	# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
	# Show the program
	$MainWindow->show();
	
	# Rest in the GTK2 main loop
	Gtk2->main;

	0;
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Main program and commandline parsing
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
GetOptions (
	'help|h' => sub { print "\nGoldenPod version $Version configuration\n\n";
		PrintHelp("-h","--help","Display this help screen and exit");
       		PrintHelp("-v","--version", "Display version information and exit");
		PrintHelp("","--no-gtk-v-check", "Don't verify GTK version (do NOT report bugs if you use this)");
		PrintHelp("","--debuginfo", "Display information useful for debugging and exit");
		exit(0);
	},
	'version|v' => sub { print "GoldenPod version $Version configuration\n"; exit(0); },
	'no-gtk-v-check' => sub {
				$SkipGtkTest = 1;
				print "Skipping Gtk2 test (you have version ", join (".", Gtk2->GET_VERSION_INFO), "). I need at least version 2.4.0\n";
		},
	'debuginfo' => sub {
		print "GoldenPod version $Version configuration ($0)\n";
		print "$RCSRev\n";
		print "Gtk2 version ", join (".", Gtk2->GET_VERSION_INFO),"\n";
		printf "Perl version %vd\n", $^V;
		exit(0);
	},
) or die "Run $0 --help for more information\n";
Gtk2Test() unless $SkipGtkTest;
MainGUI();
