# Archive.pm by Dan Aronson (danaronson@yahoo.com)
#
# This code is derived from code with the following copyright message:
#
# SliMP3 Server Copyright (C) 2001 Sean Adams, Slim Devices Inc.
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2.
use strict;

###########################################
### Section 1. Change these as required ###
###########################################

package Plugins::Archive;

use Slim::Networking::SimpleAsyncHTTP;
use Slim::Control::Command;
use Slim::Utils::Strings qw (string);
use Slim::Utils::Misc;
use vars qw($VERSION);

$VERSION = "0.92";

my @bands;
my %band_index;

my $UNKNOWN    = 0;
my $FETCH      = 1;
my $PARSE      = 2;
my $FETCH_SHOW = 3;
my $PLAYING    = 4;
my $OK         = 5;
my $ERROR      = 6;


my %bands;
my %which_concert;
my %current_song;
my %saved_song;
my %song_count;
my %band_url;
my %current_artist;
my %current_year;
my %year_index;
my %year_count;
my %years;
my @years;
my %old_year;
my %show_index_by_artist;
my %old_show_index_by_artist;
my %shows;
my %show_count;
my %show_index;
my %retrieval_error;
my %content; # used for http content (see subroutine 'get')
my %error;   # error during call to 'get'
my %get_status;
my %concert_by_id;

sub getDisplayName() {return "PLUGIN_ARCHIVE_MODULE_NAME"; };

sub strings {
	return "
PLUGIN_ARCHIVE_MODULE_NAME
	EN	Live Music Archive

";}

##################################################
### Section 2. Your variables and code go here ###
##################################################


sub setMode() {
	my $client = shift;
	if (%retrieval_error && (1 == $retrieval_error{$client})) {
	    return;
	}
	    
	$get_status{$client} = $UNKNOWN;
	$client->lines(\&lines);
	unless (%bands) {
	    $band_index{$client} = 0;
	    Slim::Buttons::Block::block($client, "Fetching Archive Band List", "");
	    get_band_listing($client);
	} else {
	    *bands = $bands{$client};
	    $current_artist{$client} = $bands[$band_index{$client}];
	    $client->update();
	}
	
	
}


my %functions = (
	'up' => sub  {
		my $client = shift;
		$band_index{$client} = Slim::Buttons::Common::scroll($client,
								     -1,
								     $#bands + 1,
								     $band_index{$client} || 0);
		$current_artist{$client} = $bands[$band_index{$client}];
		$client->update();
        },
	'down' => sub  {
	    my $client = shift;
	    $band_index{$client} = Slim::Buttons::Common::scroll($client,
								 1,
								 $#bands + 1,
								 $band_index{$client} || 0);
	    $current_artist{$client} = $bands[$band_index{$client}];
	    $client->update();
	},
	'left' => sub  {
		my $client = shift;
		Slim::Buttons::Common::popModeRight($client);
	},
	'right' => sub  {
		my $client = shift;
		if ($years{$current_artist{$client}}) {
		    Slim::Buttons::Common::pushModeLeft($client, 'Archive::band_page');
                } else {
		    # if load_band_page is successful, it pushes Archive::band_page mode'
		    load_band_page($client);
		}
	},
	'numberScroll' => sub  {
		my $client = shift;
		my $button = shift;
		my $digit = shift;
		my $line1 = "button: ${button}";
		my $line2 = "digit: ${digit}";
		Slim::Display::Animation::showBriefly($client, $line1, $line2);
	}
);

sub lines {
        my $client = shift;
	my ($line1, $line2);
	$line1 = "Live Music Archive";
	if (-1 == $#bands) {
	    $line2 = "couldn't find any bands";
	} else {
	    my $band_index = $band_index{$client};
	    $line1 .= " (" . ($band_index+1) . ' ' . string('OF') . ' ' . ($#bands + 1) . ")";
	    $line2 = $bands[$band_index];
	}
	return ($line1, $line2, undef, Slim::Hardware::VFD::symbol('rightarrow'));
}

	
# real code

sub URL_decode {
    my $the_URL = shift;
    $the_URL =~ tr/+/ /;
    $the_URL =~ s/%([a-fA-F0-9]{2,2})/chr(hex($1))/eg;
    $the_URL =~ s/<!--(.|\n)*-->//g;
    return $the_URL;
}

my $tmp_file = "/tmp/osa.$$";
my $etree_listing = "http://www.archive.org/audio/etreelisting-browse.php?mode=mp3";
my $etree_details_db_url = "http://www.archive.org/audio/etree-details-db.php?id=";

sub http_error_cb {
	my $http = shift;
	my $client = $http->params('client');

	$retrieval_error{$client} = 1;
	Slim::Buttons::Block::unblock($client);
	$::d_plugins && Slim::Utils::Misc::msg("Archive: error while getting " .
									   $http->url . "\n");
	# abort and pop back to where we were before
	Slim::Buttons::Common::popModeRight($client);
	$retrieval_error{$client} = 0;	
}

sub get_band_listing {
    my $client = shift;

	# start asynchronous get
	# we'll be called back when its done.
	my $http = Slim::Networking::SimpleAsyncHTTP->new(\&get_band_listing_cb,
												  \&http_error_cb,
												  {client=>$client});
	$http->get($etree_listing);
}

# this method called when band listing http get returns.
# here we do the real work
sub get_band_listing_cb {
	my $http = shift;

	my $content = $http->content;
	my $client = $http->params('client');

	# get the real data
	if ($content) {
		while ($content =~ s/collection=etree&mode=mp3&cat=([^&]*)&PHPS//)
		{
			my $band_name = URL_decode($1);
			$band_url{$band_name} = $1;
			push(@bands, $band_name);
		}
	}
	$bands{$client} = @bands;
	if (-1 == $#bands) {
		$::d_plugins && Slim::Utils::Misc::msg("Couldn't find any bands, take a look at\n");
		$::d_plugins && Slim::Utils::Misc::msg("the result of ${etree_listing}\n");
	}
	$current_artist{$client} = $bands[0];
	Slim::Buttons::Block::unblock($client);
	$client->update();
}


sub get_band_page_listing
{
    my $client = shift;
    my $url = shift;

	# start asynchronous get
	# we'll be called back when its done.
	my $http = Slim::Networking::SimpleAsyncHTTP->new(\&get_band_page_listing_cb,
												  \&http_error_cb,
												  {client=>$client});

	$http->get($url);
}

sub get_band_page_listing_cb {
	my $http = shift;

	my $content = $http->content;
	my $client = $http->params('client');

	my $band_url = $band_url{$current_artist{$client}};
	my $search_string = "collection=etree&mode=mp3&cat=${band_url}%3A%20(\\d\\d\\d\\d)";
	my @years = ();
	while ($content =~ s/$search_string//)
	{
		push(@years, $1);
	}
	Slim::Buttons::Block::unblock($client);
	if (-1 == $#years) {
		$::d_plugins && Slim::Utils::Misc::msg("Couldn't find any years, take a look at\n");;
		#$::d_plugins && Slim::Utils::Misc::msg("the result of ${url}\n");
	} else {
		$years{$current_artist{$client}} = join(':', reverse(@years));
		$year_index{$current_artist{$client}} = 0;
		Slim::Buttons::Common::pushModeLeft($client, 'Archive::band_page');
	}
}

	
sub load_band_page {
    my $client = shift;
    my $band_url = $band_url{$current_artist{$client}};
    my $url = $etree_listing . "&cat=" . $band_url;
    Slim::Buttons::Block::block($client, "Fetching year list", $current_artist{$client});
    get_band_page_listing($client, $url);
}

sub band_page_mode_sub {
    my $client = shift;
    $client->lines(\&band_page_lines);
    @years = split(':', $years{$current_artist{$client}});
    $year_count{$client} = $#years;
    $client->update();
};


my $band_page_mode_sub = *band_page_mode_sub;

sub band_page_lines() {
    my $client = shift;
    my ($line1, $line2);
    $line1 = $current_artist{$client};
    if (-1 != $year_index{$current_artist{$client}}) {
	$line1 .= " (" . ($year_index{$current_artist{$client}}+1) . ' ' . string('OF') . ' ' . ($#years + 1) . ")";
	$line2 = $years[$year_index{$current_artist{$client}}];
    } else {
	$line2 = "";
    }
    return ($line1, $line2, undef, Slim::Hardware::VFD::symbol('rightarrow'));
};

my %band_page_functions = 
    (
     'up' => sub  {
	 my $client = shift;
	 $year_index{$current_artist{$client}} = Slim::Buttons::Common::scroll($client,
							      -1,
							      $year_count{$client} + 1,
							      $year_index{$current_artist{$client}});
	 $client->update();
     },
     'down' => sub  {
	 my $client = shift;
	 $year_index{$current_artist{$client}} = Slim::Buttons::Common::scroll($client,
							      1,
							      $year_count{$client} + 1,
							      $year_index{$current_artist{$client}});
	 $client->update();
     },
     'left' => sub {
	 my $client = shift;
	 Slim::Buttons::Common::popModeRight($client);
     },
     'right' => sub {
	 my $client = shift;
	 if (exists ($shows{$current_artist{$client}}{$year_index{$current_artist{$client}}})) {
	     Slim::Buttons::Common::pushModeLeft($client, 'Archive::year_page');
         } else {
	     # if year_page_listing is successful than the mode is pushed
	     year_page_listing($client);
	 }
     }
     );



# Band listing mode
Slim::Buttons::Common::addMode('Archive::band_page', \%band_page_functions, $band_page_mode_sub);



my $ID_CONCERT_SEPARATOR = "ID_CONCERT_SEPARATOR";
my $CONCERT_LIST_SEPARATOR = "CONCERT_LIST_SEPARATOR";
my $SHOW_LIST_SEPARATOR = "SHOW_LIST_SEPARATOR";

sub trim_whitespace
{
    my $string = shift;
    $string =~ s/\s*(\S.*?)\s*\z//;
    return($1);
}



sub year_page_mode_listing_aux
{
    my $client = shift;
    my $url = shift;

	# start asynchronous get
	# we'll be called back when its done.
	my $http = Slim::Networking::SimpleAsyncHTTP->new(\&year_page_mode_listing_aux_cb,
												  \&http_error_cb,
												  {client=>$client});

	$http->get($url);
}

sub year_page_mode_listing_aux_cb {
	my $http = shift;

	my $content = $http->content;
	my $client = $http->params('client');

	my $search_string_details = "etree-details-db.php\\?id=(\\d+).*<strong>(.*)</strong>";
	my $search_string_date    = "<b>Date:</b>\\s*(.*?)[,<]";
	my $search_string_venue   = "<b>Venue:</b>\\s*(.*?)<";
	my @concerts = ();
	while ($content =~ s/$search_string_details//) 
	{
	    my $id = $1;
	    my $concert = $2;
	    my $date = "";
	    my $venue = "";
	    my $concerts = ();
	    if (defined($id) && defined($concert)) {
			# let's get the following date and venue location
			$content =~ s/$search_string_date//;
			$date = $1;
			$content =~ s/$search_string_venue//;
			$venue = $1;
	    }
	    push(@concerts, join($ID_CONCERT_SEPARATOR, trim_whitespace($id), 
							 trim_whitespace($concert), trim_whitespace($date), 
							 trim_whitespace($venue)));
	}
	Slim::Buttons::Block::unblock($client);
	if (-1 == $#concerts) {
	    $::d_plugins && Slim::Utils::Misc::msg("Couldn't find conert list, take a look at\n");;
	    $::d_plugins && Slim::Utils::Misc::msg("the result of ".$http->url."\n");
	} else {
	    $shows{$current_artist{$client}}{$year_index{$current_artist{$client}}} = 
		join($CONCERT_LIST_SEPARATOR, @concerts);
	    $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = 0;
	    Slim::Buttons::Common::pushModeLeft($client, 'Archive::year_page');
	}
}


sub year_page_listing
{
    my $client = shift;
    my $year_index = $year_index{$current_artist{$client}};
    my $year_name = (split(':', $years{$current_artist{$client}}))[$year_index];
    my $year_url = $band_url{$current_artist{$client}} . "%3A%20" . $year_name;
    my $url = $etree_listing . "&cat=" . $year_url;
    # cache the index (so we don't have to keep looking this up)
    Slim::Buttons::Block::block($client, "Fetching Concert list from ${year_name}",
				$current_artist{$client});
    year_page_mode_listing_aux($client, $url);
};

sub year_page_mode_sub {
    my $client = shift;
    $client->lines(\&year_page_lines);
    my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}}
			 {$year_index{$current_artist{$client}}});
    $show_index{$client} = $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}};
    $show_count{$client} = $#concerts;
    $client->update();
};

my $year_page_mode_sub = *year_page_mode_sub;



sub year_page_lines() {
    my $client = shift;
    my ($line1, $line2);
    my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}}
			 {$year_index{$current_artist{$client}}});
    if (-1 != $show_index{$client}) {
	my ($id, $concert, $date, $venue) 
	    = split($ID_CONCERT_SEPARATOR, 
		    $concerts[$show_index{$client}]);
	$line1 = $current_artist{$client} . '/' . 
	    $years[$year_index{$current_artist{$client}}] . ' (' .  
	    ($show_index{$client} + 1) .
	    ' ' . string('OF') . ' ' . ($#concerts + 1) . ")";
	$line2 = $date . "/" . $venue;
    } else {
	$line1 = "Current Artist = " . $current_artist{$client} ."\n";
	$line2 = "";
    }
    return ($line1, $line2, undef, 
	    Slim::Hardware::VFD::symbol('notesymbol') .
	    Slim::Hardware::VFD::symbol('rightarrow'));
};


sub process_concert_button_press
{
    my $client = shift;
    my $button = shift;
    my @concerts = split($CONCERT_LIST_SEPARATOR, 
			 $shows{$current_artist{$client}}{$year_index{$current_artist{$client}}});
    my ($id, $concert) = split($ID_CONCERT_SEPARATOR, 
			       $concerts[$show_index{$client}]);
    if (exists $concert_by_id{$id}) {
	process_concert_button_press_aux($client, $id, $button);
    } else {
	my $details_url = $etree_details_db_url . $id;
	Slim::Buttons::Block::block($client, "Fetching playlist", $concert);
	retrieve_playlist($client, $id, $button);
    }
}

my $PLAYLISTITEM = "PLAYLISTITEM";

sub process_concert_button_press_aux
{
    my $client = shift;
    my $id = shift;
    my $button = shift;
    if (!exists($concert_by_id{$id})) {
	return;
    }
    my @playlist = split($PLAYLISTITEM, $concert_by_id{$id});
    $which_concert{$client} = $id;
    $song_count{$id} = $#playlist + 1;
    if (('play' eq $button) || ('add' eq $button)) {
	my $index = 0;
	my $song;
	
    my ($id, $concert, $date, $venue) = split($ID_CONCERT_SEPARATOR,
											  $concert_by_id{$id});
    my $playlistName = $date . "/" . 
	$years[$year_index{$current_artist{$client}}] . "/" . $venue .
	"/" . $current_artist{$client};
	
	my $playlistid = "Archive." . $id;
	Slim::Control::Command::execute($client, 
									['info', 'begin_playlist',
									 $playlistid, $playlistName]);
	foreach $song (@playlist) {
	    if ((0 == $index) && ('play' eq $button)) {
		Slim::Control::Command::execute($client, ['playlist', 'play', $song]);
	    } else {
		Slim::Control::Command::execute($client, ['playlist', 'add', $song]);
	    }
	    $index++;
	}
	Slim::Control::Command::execute($client, 
									['info', 'end_playlist',
									 $playlistid, $playlistName]);

	Slim::Control::Command::execute($client, ['play']);
	if ('add' eq $button) {
	    Slim::Display::Animation::showBriefly($client, "Added to current playlist");
	} 
    } else {
	Slim::Buttons::Common::pushModeLeft($client, 'Archive::concert_page');
    }
}

sub retrieve_playlist
{
    my $client = shift;
    my $id = shift;
    my $button = shift;

	# start asynchronous get
	# we'll be called back when its done.
	my $http = Slim::Networking::SimpleAsyncHTTP->new(\&retrieve_playlist_cb,
												  \&http_error_cb,
												  {client=>$client,
												   id=>$id,
												   button=>$button});

	$http->get($etree_details_db_url . $id);
}

sub retrieve_playlist_cb {
	my $http = shift;

	my $content = $http->content;
	my $client = $http->params('client');
	my $id = $http->params('id');
	my $button = $http->params('button');

	Slim::Buttons::Block::unblock($client);
	if ($content) {
	    my $song;
	    my $index = 0;
	    my @playlist = parse_playlist($content);
	    if (-1 == $#playlist) {
			Slim::Display::Animation::showBriefly($client, "Error, getting show, try another.");
			$::d_plugins && Slim::Utils::Misc::msg("Couldn't find playlist, take a look at\n");;
			$::d_plugins && Slim::Utils::Misc::msg("the result of " . $etree_details_db_url . $id . "\n");
	    } else {
			$concert_by_id{$id} = join($PLAYLISTITEM, @playlist);
	    }
		
	} else {
	    Slim::Display::Animation::showBriefly($client, "Error, getting show, try another.");
	}
	process_concert_button_press_aux($client, $id, $button);
}


my %year_page_functions = 

    (
     'up' => sub  {
	 my $client = shift;
	 $show_index{$client} = Slim::Buttons::Common::scroll($client,
							      -1,
							      $show_count{$client} + 1,
							      $show_index{$client});
	 $client->update();
     },
     'down' => sub  {
	 my $client = shift;
	 $show_index{$client} = Slim::Buttons::Common::scroll($client,
							      1,
							      $show_count{$client} + 1,
							      $show_index{$client});
	 $client->update();
     },
     'left' => sub {
	 my $client = shift;
	 # reset cache
	 $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = $show_index{$client};
	 Slim::Buttons::Common::popModeRight($client);
     },
     'right' => sub {
	 my $client = shift;
	 # reset cache
	 $show_index{$current_artist{$client}}{$year_index{$current_artist{$client}}} = $show_index{$client};
	 process_concert_button_press($client, 'right');
     },
     'add' => sub {
	 my $client = shift;
	 process_concert_button_press($client, 'add');
     },
     'play' => sub {
	 my $client = shift;
	 process_concert_button_press($client, 'play');
     }
     );


my $playlist_search_string = ".*<td class=\"fileTitleHeader\">Audio Files(.*?)</table>";
my $song_search_string = ".*<td class=\"fileTitle\">(.*?)</td>.*\?href=\"(.*?vbr.mp3)\?";
my $archive_prefix = "http://www.archive.org";

sub parse_playlist {
    $_ = shift;
    my $pls_data = "";
    my $count = 1;
    my @items;
    if ( /$playlist_search_string/s ) {
		# found song playlist lines, time to build playlist
		# we could do real parsing via HTML::TreeBuilder, but this should be faster and ok
		my $song_playlist_lines = $1;
		while ( $song_playlist_lines =~ s/$song_search_string// ) {
			my $name = $1;
			my $url = $2;
			# this is the whole list of urls, let's find the last one
			my $href_string = "href=\"";
			
			if (!defined($url)) {
				return([]);
			}
			my $href_pos = rindex($url, $href_string);
			if (-1 == $href_pos) {
				next;
			} else {
				$url = substr($url, $href_pos + length($href_pos) + 3);
			}
			$url = $archive_prefix . $url;
			push @items, $url;
			Slim::Music::Info::setTitle($url, $name);
			$count++;
		}
    }
	
	if (scalar(@items) < 1) {
		msg("Archive: found no playlist.  Here's what I parsed:\n\n$_\n\n");
	}

    return @items;
}

# Band listing mode
Slim::Buttons::Common::addMode('Archive::year_page', \%year_page_functions, $year_page_mode_sub);


sub concert_page_mode_sub 
{
    my $client = shift;
    my $id = $which_concert{$client};
    $client->lines(\&concert_page_lines);
    $current_song{$client}{$id} = $saved_song{$client}{$id} || 0;
    $client->update();
}

my $concert_page_mode_sub = *concert_page_mode_sub;

sub concert_page_lines 
{
    my $client = shift;
    my ($line1, $line2);
    my @concerts = split($CONCERT_LIST_SEPARATOR, $shows{$current_artist{$client}}
			 {$year_index{$current_artist{$client}}});
    my @playlist = split($PLAYLISTITEM, $concert_by_id{$which_concert{$client}});
    my ($id, $concert, $date, $venue) 
	= split($ID_CONCERT_SEPARATOR, 
		$concerts[$show_index{$client}]);
    $line1 = $date . "/" . $years[$year_index{$current_artist{$client}}] . "/" . $venue .
	"/" . $current_artist{$client};
    $line2 = Slim::Music::Info::title($playlist[$current_song{$client}{$id}]);
    return($line1, $line2, undef, Slim::Hardware::VFD::symbol('notesymbol'));
}

my %concert_page_functions = (
	'up' => sub  {
	    my $client = shift;
	    my $id = $which_concert{$client};
	    $current_song{$client}{$id} = Slim::Buttons::Common::scroll($client,
									-1,
									$song_count{$id},
									$current_song{$client}{$id} || 0);
	    $client->update();
        },
	'down' => sub  {
	    my $client = shift;
	    my $id = $which_concert{$client};
	    $current_song{$client}{$id} = Slim::Buttons::Common::scroll($client,
									1,
									$song_count{$id},
									$current_song{$client}{$id} || 0);
	    $client->update();
	},
	'left' => sub  {
		my $client = shift;
		my $id = $which_concert{$client};
		$saved_song{$client}{$id} = $current_song{$client}{$id};
		Slim::Buttons::Common::popModeRight($client);
	},
	'right' => sub  {
		my $client = shift;
		Slim::Display::Animation::bumpRight($client);
	},
	'add' => sub  {
		my $client = shift;
		my $id = $which_concert{$client};
		my @playlist = split($PLAYLISTITEM, $concert_by_id{$which_concert{$client}});
		Slim::Control::Command::execute($client, ['playlist', 'add', 
							  $playlist[$current_song{$client}{$id}]]);
		Slim::Display::Animation::showBriefly($client, "Added to current playlist");
	},
	'play' => sub  {
		my $client = shift;
		my $id = $which_concert{$client};
		my @playlist = split($PLAYLISTITEM, $concert_by_id{$which_concert{$client}});
		Slim::Control::Command::execute($client, ['playlist', 'play', 
							  $playlist[$current_song{$client}{$id}]]);
	}
);

# Concert Listing Mode
Slim::Buttons::Common::addMode('Archive::concert_page', \%concert_page_functions, $concert_page_mode_sub);



################################################
### End of Section 2.                        ###
################################################

################################
### Ignore from here onwards ###
################################

sub getFunctions() {
	return \%functions;
}


1;
