#!/usr/bin/perl -w

eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
    if 0; # not running under some shell

=pod

=head1 NAME

tv_augment - Augment XMLTV listings files with automatic and user-defined rules.

=head1 SYNOPSIS

tv_augment [--rule <file>] [--config <file>]
           [--input <file>] [--output <file>]
           [--log <file>] [--nostats]
           [--debug <level>]

tv_augment [-r <file>] [-c <file>]
           [-i <file>] [-o <file>]
           [-l <file>] [-n]
           [-d <level>]

=head1 DESCRIPTION

Augment an XMLTV xml file by applying corrections ("fixups") to programmes
matching defined criteria ("rules").

Two types of rules are actioned: (i) automatic, (ii) user-defined.

Automatic rules use pre-programmed input and output to modify the input
programmes. E.g. removing a "title" where it is repeated in a "sub-title"
(e.g. "Horizon" / "Horizon: Star Wars"), or trying to identify and extract
series/episode numbers from the programme title, sub-title or description.

User-defined rules use the content of a "rules" file which allows programmes
matching certain user-defined criteria to be corrected/enhanced with the user
data supplied (e.g. adding/changing categories for all episodes of "Horizon",
or fixing misspellings in titles, etc.)

(see "perldoc XMLTV::Augment" for more details)

B<--input FILE> read from FILE rather than standard input.

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

B<--rule FILE> file containing the user-defined rules.

B<--config FILE> configuration file containing a list of which rules
you want to run.

B<--nostats> do not print the summary log of actions performed, or list of
suggested fixups.

B<--log FILE> output the stats to this FILE.

B<--debug LEVEL> print debug info to STDERR (debug level > 3 is not
likely to be of much use (it generates a lot of output))

=head1 SEE ALSO

L<xmltv(5)>

=head1 AUTHOR

Geoff Westcott, honir.at.gmail.dot.com, Dec. 2014.

=cut


use strict;
use XMLTV;
use XMLTV::Version "$XMLTV::VERSION";
use Data::Dumper;
use Getopt::Long;

use XMLTV::Data::Recursive::Encode;

# simplify testing by also looking for package in current directory
eval 'use XMLTV::Augment';
  if ($@ ne '') { eval 'use Augment'; }


use XMLTV::Usage <<END
$0: Augment programme listings with automatic and user-defined rules
$0 [--rule <file>] [--config <file>] [--input <file>] [--output <file>] [--nostats] [--log <file>] [--debug (1-10)]
$0 [-r <file>] [-c <file>] [-i <file>] [-o <file>] [-n] [-l <file>] [-d (1-10)]
END
;


my (
	$opt_help,
    $opt_input,
    $opt_output,
    $opt_rule,
    $opt_config,
	$opt_nostats,
	$opt_log,
    $opt_debug,
	$opt_do,
   );

GetOptions(
		'h|help'          => \$opt_help,
		'i|input=s'       => \$opt_input,
		'o|output=s'      => \$opt_output,
		'r|rule=s'        => \$opt_rule,
		'c|config|config-file=s'      => \$opt_config,
		'n|nostats'       => \$opt_nostats,
		'l|log:s'         => \$opt_log,
		'd|debug:i'       => \$opt_debug,
		'do:i'            => \$opt_do,
     ) or usage(0);

usage(1) if $opt_help;
#rule is now optional if using Supplement via config
#   usage(0) if !$opt_rule;

$opt_input     = '-'   if ( !defined($opt_input) );
#$opt_output    = 'STDOUT'   if ( !defined($opt_output) );
$opt_debug     = 0    if ( !defined($opt_debug) );
my $opt_stats  = ( !defined($opt_nostats) ? 1 : !$opt_nostats );


# object construction & open log file
my $augment = new XMLTV::Augment(
			'rule'       => $opt_rule,
			'config'     => $opt_config,
			'debug'      => $opt_debug,
			'stats'      => $opt_stats,
			'log'        => $opt_log,
			)
	|| eval { print STDERR "Failed to create XMLTV::Augment object \n"; exit 1; };


my %w_args = ();
if (defined $opt_output) {
    my $fh = new IO::File ">$opt_output";
    die "cannot write to $opt_output\n" if not $fh;
    %w_args = (OUTPUT => $fh);
}


# our XMLTV::Writer object
my $w;

# store the input file's encoding
my $encoding;

# count of input records
my $in_count = 0;

# parsefiles_callback needs an array
my @files = ( $opt_input );

XMLTV::parsefiles_callback(\&encoding_cb, \&credits_cb, \&channel_cb, \&programme_cb, @files);

# note: we only get a Writer if the encoding callback gets called
if ( $w ) {
    $w->end();
}

# log the stats
$augment->printInfo();

# close the log file
$augment->end();

exit(0);

# callbacks used by parsefiles_callback
#
sub encoding_cb( $ ) {
    die if defined $w;
    $encoding = shift;    # callback returns the file's encoding
    $w = new XMLTV::Writer(%w_args, encoding => $encoding);
	$augment->setEncoding($encoding);
}
#
sub credits_cb( $ ) {
    $w->start(shift);
}
#
sub channel_cb( $ ) {
    my $ch = shift;

	# store the channel details
    $augment->inputChannel( $ch );

	# write the channel element to the output xml
	$w->write_channel($ch);
}
#
sub programme_cb( $ ) {
    my $prog = shift;
    $in_count++;

	# developer's option to only process a few records in input file and then stop
	if ( defined($opt_do) && $in_count > $opt_do ) { return; }

	# decode the incoming programme
	$prog = XMLTV::Data::Recursive::Encode->decode($encoding, $prog);

    # augmentProgramme will now do any requested processing of the input xml
    $prog = $augment->augmentProgramme( $prog );

	# re-code the modified programme back to original encoding
    $prog = XMLTV::Data::Recursive::Encode->encode($encoding, $prog);

    # output the augmented programme
    $w->write_programme($prog);

}
#
