#
#	$Id: MaximumEdgeNews.pm,v 1.20 2004/12/30 02:48:21 kevin Exp $
#
#	Author: Kevin Walsh <kevin@cursor.biz>
#
#	Copyright (c) 2003-2004 Cursor Software Limited.
#	All rights reserved.
#
#	----------------------------------------------------------------------
#
#	"Maximum Edge News" plugin plug-in for the SLIMP3 server.
#
#	Requires the "maximumedge_update.pl" to update the news text file on
#	a regular basis, unless you decide to configure live updates.
#	Note that live updates could stop the music on all players while the
#	update is in progress - depending upon your Internet connection
#	speed, of course.
#
#	The maximumedge_update.pl script should be configured to create/update
#	the maximumedgenews.txt file in the SLIMP3 server user's home
#	directory, which is the same directory as the .slimp3.pref file.
#	You don't need to do this if you configure this module to perform
#	live lookups.
#
#	Scroll through the news categories using the up/down buttons.
#	The 'add' (rec) key will re-read the news from the data source.
#	The news will be re-read every now and again so the 'add' key is
#	largely unnecessary.
#
#	----------------------------------------------------------------------
#
#	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
#
package Plugins::MaximumEdgeNews;
use File::Spec::Functions qw(catfile);
use Slim::Utils::Strings qw(string);
use strict;

#
#	The MaximumEdge news comes in several categories
#	put these in whatever order you prefer
#
my @news_description = (
    [ 'top',		'HEADLINES' ],		#0
    [ 'entertainment',	'ENTERTAINMENT' ],	#1
    [ 'business',	'BUSINESS' ],		#2
    [ 'sports',		'SPORTS' ],		#3
    [ 'health',		'HEALTHCARE' ],		#4
);

#
#	specify the catagories you want and their display order
#	(see the @news_description array, below)
#
my @display_order = (0,2,3);

#
#	specify the file/inet data sources here
#
my @locations = (
    'http://www.maximumedge.com/cgi/news/',
    catfile(Slim::Utils::Prefs::preferencesPath(),'maximumedgenews.txt'),
);

#
#	end of configurable variables
#

use vars qw($VERSION);
$VERSION = substr(q$Revision: 1.20 $,10);

my %prefs = (
    'location' => {
	order => 0,
	default => 0,
	translate => 1,
	widget => {
	    options => {
		0 => 'LIVEDATA',
		1 => 'TEXTFILE',
	    },
	},
    },
    'checktime' => {
	order => 1,
	default => 60,
	widget => {
	    validate => \&Slim::Web::Setup::validateInt,
	    validateArgs => [1,1440,1,1440],
	},
    },
);

my $last_check;
my $last_update;
my @news_list = ();
my %context;

my %functions = (
    'left' => sub {
	Slim::Buttons::Common::popModeRight(shift);
    },
    'right' => sub {
	Slim::Display::Animation::bumpRight(shift);
    },
    'up' => sub {
	my $client = shift;

	$context{$client} = Slim::Buttons::Common::scroll(
	    $client,
	    -1,
	    $#display_order + 1,
	    $context{$client} || 0,
	);
	$client->update();
    },
    'down' => sub {
	my $client = shift;

	$context{$client} = Slim::Buttons::Common::scroll(
	    $client,
	    1,
	    $#display_order + 1,
	    $context{$client} || 0,
	);
	$client->update();
    },
    'add' => sub {
	#
	#	refresh the news from the text file
	#
	#	note that the [add] key is labeled as [rec] on the Sony remotes
	#
	my $client = shift;

	Slim::Display::Animation::showBriefly(
	    $client,
	    string('PLUGIN_MAXIMUMEDGENEWS_MODULE_NAME') .
	    ' ' .
	    string('PLUGIN_MAXIMUMEDGENEWS_UPDATING')
	);
	$last_update = $last_check = undef;
    },
);

#
#	lines()
#	-------
#	Create and return the two-line display.  Also "fake" the IR time so
#	that the "screen saver" doesn't take over.
#
sub lines
{
    my $client = shift;
    my @lines;

    load_preferences();

    #
    #	"Fake" the last remote control keypress time so the "screen saver"
    #	doesn't take over
    #
    Slim::Hardware::IR::setLastIRTime(
	$client,
	Time::HiRes::time() + (Slim::Utils::Prefs::get("screensavertimeout") * 5),
    );

    #
    #	re-read the news every now and again
    #
    if (!$last_check || (time() - $last_check) > ($prefs{checktime}->{current} * 60)) {
	$last_check = time();
	my $mtime = (stat($locations[$prefs{location}->{current}]))[9] || 0;

	if ($locations[$prefs{location}->{current}] =~ m|^http://|i) {
	    #
	    #	update the news from the internet on a regular basis
	    #
	    update_news($client);
	}
	else {
	    #
	    #	update the news from the text file only if the file has been
	    #	modified since we last read it
	    #
	    if (!$last_update || $mtime > $last_update) {
		$last_update = $mtime;
		update_news($client) if $mtime;
	    }
	}
    }

    $context{$client} ||= 0;

    my $title = string(
	'PLUGIN_MAXIMUMEDGENEWS_' .
	$news_description[$display_order[$context{$client}]]->[1]
    );
    $lines[0] = (
	string('PLUGIN_MAXIMUMEDGENEWS_MODULE_NAME') .
	' (' .
	(($context{$client} || 0) + 1) .
	' ' .
	string('OF') .
	' ' .
	($#display_order + 1) .
	") $title"
    );

    if (exists($news_list[$context{$client}])) {
	$lines[1] = $news_list[$context{$client}];
    }
    else {
	$lines[1] = string('PLUGIN_MAXIMUMEDGENEWS_NO_NEWS');
    }
    $lines[2] = $lines[3] = undef;
    @lines;
}	

#
#	update_news()
#	-------------
#	Overwrites the @news_list with data collected from either the
#	remote HTTP URI or a text file, depending upon what is configured
#	into the $prefs{location}->{current} preference.
#
sub update_news
{
    my $client = shift;
    my $divider = Slim::Hardware::VFD::symbol('rightarrow') x 2;
    my $cur_story = 0;
    my $cur_desc = '';
    my @text = ();

    if ($locations[$prefs{location}->{current}] =~ m|^http://|i) {
	#
	#	the location is a HTTP URI so open a socket to the remote
	#	webpage and collect the data
	#
	$locations[$prefs{location}->{current}] =~ s|/*$|/|;
	foreach my $ref (@news_description[@display_order]) {
	    my $sock = Slim::Web::RemoteStream::openRemoteStream("$locations[$prefs{location}->{current}]$ref->[0].txt",$client) or return undef;
	    push(@text,"$ref->[0]|$_") while (<$sock>);
	    $sock->close();
	}
    }
    else {
	#
	#	the location is not a HTTP URI so strip any leading
	#	protocol specification, such as "file://" from the location
	#	and treat the resulting string as a path to a text file
	#
	$locations[$prefs{location}->{current}] =~ s|^\w+://||i;
	open(FILE,$locations[$prefs{location}->{current}]) or return undef;
	push(@text,$_) while (<FILE>);
	close FILE;
    }

    @news_list = ();
    my %index;

    foreach (0 .. $#display_order) {
	$index{$news_description[$display_order[$_]]->[0]} = $_;
	$news_list[$_] = '';
    }

    #
    #	parse the data gathered in one of the above collection operations
    #	to create the @news_list array
    #
    foreach (@text) {
	chomp;

    	my ($name,undef,$text,undef) = split('\|',$_);
	next unless ($text && defined($index{$name}));

	# $text = decode_entities($_);
	$text =~ s/&nbsp;/ /g;
	$text =~ s/&quot;/"/g;
	$text =~ s/&.*?;//g;

        $text =~ s/ ; /: /g;
        $text =~ s/\240/ /g;
        $text =~ s/^\s+//;
        $text =~ s/\s+$//;
	$text =~ s/^AP\s+//;
        $text =~ s/\s\s+/ /g;
        $text =~ s/ p\.m\./pm/ig;
        $text =~ s/ a\.m\./am/ig;
        $text =~ s/([.:?])\s+([A-Z])/$1  $2/g;
        $text =~ s/`/'/g;
	$news_list[$index{$name}] .= "$text $divider ";
    }

    #
    #	go through the @news_list removing unnecessary whitespace and
    #	appending a long gap to the end of the news item
    #
    foreach (@news_list) {
	s/\s\s+/ /g;
	s/\s+$divider\s+$/                              /;
    }
    undef;
}

#
#	load_preferences()
#	------------------
#	Load the current preferences or set defaults
#
sub load_preferences
{
    while (my ($key,$val) = each %prefs) {
	my $name = __PACKAGE__;
	$name =~ s/^.*:://;
	$key = 'plugin_' . lc($name) . "_$key";

	if (Slim::Utils::Prefs::isDefined($key)) {
	    $val->{current} = Slim::Utils::Prefs::get($key);
	}
	else {
	    $val->{current} = $val->{default};
	    Slim::Utils::Prefs::set($key,$val->{default});
	}
    }
}

sub setupGroup
{
    my $name = __PACKAGE__;
    $name =~ s/^.*:://;
    $name = 'plugin_' . lc($name);

    load_preferences();

    my @order;
    push(@order,"${name}_$_") for (sort {$prefs{$a}->{order} <=> $prefs{$b}->{order}} keys %prefs);

    my $version = ' (' . string("${name}_VERSION") . ')';
    $version =~ s/%s/$VERSION/;
    $version =~ s/\s+\)/)/;

    my %group = (
	PrefOrder => \@order,
	GroupHead => string("${name}_MODULE_NAME") . $version,
	GroupDesc => string("${name}_MODULE_DESCRIPTION"),
	GroupLine => 1,
	GroupSub => 1,
	Suppress_PrefSub => 1,
	Suppress_PrefLine => 1,
    );

    my %widgets;
    while (my ($key,$val) = each %prefs) {
	$widgets{"${name}_$key"} = $val->{widget};

	if ($val->{translate} && exists($val->{widget}->{options})) {
	    $_ = string("${name}_$_") for (values %{$val->{widget}->{options}});
	    delete $val->{translate};
	}
    }

    return (\%group,\%widgets);
}

sub setMode
{
    my $client = shift;

    $client->lines(\&lines);
    $client->update();
}

sub getFunctions
{
    \%functions;
}

sub getDisplayName
{
    my $name = 'PLUGIN_MAXIMUMEDGENEWS_MODULE_NAME';

    $::VERSION =~ /^(\d+)/;
    return ($1 >= 6) ? $name : string($name);
}

sub strings
{
    local $/ = undef;
    <DATA>;
}

1;

__DATA__

PLUGIN_MAXIMUMEDGENEWS_MODULE_NAME
	EN	MaximumEdge News

PLUGIN_MAXIMUMEDGENEWS_MODULE_DESCRIPTION
	EN	Regurlarly updated news feed from maximumedge.com

PLUGIN_MAXIMUMEDGENEWS_VERSION
	EN	Version %s

PLUGIN_MAXIMUMEDGENEWS_UPDATING
	EN	updating...

PLUGIN_MAXIMUMEDGENEWS_NO_NEWS
	EN	No news at this time.  Press ADD to retry

PLUGIN_MAXIMUMEDGENEWS_HEADLINES
	EN	Headlines

PLUGIN_MAXIMUMEDGENEWS_ENTERTAINMENT
	EN	Entertainment

PLUGIN_MAXIMUMEDGENEWS_BUSINESS
	EN	Business

PLUGIN_MAXIMUMEDGENEWS_SPORTS
	EN	Sports

PLUGIN_MAXIMUMEDGENEWS_HEALTHCARE
	EN	Healthcare

PLUGIN_MAXIMUMEDGENEWS_LIVEDATA
	EN	Read data directly from www.maximumedge.com

PLUGIN_MAXIMUMEDGENEWS_TEXTFILE
	EN	Read data from a text file (maximumedgenews.txt)

SETUP_PLUGIN_MAXIMUMEDGENEWS_CHECKTIME
	EN	Re-read the news every now and again (minutes)

SETUP_PLUGIN_MAXIMUMEDGENEWS_LOCATION
	EN	News feed location


