package Slim::Web::Pages::Playlist;

# $Id: Playlist.pm 11972 2007-05-12 07:32:19Z kdf $

# SlimServer Copyright (c) 2001-2007 Logitech.
# 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;

use File::Spec::Functions qw(:ALL);
use POSIX ();
use Scalar::Util qw(blessed);

use Slim::Utils::Misc;
use Slim::Utils::Strings qw(string);
use Slim::Web::Pages;

use constant CACHE_TIME => 300;

sub init {
	
	Slim::Web::HTTP::addPageFunction( qr/^playlist\.(?:htm|xml)/, \&playlist, 'fork' );
}

sub playlist {
	my ($client, $params, $callback, $httpClient, $response) = @_;
	
	if (!defined($client)) {

		# fixed faster rate for noclients
		$params->{'playercount'} = 0;
		return Slim::Web::HTTP::filltemplatefile("playlist.html", $params);
	
	} elsif ($client->needsUpgrade()) {

		$params->{'player_needs_upgrade'} = '1';
		return Slim::Web::HTTP::filltemplatefile("playlist_needs_upgrade.html", $params);
	}
	
	# If synced, use the master's playlist
	$client = Slim::Player::Sync::masterOrSelf($client);

	$params->{'playercount'} = Slim::Player::Client::clientCount();
	
	my $songcount = Slim::Player::Playlist::count($client);

	$params->{'playlist_items'} = '';
	$params->{'skinOverride'} ||= '';
	
	my $count = Slim::Utils::Prefs::get('itemsPerPage');

	unless (defined($params->{'start'}) && $params->{'start'} ne '') {

		$params->{'start'} = (int(Slim::Player::Source::playingSongIndex($client)/$count)*$count);
	}

	if ($client->currentPlaylist() && !Slim::Music::Info::isRemoteURL($client->currentPlaylist())) {
		$params->{'current_playlist'} = $client->currentPlaylist();
		$params->{'current_playlist_modified'} = $client->currentPlaylistModified();
		$params->{'current_playlist_name'} = Slim::Music::Info::standardTitle($client,$client->currentPlaylist());
	}

	if ($::d_playlist && $client->currentPlaylistRender() && ref($client->currentPlaylistRender()) eq 'ARRAY') {

		msg("currentPlaylistChangeTime : " . localtime($client->currentPlaylistChangeTime()) . "\n");
		msg("currentPlaylistRender     : " . localtime($client->currentPlaylistRender()->[0]) . "\n");
		msg("currentPlaylistRenderSkin : " . $client->currentPlaylistRender()->[1] . "\n");
		msg("currentPlaylistRenderStart: " . $client->currentPlaylistRender()->[2] . "\n");

		msg("skinOverride: $params->{'skinOverride'}\n");
		msg("start: $params->{'start'}\n");
	}

	# Only build if we need to - try to return cached html or build page from cached info
	my $cachedRender = $client->currentPlaylistRender();

	if ($songcount > 0 && 
		defined $params->{'skinOverride'} &&
		defined $params->{'start'} &&
		$cachedRender && ref($cachedRender) eq 'ARRAY' &&
		$client->currentPlaylistChangeTime() &&
		$client->currentPlaylistChangeTime() < $client->currentPlaylistRender()->[0] &&
		$cachedRender->[1] eq $params->{'skinOverride'} &&
		$cachedRender->[2] eq $params->{'start'} ) {

		if ($cachedRender->[5]) {

			$::d_playlist && msg("Returning cached playlist html - not modified.\n");

			# reset cache timer to forget cached html
			Slim::Utils::Timers::killTimers($client, \&flushCachedHTML);
			Slim::Utils::Timers::setTimer($client, time() + CACHE_TIME, \&flushCachedHTML);

			return $client->currentPlaylistRender()->[5];

		} else {

			$::d_playlist && msg("Rebuilding playlist from cached params.\n");

			if (Slim::Utils::Prefs::get("playlistdir")) {
				$params->{'cansave'} = 1;
			}

			$params->{'playlist_items'}   = $client->currentPlaylistRender()->[3];
			$params->{'pageinfo'}         = $client->currentPlaylistRender()->[4];

			return Slim::Web::HTTP::filltemplatefile("playlist.html", $params);
		}
	}

	if (!$songcount) {
		return Slim::Web::HTTP::filltemplatefile("playlist.html", $params);
	}

	my $item;
	my %form;

	$params->{'cansave'} = 1;
	
	$params->{'pageinfo'} = Slim::Web::Pages->pageInfo({
				'itemCount'    => $songcount,
				'currentItem'  => Slim::Player::Source::playingSongIndex($client),
				'path'         => $params->{'path'},
				'otherParams'  => "player=" . Slim::Utils::Misc::escape($client->id()),
				'start'        => $params->{'start'},
				'perPage'      => $params->{'itemsPerPage'},
	});
	
	my ($start,$end);
	$start = $params->{'start'} = $params->{'pageinfo'}{'startitem'};
	$end = $params->{'pageinfo'}{'enditem'};
	
	my $offset = $start % 2 ? 0 : 1; 

	my $currsongind   = Slim::Player::Source::playingSongIndex($client);

	my $itemsPerPage = Slim::Utils::Prefs::get('itemsPerPage');
	my $composerIn   = Slim::Utils::Prefs::get('composerInArtists');

	my $titleFormat  = Slim::Music::Info::standardTitleFormat();

	$params->{'playlist_items'} = [];
	$params->{'myClientState'}  = $client;

	# This is a hot loop.
	# But it's better done all at once than through the scheduler.

	for my $itemnum ($start..$end) {

		# These should all be objects - but be safe.
		my $objOrUrl = Slim::Player::Playlist::song($client, $itemnum);
		my $track    = $objOrUrl;

		if (!blessed($objOrUrl) || !$objOrUrl->can('id')) {

			$track = Slim::Schema->rs('Track')->objectForUrl($objOrUrl) || do {

				msg("Couldn't retrieve objectForUrl: [$objOrUrl] - skipping!\n");
				next;
			};
		}

		my %form = ();

		$track->displayAsHTML(\%form);

		$form{'num'}       = $itemnum;
		$form{'levelName'} = 'track';
		$form{'odd'}       = ($itemnum + $offset) % 2;

		if ($itemnum == $currsongind) {
			$form{'currentsong'} = "current";

			if (Slim::Music::Info::isRemoteURL($track)) {
				$form{'title'} = Slim::Music::Info::standardTitle(undef, $track) || $track->url;
			} else {
				$form{'title'} = Slim::Music::Info::getCurrentTitle(undef, $track->url);
			}

		} else {

			$form{'currentsong'} = undef;
			$form{'title'}    = Slim::Music::TitleFormatter::infoFormat($track, $titleFormat);
		}

		$form{'nextsongind'} = $currsongind + (($itemnum > $currsongind) ? 1 : 0);

		push @{$params->{'playlist_items'}}, \%form;

		# don't neglect the streams too long
		main::idleStreams();
	}

	$::d_playlist && msg("End playlist build.\n");

	my $page = Slim::Web::HTTP::filltemplatefile("playlist.html", $params);

	if ($client) {

		# Cache to reduce cpu spike seen when playlist refreshes
		# For the moment cache html for Default, other skins only cache params
		# Later consider caching as html unless an ajaxRequest
		# my $cacheHtml = !$params->{'ajaxRequest'};
		my $cacheHtml = (($params->{'skinOverride'} || Slim::Utils::Prefs::get('skin')) eq 'Default');

		my $time = time();

		$client->currentPlaylistRender([
			$time,
			($params->{'skinOverride'} || ''),
			($params->{'start'}),
			$params->{'playlist_items'},
			$params->{'pageinfo'},
			$cacheHtml ? $page : undef,
		]);

		$::d_playlist && msg(sprintf("Caching playlist as %s.\n", $cacheHtml ? 'html' : 'params'));

		Slim::Utils::Timers::killTimers($client, \&flushCachedHTML);

		if ($cacheHtml) {
			Slim::Utils::Timers::setTimer($client, $time + CACHE_TIME, \&flushCachedHTML);
		}
	}

	return $page;
}

sub flushCachedHTML {
	my $client = shift;

	$::d_playlist && msg("Flushing playlist html cache for client.\n");
	$client->currentPlaylistRender(undef);
}

1;

__END__

# Local Variables:
# tab-width:4
# indent-tabs-mode:t
# End:
