# BBCNews Ticker v1.4.1
# Copyright (c) 2002-2004 Gordon Johnston (gordonj@newswall.org.uk)
# Designed for Slimserver v 5.0 and above
# Slimserver v 5.2 or above required to access all features

# http://newswall.org.uk/~slimp3/news_ticker.html

# Contains code from SlimServer which is:  Copyright (c) 2001-2004 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.

package Plugins::BbcNews;
use strict;

# USER DEFINED CONSTANTS - You can configure the ticker here

# Automatic Refresh Frequency (sec). The frequency at which the ticker refreshes.
# Default 120 seconds (2 minutes)
# Set to 0 to disable automatic refresh
my $refresh_interval = 120;

# Now Playing Overlay - Display the 'now playing' display whilst running
# 0 - off
# 1 - screensaver mode only
# 2 - always
my $nowPlayingOverlay = 2;

# Disable Screensaver. Set this to 1 (one) to disable the screensaver whilst the BBC plugin is displayed
# You can set this and still use the plugin as a screensaver
# Set to 0 (zero) if you do not want to disable the screensaver.
my $screensaver_disable = 1;

# Auto Scroll Speed
# Delay in seconds before ticker automatically moves to the next news topic (disabled by default)
my $auto_scroll = 0;

# Auto Scroll Screensaver Speed
# As auto_scroll, above, but only applied when plugin in running as a screensaver.
# Any keypress exits the screensaver so ticker must move between topics automatically (32 seconds by default)
my $auto_scroll_screensaver = 32;

# Item Seperator. This is inserted between each item of news.
my $item_seperator = ' --- ';

# Wrap Around. Set to 0 (zero) if you would like the ticker to stop when scrolling with the remote reaches
#              the final news catagory 
my $wrap = 1;

# Current date masking. Set this to 1 (one) to hide the date when displaying the ticker text
# The date can always be viewed in the 'World News' category, so by default this option is on
my $date_hide = 1;

# The url for the bbc news ticker
my $bbcnews_location = 'http://tickers.bbc.co.uk/tickerdata/story2.dat';

# Number of seconds to wait between categories when scrolling with the mouse button held down
my $scrollSpeed = .3;


# The BBC News Ticker is split into 8 news topics.
# You can specify the topics you want to display and their ordering here:
# See below for a key to the topic numbers

# show all
#my @display_stack=(2,3,4,5,6,7,8);
#my @display_stack_left=();
#my $display_current=1;

# no travel
# we hide travel by default as it doesn't display anything useful
my @display_stack=(2,3,4,5,6,8);
my @display_stack_left=();
my $display_current=1;

# Topics are:

my @news_description=(
	'NEWS DESCRIPTIONS',	#Topic Numbers
	'WORLD NEWS',		#1
	'UK NEWS',		#2
	'SPORTS NEWS',		#3
	'BUSINESS NEWS',	#4
	'SCI-TECH NEWS',	#5
	'WEATHER',		#6
	'TRAVEL NEWS',		#7
	'FINANCE'		#8
);

######
# CHANGELOG
######
#
# 1.41 - Released 27/Sep/04
#
# FIX - Fixed recursion flaw arising if connectivity to ticker was not available during auto refresh.
#        Reported by Dean Blackketter, Thanks.
#
######
#
# 1.4 - Released 01/Jul/04
#
# NEW - Ticker integration into the web interface, click on a link to view the news article!
#        Requires v 5.2 or later of Slimserver
# NEW - Can display the 'now playing' track number, progress bar and time when running either as a plugin or as a screensaver
#        The display type (progress bar / time remaining etc..) is taken from your 'now playing' settings.
# NEW - Hide the topic title from the second row of text unless running in doublesize mode.
# FIX - Improved scrolling when a remote button is held down
# CHG - Stopped bothering with a beta/final release schedule. Doesn't really seem worth it.
#
######
#
# 1.3 - Final Released 28/Jun/04
# 1.3 - Beta Released 10/May/04
#
# NEW - Can run as a 'screensaver'. Press 'play' to activate whilst using plugin or select in the web interface
# NEW - Auto Scoll - Required for screensaver but can also be used in normal use
#
######
#
# 1.2 - Final Released 26/Apr/04
# 1.2 - Beta Released 28/Mar/04
#
# NEW - Automatic refresh of news at defined interval.
# NEW - Surpresses the display of the date on each news topic.
# NEW - User definable article seperator.
# NEW - Started Changelog and To-do list.
# FIX - Safer screensaver disabling code.
#
######
#
# 1.1 - Released 11/Feb/04
#
######
# TODO
######
#
# Allow configuration of Plugin settings via the web interface
# Tidy up the variable naming. It's a mess!
#
######

# INTERNAL VARIABLES and STUFF!. Do not edit.
use Slim::Buttons::Common;
use Slim::Web::RemoteStream;
use Slim::Control::Command;
use Slim::Utils::Strings qw (string);
use Slim::Utils::Timers;
use Slim::Utils::Misc;
use Socket;
use vars qw($VERSION);
$VERSION = substr(q$Revision: 1.4.1 $,10);
my @thenews ;
my $state = "wait";
my $refresh_last = 0;
my $screensaver_timeout = 0;
my $screensaver_reset_interval = 0;
my $running_as = 'plugin';
my $htmlNews;
my $lastUpdate;
my $lastScroll;
my $lastScrollHoldTime;
my $firstHtmlRefresh = 1;
# $refresh_min is the minimum time in seconds between refreshes of the ticker from the BBC.
# Please do not lower this value. It prevents excessive queries to the BBC.
# This value is ignored when a refresh is manually requested via the remote.
my $refresh_min = 30; 

# Plugin descriptions
# If anyone would like to suggest translations, please do via email to gordonj@newswall.org.uk

sub getDisplayName() {return string('PLUGIN_BBCNEWS')}

sub strings() { return q!
PLUGIN_BBCNEWS
	EN	BBC News Ticker
	
PLUGIN_BBCNEWS_WAIT
	EN	Please wait requesting...

PLUGIN_BBCNEWS_ERROR
	EN	Failed to retrieve ticker - Press '->'

PLUGIN_BBCNEWS_SCREENSAVER
	EN	BBC News Ticker - Screensaver

PLUGIN_BBCNEWS_SCREENSAVER_ENABLE
	EN	Activating ticker as current screensaver

PLUGIN_BBCNEWS_SCREENSAVER_DISABLE
	EN	Returning to default screensaver
!};

# button functions
# These functions are run when the respective button is pressed on the remote

my %functions = (
	'left' => sub {
		#Return to previous menu
		my $client=shift;
		if ($screensaver_disable) {
			Slim::Utils::Timers::killTimers($client, \&screensaverTimerReset);
		        Slim::Utils::Timers::killTimers($client, \&autoScrollTimer);
		}
		Slim::Buttons::Common::popModeRight($client);
		return;
},
	'up' => sub {
		#move 'up' the list of news topics
		my $client = shift;
		if (&scrollLock($client)) {
			&nextTopic($client);
			$client->lines(\&lines);
			Slim::Display::Display::update($client);;
		}
		return;
},

	'down' => sub {
		#move 'down' the list of news topics
		my $client = shift;
		if (&scrollLock($client)) {
			&previousTopic($client);
			$client->lines(\&lines);
			Slim::Display::Display::update($client);;
		}
		return;
},
	'right' => sub {
		#Refresh the news
	        my $client = shift;
                my @oldlines = Slim::Display::Display::curLines($client);
		$state='wait';
		$client->lines(\&lines);
                Slim::Display::Display::update($client);
		#user requested refresh, override $refresh_min
		$refresh_last = 0;
		&retrieveNews($client);
		Slim::Display::Display::update($client);
		return;
},

	'play' => sub {
	        #press 'play' to activate screensaver
		my $client = shift;
                if (Slim::Utils::Prefs::clientGet($client,'screensaver') ne 'SCREENSAVER.bbcnews') {
                        Slim::Utils::Prefs::clientSet($client,'screensaver','SCREENSAVER.bbcnews');
                        my ($line1, $line2) = (string('PLUGIN_BBCNEWS_SCREENSAVER'), string('PLUGIN_BBCNEWS_SCREENSAVER_ENABLE'));
                        Slim::Display::Animation::showBriefly($client, $line1, $line2);
                } else {
                        Slim::Utils::Prefs::clientSet($client,'screensaver','screensaver');
                        my ($line1, $line2) = (string('PLUGIN_BBCNEWS_SCREENSAVER'), string('PLUGIN_BBCNEWS_SCREENSAVER_DISABLE'));
                        Slim::Display::Animation::showBriefly($client, $line1, $line2);
                }
		


}
);

sub nextTopic {
		my $client = shift;
                #if there are no topics left then wrap around if selected (always wrap when running as screensaver)
                if( (!@display_stack) && ( $wrap || $running_as eq 'screensaver') ) {
                        @display_stack=@display_stack_left;
                        @display_stack_left=();
                }
                #Move up the list of topics
                if(@display_stack) {
                        push @display_stack_left, ($display_current);
                        $display_current=shift @display_stack;
                }
		if ($client) {
			Slim::Display::Display::update($client);;
		}
}


sub previousTopic {
		my $client = shift;
                #if there are no topics left then wrap around if selected (always wrap when running as screensaver)
                if ( (!@display_stack_left) && ( $wrap || $running_as eq 'screensaver') ) {
                        @display_stack_left=@display_stack;
                        @display_stack=();
                }
                #Move down the list of topics
                if(@display_stack_left){
                        unshift @display_stack, ($display_current);
                        $display_current=pop @display_stack_left;
                }
		if ($client) {
			Slim::Display::Display::update($client);;
		};
}

sub getFunctions {
	#export the functions to SlimServer
	return \%functions;
}

sub retrieveNews {

	my $client = shift;

	my $now = time();

	if ( $now - $refresh_last > $refresh_min ) {
        	my $sock = Slim::Web::RemoteStream::openRemoteStream($bbcnews_location, $client);	
		if ($sock) {
			#import the news from the socket 
		        my $cur_story=1;
			my $last_story = 0;
			my $headline;
			my $start=0;
			$htmlNews="";
			@thenews=();
		        while(<$sock>) {
				if (/^STORY (.)/) {
					$cur_story=$1;
					if ($cur_story ne $last_story && $start) {
						$htmlNews .= "<!--Topic Start $cur_story-->\n";
						$htmlNews .= qq!<div class="tickerSection">!.$news_description[$cur_story]."</div>\n";

					}
				} elsif (/^HEADLINE (.*)/) {
					$headline=$1;
					if ($headline =~ /Last update at (\d{2}:\d{2})/) {
						$headline="";
						$lastUpdate=$1;
						$cur_story = 0;
						$start = 1;
						$htmlNews .= qq!<div class="tickerTitle">BBC News Ticker</div>
								<div class="tickerUpdate">Last Update $lastUpdate</div>\n
								<div class="tickerLinkFlush"><a href="/home.html" target="_self">Return to home page</a></div>\n!;
					}
					if ($headline) {
			                        $thenews[$cur_story].=$headline.$item_seperator;
					}
				} elsif (/^URL\s*http:\/\/(.*)/) {
					if ($headline ne "") {
						$htmlNews .= qq!<div class="tickerLink"><a href="http://$1" target="_new">$headline</a></div>\n!;
					}
				} elsif (/^URL\s*[^http]/) {
					if ($cur_story eq $last_story) {
						if ($headline ne "") {
							$htmlNews .= qq!<div class="tickerLink">$headline</div>\n!;
						}
					} else {
						$last_story = $cur_story;
					}
				}

			}
			$sock->close;;
			$thenews[1]="Last Update $lastUpdate$item_seperator".$thenews[1];
			$refresh_last = $now;
		};
	}
	$state='';
}

sub setMode {
	#This is executed each time the Ticker is selected from the plugins/extras menu.
	my $client = shift;
        $state= 'wait';
	$running_as = 'plugin';

        $client->lines(\&lines);
        $refresh_last = 0;
        &retrieveNews($client);


	if ($auto_scroll) {
                Slim::Utils::Timers::setTimer($client, time() + $auto_scroll, \&autoScrollTimer);
	}

	if ($screensaver_disable) {
		$screensaver_timeout=Slim::Utils::Prefs::clientGet($client,"screensavertimeout");
		$screensaver_reset_interval = $screensaver_timeout / 2;
		if ($screensaver_reset_interval < 1) { $screensaver_reset_interval = 1; };
		Slim::Utils::Timers::setTimer($client, time()+$screensaver_reset_interval, \&screensaverTimerReset);
	}
}

sub screensaverTimerReset {

	my $client = shift;
	my $now = time();

	Slim::Hardware::IR::setLastIRTime($client,$now);
	Slim::Utils::Timers::setTimer($client,$now+$screensaver_reset_interval, \&screensaverTimerReset);

}

sub autoScrollTimer {

	my $client = shift;

	&nextTopic($client);
	if ($running_as eq 'plugin' && ($auto_scroll > 0)) {
		Slim::Utils::Timers::setTimer($client, time() + $auto_scroll, \&autoScrollTimer);
	} else {
		Slim::Utils::Timers::setTimer($client, time() + $auto_scroll_screensaver, \&autoScrollTimer);
	}
}

sub scrollLock {

	my $client = shift;

	#prevents the ticker scrolling between categories too quickly when the remote button is held down

	my $now = time();
	my $holdTime = Slim::Hardware::IR::holdTime($client);
	my $doScroll = 0;

	if ($holdTime == 0 || ($holdTime - $lastScrollHoldTime > $scrollSpeed)) {
		$doScroll = 1;
	        $lastScrollHoldTime = $holdTime;
	}

	return $doScroll;
}
		

sub lines {
	#This returns the 2 lines to display on the unit 
	my $client = shift;
	my ($line1, $line2);
	my $now = time();

	if ( $refresh_interval && ( $now - $refresh_last > $refresh_interval ) ) {
		&retrieveNews($client);
	}

        $line1 = $news_description[$display_current];

	if ($state eq 'wait') {
		$line2 = string('PLUGIN_BBCNEWS_WAIT');
	} 
	elsif (exists($thenews[$display_current])) {
                $line2 = $thenews[$display_current];

		if ($date_hide && ($line2 =~ /(.*)\s*[^\d]\d{1,2}\s[a-zA-Z]*\s\d{4}(.*)/) ) {
			$line2=$1.$2;
		}
		if (!Slim::Utils::Prefs::clientGet($client,'doublesize')) {
			#Hide the category title from the ticker when running in 'normal mode'
			#It is not necessary to display it unless in doublesize mode as it can be seen on the line above
			if ($line2 =~ /^(Last Update\s*\d*:\d*$item_seperator)?[A-Z\s-]*\s*$item_seperator\s*(.*)/) {
				if ($1) {
					$line2= $1.$2;
				} else {
					$line2 = $2;
				}
			}
		}
        } else {
                $line2 = string('PLUGIN_BBCNEWS_ERROR');
        }
	
	my $playlistlen = Slim::Player::Playlist::count($client);

	if ( ( ($nowPlayingOverlay  && $running_as eq 'screensaver') || ($nowPlayingOverlay > 1) ) && $playlistlen > 0) {

                $line1 .= sprintf(
                      " (Track %d %s %d) ",
                                Slim::Player::Source::currentSongIndex($client) + 1, string('OUT_OF'), $playlistlen
                        );


		($line1,my $overlay1) = Slim::Buttons::Playlist::nowPlayingModeLines($client,$line1,'');

	} else {
		$line1 = "BBC News - ".$line1;
	}

	return ($line1, $line2);
}	

sub screenSaver() {
        Slim::Utils::Strings::addStrings(&strings());
        Slim::Buttons::Common::addSaver('SCREENSAVER.bbcnews', getScreensaverBbcNews(), \&setScreensaverBbcNewsMode,\&leaveScreenSaverBbcNews,string('PLUGIN_BBCNEWS_SCREENSAVER'));
}

my %screensaverBbcNewsFunctions = (
        'done' => sub  {
		my ($client, $funct, $functarg) = @_;
       		Slim::Buttons::Common::popMode($client);
		$client->update();
		#pass along ir code to new mode if requested
		if (defined $functarg && $functarg eq 'passback') {
			Slim::Hardware::IR::resendButton($client);
		}
	}
);

sub getScreensaverBbcNews {
        return \%screensaverBbcNewsFunctions;
}

sub setScreensaverBbcNewsMode() {

        my $client = shift;
	
        $state= 'wait';
        $running_as = 'screensaver';                                                                                                                                                               
	Slim::Utils::Timers::setTimer($client, time() + $auto_scroll_screensaver, \&autoScrollTimer);

	$client->lines(\&lines);
        $refresh_last = 0;
        &retrieveNews($client);

}

sub leaveScreenSaverBbcNews {

	#kill timers
	my $client = shift;
	Slim::Utils::Timers::killTimers($client, \&autoScrollTimer);

}

sub webPages {

	my %pages = (
		".*" => \&htmlIndex,
	);
	
	return (\%pages, "index.html");
}

sub htmlIndex {

	my ($client, $params) = @_;

        &retrieveNews($client);

	my $htmlRefreshInterval;

	if ($firstHtmlRefresh) {

		#This ensures the web page is refresh shortly after ticker data is refreshed from the BBC
	
		$htmlRefreshInterval = $refresh_interval + 5;
		$firstHtmlRefresh = 0;
	
	} else {
		
		$htmlRefreshInterval = $refresh_interval;

	}

        my $layout=qq^
<html>
<head>
<META HTTP-EQUIV="refresh" content="$htmlRefreshInterval">
<title>Bbc News Ticker</title>
<style type="text/css"> <!--
BODY, TABLE, INPUT, SELECT, P, li, a { color: #444; font-size: 10px; font-family: Verdana, Arial, Helvetica, sans-serif  }

BODY {margin-left: 20px}

.tickerTitle { font-size: 12px; font-weight:bold; padding-top: 10px;}
.tickerSection { font-weight:bold; padding-bottom: 5px; padding-top: 10px; margin-left: 15px}
.tickerLink { margin-left: 30px }
.tickerLinkFlush {}
a { text-decoration:none; }

-->
</style>
</head>
<body>$htmlNews
</body>
</html>
^;
	
	return \$layout;

};


1;

__END__
