#!/usr/bin/perl -w

=head1 NAME

octo_reporter - Octopussy Reporter program

=head1 SYNOPSIS

octo_reporter --report <report> --device <device> --service <service>
  --taxonomy <taxonomy> --begin YYYYMMDDHHMM --end YYYYMMDDHHMM
  [ --pid_param <string> ] --output <output_file>

Mail options:
  --mail_subject <subject> --mail_recipients <recipients>
 	
FTP options:
  --ftp_host <host> --ftp_dir <dir>
  --ftp_user <user> --ftp_password <password>

SCP options: (SSH Public Key required)
  --scp_host <host> --scp_dir <dir> --scp_user <user>

=head1 DESCRIPTION

octo_reporter is the program used by the Octopussy Project to generate Reports

=cut

use strict;
no strict 'refs';
use Cache::SharedMemoryCache;
use Getopt::Long;
Getopt::Long::Configure('bundling');

use Octopussy;

my $APPLI = "Octopussy";
my $PROG_NAME = "octo_reporter";
my $PROG_VERSION = Octopussy::Version();
my $FILE_DB = "/var/run/octopussy/file.db";

my $dir_pid = Octopussy::Directory("running");
my $pid_file = undef; 

my $help;
my (@devices, @services) = ((), ());
my ($report, $rc, $taxonomy, $begin, $end, $pid_param, $x, $y, $lang);
my ($mail_subject, $mail_recipients);
my ($ftp_host, $ftp_dir, $ftp_user, $ftp_pwd);
my ($scp_host, $scp_dir, $scp_user, $scp_pwd);
my ($type, $title) = ("bars", "test");
my $output = undef;
my $shared_memory_cache = undef;

=head1 FUNCTIONS

=head2 Help()

Prints Help

=cut
sub Help()
{
	my $help_str = <<EOF;

$PROG_NAME (version $PROG_VERSION)

 Usage: $PROG_NAME --report <report>
          --device <device> --service <service> --taxonomy <taxonomy>
          --begin YYYYMMDDHHMM --end YYYYMMDDHHMM
          [ --pid_param <string> ] --output <output_file>
 Mail options:
   --mail_subject <subject> --mail_recipients <recipients>
 Ftp options:
   --ftp_host <host> --ftp_dir <dir>
   --ftp_user <user> --ftp_password <password>
 Scp options:
   --scp_host <host> --scp_dir <dir> --scp_user <user>

EOF

	print $help_str;

	if (!defined $report)
  { 
		my @reps = Octopussy::Report::List(undef, undef);
		print " Report list: " . join(", ", @reps) . "\n"; 
	}
	else
	{	
		$rc = Octopussy::Report::Configuration($report);
		if (!defined $rc)
		{
			print "Error: Unable to get configuration for Report [$report]\n";
			exit();
		}
		my ($dev_groups, $devs, $servs) = 
			Octopussy::Table::Devices_and_Services_With($rc->{table});
		if ((! @devices) && (defined $devs))
    	{ print " Device list: " . join(", ", @{$devs}) . "\n"; }
  	elsif ((! @services) && (defined $servs))
    	{ print " Service list: " . join (", ", sort @{$servs}) . "\n"; }
  	elsif (!defined $taxonomy)
  	{
    	my @taxo_list = Octopussy::Taxonomy::List_And_Any();
    	print " Taxonomy list: " . join (", ", sort @taxo_list) . "\n";
  	}
	}	
	print "\n";

	exit();
}

=head2 Progress($msg, $num)

Sets progress status

=cut
sub Progress
{
  my ($msg, $num) = @_;

	$shared_memory_cache->set("status_$$", AAT::Translation($msg) . " [$num]");
}

=head2 SQL($ref_pos, @args)

Generates SQL line

=cut
sub SQL
{
  my ($ref_pos, @args) = @_;
  my $line = "";

  foreach my $p (@{$ref_pos})
  {
    if (defined $p)
    {
			if (defined $p->{pos})
			{
				my $f = $p->{function};
				return (undef)	if ((defined $f) && (!defined &{$f}($args[$p->{pos}])));
				my $value = (defined $f ? &{$f}($args[$p->{pos}]) : $args[$p->{pos}]); 
      	$line .= Octopussy::Type::SQL_Datetime($value);
			}
			else
			{
				$line .= "NULL";
			}
    }
    $line .= "\t";
  }

	return ($line);
}

=head2 Get_Messages_To_Parse($services, $taxo, $table, $query, $fields_list)

Returns list of Messages to parse

=cut
sub Get_Messages_To_Parse($$$$$)
{
  my ($services, $taxo, $table, $query, $fields_list) = @_;
  my @fields = Octopussy::Report::Table_Creation($table, $query);
  my $fields_regexp = Octopussy::Message::Regexped_Fields($query);
  my @msg_to_parse = Octopussy::Message::Parse_List($services, $taxo,
    $table, \@fields, $fields_regexp, $fields_list);

  return (@msg_to_parse);
}

=head2 Get_TimePeriod_Files($devices, $services, $begin, $end)

Returns list of Files for Devices $devices, Services $services
and Period $begin-$end

=cut
sub Get_TimePeriod_Files($$$$)
{
  my ($devices, $services, $begin, $end) = @_;

  my ($y1, $m1, $d1, $hour1, $min1) = ($1, $2, $3, $4, $5)
    if ($begin =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})$/);
  my ($y2, $m2, $d2, $hour2, $min2) = ($1, $2, $3, $4, $5)
    if ($end =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})$/);
  my %start = ( year => $y1, month => $m1, day => $d1,
    hour => $hour1, min => $min1 );
  my %finish = ( year => $y2, month => $m2, day => $d2,
    hour => $hour2, min => $min2 );
  my $files = Octopussy::Logs::Files($devices, $services, \%start, \%finish);

  return ($files);
}

=head2 Insert_Data($devices, $services, $taxo, $begin, $end, $table, 
 $query, $field_list)

Inserts data

=cut
sub Insert_Data($$$$$$$$)
{
  my ($devices, $services, $taxo, $begin, $end,
      $table, $query, $field_list) = @_;
  Progress("_MSG_REPORT_PROGRESS_LISTING_FILES", "1/1");
  my @msg_to_parse = Get_Messages_To_Parse($services, $taxo, $table, $query, $field_list);
  my $files = Get_TimePeriod_Files($devices, $services, $begin, $end);
  my $total = $#{@{$files}} + 1;
  my @known_msgs = ();

  AAT::Syslog("octo_core", "$total file(s) to parse with "
    . ($#msg_to_parse + 1) . " message(s).");
  my $i = 1;
  my $nb_lines = 0;
	`touch "$FILE_DB.$$"`;
	`chmod 744 "$FILE_DB.$$"`;
  foreach my $f (@{$files})
  {
    Progress("_MSG_REPORT_PROGRESS_INSERTING_DATA", $i . "/$total");
    if (defined open(FILE, "zcat \"$f\" |"))
		{
    	while (<FILE>)
    	{
      	$nb_lines++;
      	my $line = $_;
      	chomp($line);
      	foreach my $msg (@msg_to_parse)
      	{
        	if (my (@args) = $line =~ $msg->{re})
        	{
						my $sql = SQL($msg->{positions}, @args);
          	push(@known_msgs, $sql)	if (defined $sql);
          	last;
        	}
      	}
    	}
    	close(FILE);
		}
		else
    {
    	print "Unable to open file '$f'\n";
     	AAT::Syslog("octo_extractor", "UNABLE_OPEN_FILE", $f);
    }
    if ($i%10 == 0)
    {
      AAT::DB::Load_Infile($APPLI, $table, "$FILE_DB.$$", \@known_msgs);
      @known_msgs = ();
    }
    $i++;
  }
  AAT::DB::Load_Infile($APPLI, $table, "$FILE_DB.$$", \@known_msgs);
  unlink("$FILE_DB.$$");

  return ($total, $nb_lines);
}

=head2 End()

Ends Reporter

=cut
sub End
{
	AAT::Syslog($PROG_NAME, "Report Generation Aborted !");	
	AAT::DB::Table_Destruction($APPLI, $rc->{table} . "_$$");
	unlink($pid_file);
	unlink("$FILE_DB.$$");
	$shared_memory_cache->remove("info_$$");
	$shared_memory_cache->remove("status_$$");
	exit();
}



#
# MAIN
#
$SIG{USR2} = \&End;

my $status = GetOptions(
	"h" => \$help, "help" => \$help,
	"report=s" => \$report,
  "devices=s" => \@devices, "services=s" => \@services,
	"taxonomy=s" => \$taxonomy,
	"begin=s" => \$begin, "end=s" => \$end, "lang=s" => \$lang, 
	"pid_param=s" => \$pid_param, "output=s" => \$output,
	"mail_subject=s" => \$mail_subject, "mail_recipients=s" => \$mail_recipients,
	"ftp_host=s" => \$ftp_host, "ftp_dir=s" => \$ftp_dir,
	"ftp_user=s" => \$ftp_user, "ftp_pwd=s" => \$ftp_pwd,
	"scp_host=s" => \$scp_host, "scp_dir=s" => \$scp_dir,
	"scp_user=s" => \$scp_user);

Help()	if ((! $status) || ($help) 
	|| (!defined $report) || (! @devices) || (! @services) 
	|| (!defined $begin) || (!defined $end) || (!defined $output));

my $pid = $$;
$shared_memory_cache = 
	new Cache::SharedMemoryCache( { namespace => "octo_reporter", 
		default_expires_in => "1 hour" } ) or
	croak( "Couldn't instantiate SharedMemoryCache" );

Progress("_MSG_REPORT_PROGRESS_REPORT_CONFIG", "1/1");
$rc = (defined $report ? Octopussy::Report::Configuration($report) : undef);
print "Error: Unable to get configuration for Report [$report]\n"
	if (!defined $rc);

$lang ||= "EN";

my $time = time();
print "Report: $rc->{name}\n";
print "Description: $rc->{description}\n";
my $pid_name = $PROG_NAME . (defined $pid_param ?"_$pid_param" : "");
$pid_file = Octopussy::PID_File($pid_name);
my $started = Date::Manip::ParseDateString("epoch $time");
my %info = ( report => $rc->{name}, 
	started => Date::Manip::UnixDate($started, "%Y/%m/%d %H:%M"), 
	devices => \@devices, services => \@services ); 
$shared_memory_cache->set("info_$pid", \%info);

AAT::Syslog($PROG_NAME, "Report Generation: "
	. "D=[" . join(",", @devices) . "] S=[" . join(",", @services) 
	. "] T=[$rc->{table}]");
AAT::Syslog($PROG_NAME, "Report Generation: B=[$begin] E=[$end]");
AAT::Syslog($PROG_NAME, "Report Generation: type=[$rc->{graph_type}] title=[$rc->{name}]");

my @field_list = ();
push(@field_list, split(/,/, $rc->{columns}))	
	if (AAT::NOT_NULL($rc->{columns}));
foreach my $ds ("datasource1", "datasource2", "datasource3")
	{	push(@field_list, $rc->{$ds})	if (AAT::NOT_NULL($rc->{$ds})); }

Progress("_MSG_REPORT_PROGRESS_INIT_PLUGIN", "1/1");
Octopussy::Plugin::Init({ lang => $lang }, @field_list);

#foreach my $e ($rc->{datasource1}, $rc->{datasource2}, $rc->{datasource3})
#	{ push(@field_list, $e)	if ($e ne ""); } 
Progress("_MSG_REPORT_PROGRESS_INIT_DB", "1/1");


my ($nb_files, $nb_lines) = Insert_Data(\@devices, \@services, 
	$taxonomy, $begin, $end, $rc->{table}, $rc->{query}, \@field_list);
	
Progress("_MSG_REPORT_PROGRESS_QUERYING_DB", "0/1");
my $query = $rc->{query};
$query =~ s/FROM(\.*[, ])($rc->{table})([, ]\.*)/FROM$1$2_$pid$3/i;
$query =~ s/ (\w+) JOIN(\.*[, ])($rc->{table})([, ]\.*)/ $1 JOIN$2$3_$pid$4/gi;
my @data = AAT::DB::Query($APPLI, $query);
Progress("_MSG_REPORT_PROGRESS_QUERYING_DB", "1/1");

AAT::DB::Table_Destruction($APPLI, $rc->{table} . "_$pid");

Progress("_MSG_REPORT_PROGRESS_BUILDING_REPORT", "0/1");
my %mail_conf = ( recipients => $mail_recipients, subject => $mail_subject );
my %ftp_conf = ( host => $ftp_host, dir => $ftp_dir,
	user => $ftp_user, pwd => $ftp_pwd );
my %scp_conf = ( host => $scp_host, dir => $scp_dir,
	user => $scp_user);
my %report_stats = ( nb_files => $nb_files, nb_lines => $nb_lines, 
	nb_result_lines => $#data+1, seconds =>  (time() - $time));

Octopussy::Report::Generate($rc, $begin, $end, $output, \@devices, \@services, 
	\@data, \%mail_conf, \%ftp_conf, \%scp_conf, \%report_stats, $lang);
Progress("_MSG_REPORT_PROGRESS_BUILDING_REPORT", "1/1");
AAT::Syslog($PROG_NAME, "Report Generation Completed !");
unlink($pid_file);
$shared_memory_cache->remove("info_$$");
$shared_memory_cache->remove("status_$$");

=head1 AUTHOR

Sebastien Thebert <octo.devel@gmail.com>

=head1 SEE ALSO

octo_dispatcher, octo_extractor, octo_parser, octo_uparser, octo_scheduler

=cut
