#!/usr/bin/perl -w

=pod

=head1 NAME

tv_grab_ru_sat - Grab TV listings for Russian satellite channels.

=head1 SYNOPSIS

tv_grab_ru_sat --help

tv_grab_ru_sat [--config-file FILE] --configure [--gui OPTION]

tv_grab_ru_sat [--config-file FILE] [--output FILE] [--days N]
           [--offset N] [--quiet]

tv_grab_ru_sat --list-channels

=head1 DESCRIPTION

Output TV listings for several Russian satellite channels.
The grabber relies on availability of a ready-made XMLTV 
data file in the Internet so it might stop working at any time.
The best time to run this script is probably on Sunday evening.

First run B<tv_grab_ru_sat --configure> to choose, which channels you want
to download. Then running B<tv_grab_ru_sat> with no arguments will output
listings in XML format to standard output.

B<--configure> Prompt for which channels,
and write the configuration file.

B<--config-file FILE> Set the name of the configuration file, the
default is B<~/.xmltv/tv_grab_ru_sat.conf>.  This is the file written by
B<--configure> and read when grabbing.

B<--gui OPTION> Use this option to enable a graphical interface to be used.
OPTION may be 'Tk', or left blank for the best available choice.
Additional allowed values of OPTION are 'Term' for normal terminal output
(default) and 'TermNoProgressBar' to disable the use of XMLTV::ProgressBar.

B<--output FILE> write to FILE rather than standard output.

B<--days N> grab N days.  The default is 7. This has little meaning because 
the data seems to be refreshed once a week on Sundays and always covers 7 days. 
Any choice will either remove some information or have no effect.

B<--offset N> start N days in the future.  The default is to start
from today.

B<--quiet> suppress the progress messages normally written to standard
error.

B<--help> print a help message and exit.

=head1 SEE ALSO

L<xmltv(5)>.

=head1 AUTHOR

Vyacheslav Dikonov, sdiconov@mail.ru

=head1 BUGS

=cut

######################################################################
# Initialisation

use strict;
use XMLTV;
use XMLTV::Get_nice;

# Options
use Getopt::Long;
use XMLTV::Ask;
use XMLTV::Mode;
use XMLTV::Config_file;
# get_channels
use XMLTV::ProgressBar;
use Date::Manip;
use XMLTV::Date;



use XMLTV::Usage <<END
$0: get Russian satellite television listings in XMLTV format
To configure: $0 --configure [--config-file FILE]
To grab listings: $0 [--config-file FILE] [--output FILE] [--days N]
        [--offset N] [--quiet]
To list channels: $0 --list-channels
END
  ;

######################################################################
# get options

my ($opt_days, $opt_offset, $opt_help, $opt_output,
    $opt_configure, $opt_config_file, $opt_gui,
    $opt_quiet, $opt_list_channels);
$opt_days  = 7; # default
$opt_offset = 0; # default
$opt_quiet  = 0; # default
GetOptions('days=i'        => \$opt_days,
	   'offset=i'      => \$opt_offset,
	   'help'          => \$opt_help,
	   'configure'     => \$opt_configure,
	   'config-file=s' => \$opt_config_file,
       'gui:s'         => \$opt_gui,
	   'output=s'      => \$opt_output,
	   'quiet'         => \$opt_quiet,
	   'list-channels' => \$opt_list_channels
	  )
  or usage(0);
die 'number of days must not be negative'
  if (defined $opt_days && $opt_days < 0);
usage(1) if $opt_help;

XMLTV::Ask::init($opt_gui);

my $mode = XMLTV::Mode::mode('grab', # default
			     $opt_configure => 'configure',
			     $opt_list_channels => 'list-channels',
			    );

######################################################################
# Set global variables

my $url_datafile = "http://www.free-x.de/vdr/vsetv.xml";
$XMLTV::Get_nice::Delay = 0; # No need to be nice.
# File that stores which channels to download.
my $config_file = XMLTV::Config_file::filename($opt_config_file, 'tv_grab_ru_sat', $opt_quiet);


######################################################################
# Configure

if ($mode eq 'configure') {
    XMLTV::Config_file::check_no_overwrite($config_file);
    # get the channels data.
    my %channels = get_channels(); 

    # write configuration
    if ($mode eq 'configure') {
	open(CONF, ">$config_file") or die "cannot write to $config_file: $!";

	# Ask about each channel.
	my @chs = sort keys %channels;
	my @names = map { "$channels{$_}" } @chs;
	my @want = ask_many_boolean(1, @names);
	foreach (@chs) {
	    my $w = shift @want;
	    warn("cannot read input, stopping channel questions"), last
	      if not defined $w;
	    print CONF '#' if not $w;
	    my $name = shift @names;
	    print CONF "channel $_ $name\n";
	};

	close CONF or warn "cannot close $config_file: $!";
        say("Finished configuration.");

        exit();
    }
}

######################################################################
# List channels
elsif ($mode eq 'list-channels') {
    my %channels = get_channels(); 
    
    my $writer = new XMLTV::Writer();
    $writer->start({ 'generator-info-name' => 'tv_grab_ru_sat' });
    foreach (keys(%channels)) {
        $writer->write_channel({id => $_, 'display-name' => [ [ $channels{$_}, 'ru' ] ]});
    };
    $writer->end();

    exit();
}

######################################################################
# Grab

elsif ($mode eq 'grab') {
    my @config_lines = XMLTV::Config_file::read_lines($config_file);
    my %channels;
    
    # Read configuration
    my $line_num = 1;
    foreach (@config_lines) {
        ++ $line_num;
        next if not defined;
        if (/^channel:?\s+(\S+)\s+([^\#]+)/) {
	    my $ch_did = $1;
	    my $ch_name = $2;
	    $ch_name =~ s/\s*$//;
	    $channels{$ch_did} = $ch_name;
	}
	else {
	    warn "$config_file:$line_num: bad line\n";
        }
    };

    die "No channels specified, run me with --configure\n" if not keys %channels;

    my $data = XMLTV::parse(get_datafile()); 

    # Set time limits
    my $now = DateCalc(parse_date('now'), "$opt_offset days");
    my $timefiltstart = $now;
    if ($opt_offset) { $timefiltstart = DateCalc($now,"+ $opt_offset days") };
    my $timefiltend = DateCalc($timefiltstart,"+ $opt_days days");

    # Process and output
    my ($encoding, $credits, $ch, $progs) = @$data;

    my $writer = new XMLTV::Writer(encoding => $encoding);
    $writer->start({ 	'source-info-url' => "?",
			'source-data-url' => $url_datafile,
			'generator-info-name' => "XMLTV",
			'generator-info-url' => "http://membled.com/work/apps/xmltv/" });


    foreach (values %$ch) {
	if ( exists($channels{$_->{id}}) ) { # Only wanted channels
    	    $writer->write_channel($_);
	}
    };
    
    
    foreach (@$progs) {
	if ( exists($channels{$_->{channel}}) ) { # Only wanted channels
	    my $progtime = ParseDate($_->{start});
	    my $pastflag = Date_Cmp($progtime, $timefiltstart);
	    my $futureflag = Date_Cmp($progtime, $timefiltend);
	    
	    if ( $pastflag >= 0 and $futureflag <= 0 ) { # Only falling into the specified timegap
		$writer->write_programme($_);
	    }
        }
    };
    
    $writer->end();

exit();
}
else { die }

# END

######################################################
# Service routines

# Download
sub get_datafile { 
    my $sitedata = get_nice($url_datafile);
    die "could not get index page $url_datafile, aborting\n" if not defined $sitedata;
    return $sitedata;
}

# get channel listing
sub get_channels {
    my $bar = new XMLTV::ProgressBar('getting list of channels', 1)
	if not $opt_quiet;
    my %channels;
    my $data = XMLTV::parse(get_datafile()); 
    my ($encoding, $credits, $ch, $progs) = @$data;
    foreach (values %$ch) {
	$channels{$_->{id}}=$_->{'display-name'}[0][0]; 
	# Lang tags are not used, get channel_id - channel_name pairs
    };
    die "no channels could be found" if not keys %channels;
    update $bar if not $opt_quiet;
    $bar->finish() if not $opt_quiet;
    return %channels;
};
