#!/usr/bin/perl -s

use strict;
use warnings;
use encoding "utf-8";
use Encode;
use XML::XPath;
use XML::XPath::XMLParser;
use DateTime;
use LWP::Simple;
use XMLTV;

my @elements = qw(schedule_id
                  program_title
                  sub_titles
                  program_date_time
                  program_description
                  episode
                  rating
                  genre
                  series
                  new
                  category_id
                  default_video_link
                  short_name);

my %channels = (bbchd => {id => 27,
			  xmltvid => "hd.bbc.co.uk",
			  displayname => "BBC HD"},
		bbcentertainment => {id => 26,
				     xmltvid => "entertainment.bbc.co.uk",
				     displayname => "BBC Entertainment"},
		bbcknowledge => {id => 24,
				 xmltvid => "knowledge.bbc.co.uk",
				 displayname => "BBC Knowledge"},
		bbclifestyle => {id => 25,
				 xmltvid => "lifestyle.bbc.co.uk",
				 displayname => "BBC Lifestyle"}
    );

our $days;

my @dates = calc_days($days || 3);
my @channels = @ARGV;

if (@channels == 0) {@channels = keys %channels}

my @all_programs = ();

for my $ch (@channels) {
    for my $date (@dates) {
	my $url = sprintf("http://scandinavia.bbcentertainment.com/ajax/data/%d/%s.xml",
			  $channels{$ch}->{id},
			  $date);
	my $document = get $url;
	$document = decode("utf-8", $document);

	print STDERR "Fetching programs for $ch on $date\n";
	parse_programs($document, $channels{$ch}, \@all_programs);
    }
}

write_document(\@all_programs);

sub write_document {
    my $programs = shift;
    my $w = new XMLTV::Writer();
    $w->comment("Hello from XML::Writer's comment() method");
    $w->start({'generator-info-name' => 'BBC Channels' });
    for my $ch (values %channels) {
	$w->write_channel({
	    id             => $ch->{xmltvid},
	    "display-name" => [[$ch->{displayname}, "en"]]
			  });
    }

    for (my $i=0; $i<@$programs; $i++) {
	my $p  = $programs->[$i];
	my $np = $programs->[$i+1];
	if (defined $np) {
	    $p->{stop} = $np->{start};
	}
	$w->write_programme($p);
    }
    $w->end();
}


sub to_xmltv_hash {
    my ($p) = @_;
    my $dt  = parse_time($p->{program_date_time});

    my %program = (
	channel => $p->{channel}->{xmltvid},
	start   => format_xmltv_time($dt),
	desc    => [[$p->{program_description}, "en"]]
	);

    $program{title} = [[$p->{program_title}, "en"]];

    if (defined $p->{episode} && $p->{episode} ne "") {
	if (not defined $p->{series} || $p->{series} eq "") {
	    $p->{series} = 1
	}
	$program{"episode-num"} = [[sprintf("%d.%d.%d", 
					    $p->{series}-1, 
					    $p->{episode}-1, 
					    0),
				    "xmltv_ns"]];
    }
    
    return \%program;
}

sub parse_programs {
    my ($document, $channel, $programs) = @_;
    my $xp = XML::XPath->new(xml => $document);

    my $nodeset = $xp->find('/xml/row'); # find all programs

    foreach my $node ($nodeset->get_nodelist) {
	my %program = (channel => $channel);

	for my $element (@elements) {
	    $program{$element} = stringnode($node, $element);
	}


	push @$programs, to_xmltv_hash(\%program);
    }
}

sub calc_days {
    my $num_days = shift;
    my $day = DateTime->today();
    my @dates;
    for (1..$num_days) {
	push @dates, $day->ymd("");
	$day = $day->add(days => 1);
    }
    return @dates;
}

sub stringnode {
    my ($node, $element) = @_;
    return eval{($node->find($element)->get_nodelist)[0]->string_value};
}

sub parse_time {
    my $timestring = shift;
    my ($month, $day, $year, $h, $m, $s) 
	= $timestring =~ m|(\d\d)/(\d\d)/(\d\d\d\d) (\d\d):(\d\d):(\d\d)|;
    my $dt = DateTime->new(year   => $year,
			   month  => $month,
			   day    => $day,
			   hour   => $h,
			   minute => $m,
			   second => $s,
			   time_zone => 'Europe/London');
    $dt->set_time_zone("Europe/Oslo");
    return $dt;

}

sub format_xmltv_time {
    return $_[0]->strftime("%Y%m%d%H%M%S %z");
}

