############################################################################
##      Copyright (C) 2005 Subredu Manuel  <diablo@iasi.roedu.net>.        #
##                                                                         #
## This program is free software; you can redistribute it and/or modify    #
## it under the terms of the GNU General Public License v2 as published by #
## the Free Software Foundation.                                           #
##                                                                         #
## 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 RoPkg::Simba::Plugin::GenHTML;

use strict;
use warnings;

use RoPkg::Exceptions;
use RoPkg::Rsync::LogParser;
use RoPkg::Utils qw(GetHumanDate SecToTime CreateFile);

use Number::Format qw(:subs :vars);
use Scalar::Util   qw(blessed);

use DateTime;
use HTML::Template;

use vars qw($VERSION);

$VERSION='0.2.4';

my $defaults_values = {
  templatesDir => q{./},
  enabled      => 'yes',
  gmt_offset   => '+02:00',
};

sub new {
  my ($class, %opt) = @_;
  my $self;

  $self = bless { %opt }, $class;

  $self->{lp} = new RoPkg::Rsync::LogParser(
                      type => 'client'
                    );

  return $self;
}

##############################
## General pourpose - BEGIN ##
##############################

sub _enabled {
  my ($self, $cfg) = @_;

  #if enabled found
  if ( $cfg->{enabled} ) {

    #return 1 if the value is yes (case insensitive)
    return 1 if ( $cfg->{enabled} =~ m{^yes$}xmi );

    #else, return 0
    return 0;
  }

  #if no enabled was found, assume yes
  return 1;
}

sub _load_config {
  my ($self, $cfg) = @_;
  
  foreach(keys %{$cfg->{plugins}}) {
    next if ( $_ ne 'html' );

    $self->{cfg} = $cfg->{plugins}->{$_};
  }

  #read the parameters from the configuration file
  foreach(qw(templatesDir enabled shortName gmt_offset)) {
    if ( $self->{cfg}->{$_} ) {
      $self->{$_} = $self->{cfg}->{$_};
    }
  }

  #put default values (at those vars who are not present in the config)
  foreach(keys(%{ $defaults_values })) {
    if ( !$self->{$_} ) {
      $self->{$_} = $defaults_values->{$_};
    }
  }

  #read the probes list
  if ( !$self->{mirror_probes} ) {
    if ( $cfg->{mProbes} ) {
      $self->{mirror_probes} = $cfg->{mProbes};
    }
    else {
      $self->{mirror_probes} = ();
    }
  }
  
  $self->{lp}->Probes(@{ $self->{mirror_probes} });

  return 1;
}

##############################
##  General pourpose -  END ##
##############################

############################
## Common methods - BEGIN ##
############################

# Add SimbaVersion, SimbaURL and LastPageUpdate to the template
sub _add_general_details {
  my ($self, $tmpl) = @_;

  $tmpl->param(SimbaVersion   => RoPkg::Simba::VERSION());
  $tmpl->param(SimbaURL       => RoPkg::Simba::SimbaURL());
  $tmpl->param(LastPageUpdate => GetHumanDate(time()));

  return $tmpl;
}

# Add general mirror infos (with formating)
# returns a hash with the mirror infos
sub _build_mirror_infos {
  my ($self, $mirror) = @_;
  my %m_data;

  foreach($mirror->GetMethods) {
    next if ( $_ eq 'StdOut' );
    next if ( $_ eq 'StdErr' );

    $m_data{$_} = $mirror->$_;
  }

  $KILO_SUFFIX = ' K';
  $MEGA_SUFFIX = ' M';
  $GIGA_SUFFIX = ' G';
  $DECIMAL_FILL = 1;

  $m_data{LastUpdateSpeed}    = Number::Format::format_bytes($mirror->LastUpdateSpeed, 1);
  $m_data{LastUpdateBytes}    = Number::Format::format_bytes($mirror->LastUpdateBytes, 1);
  $m_data{Size}               = Number::Format::format_bytes($mirror->Size, 1);
  $m_data{LastUpdateDuration} = SecToTime($mirror->LastUpdateDuration);
  $m_data{LastUpdated}        = GetHumanDate($mirror->LastUpdated);
  $m_data{UTC_DATE}           = (DateTime->from_epoch( epoch => $mirror->LastUpdated) . $self->{gmt_offset});

  return %m_data;
}

##########################
## Common methods - END ##
##########################

#################################
## Gen Indexes section - BEGIN ##
#################################

sub _gen_html_index {
  my ($self, $hd, $mirrors) = @_;
  my @loop_data;
  my $tmpl;

  $tmpl = HTML::Template->new(
            filename => ($self->{templatesDir} . $hd->{template}),
            loop_context_vars => 1,
            die_on_bad_params => 0
          );
  
  $self->_add_general_details($tmpl);
  $tmpl->param(MirrorsNo => (scalar @{ $mirrors }));

  foreach(@{ $mirrors }) {
    my $mirror = $_;
    my %m_data;

    %m_data = $self->_build_mirror_infos($mirror);
    push(@loop_data, \%m_data);
  }

  $tmpl->param(Mirrors => \@loop_data);
  CreateFile($hd->{outfile}, $tmpl->output);

  return 1;
}

#################################
##  Gen Indexes section -  END ##
#################################

##############################
## Gen page section - BEGIN ##
##############################

sub _build_file_list {
  my ($self, $mirror, $max_items, @files) = @_;
  my @file_list;
  my $items = 0;

  foreach(@files) {
    my %file;

    $file{Filename} = $_;
    $file{LocalURL} = $mirror->LocalURL;

    push (@file_list, \%file);

    if (++$items >= $max_items) {
      return \@file_list;
    }
  }

  return \@file_list;
}

# Add all mirror related information (information from database).
sub _add_mirror_details {
  my ($self, $tmpl, $mirror) = @_;
  my %m_data;

  %m_data = $self->_build_mirror_infos($mirror);

  #these are not inserted by _build_mirror_infos
  $m_data{StdOut} = $mirror->StdOut;
  $m_data{StdErr} = $mirror->StdErr;

  foreach(keys(%m_data)) {
    $tmpl->param($_ => $m_data{$_});
  }

  return $tmpl;
}

sub _add_transfered_files {
  my ($self, $tmpl, $mirror, $hd) = @_;
  my $max_items = 999999;

  if ($hd->{max_transfered}) {
    $max_items = $hd->{max_transfered};
  }

  $tmpl->param(FilesNo       => (scalar $self->{lp}->Files));
  $tmpl->param(RealFilesNo   => (scalar $self->{lp}->RealFiles));
  $tmpl->param(FilesList     => $self->_build_file_list($mirror, $max_items, $self->{lp}->Files));
  $tmpl->param(RealFilesList => $self->_build_file_list($mirror, $max_items, $self->{lp}->RealFiles()));

  return $tmpl;
}

sub _add_deleted_files {
  my ($self, $tmpl, $mirror, $hd) = @_;
  my $max_items = 999999;

  if ($hd->{max_deleted}) {
    $max_items = $hd->{max_deleted};
  }
  $tmpl->param(DeletedFilesNo       => (scalar $self->{lp}->Deleted));
  $tmpl->param(RealDeletedFilesNo   => (scalar $self->{lp}->RealDeleted));
  $tmpl->param(DeletedFilesList     => $self->_build_file_list($mirror, $max_items, $self->{lp}->Deleted));
  $tmpl->param(RealDeletedFilesList => $self->_build_file_list($mirror, $max_items, $self->{lp}->RealDeleted));

  return $tmpl;
}

sub _add_symlinks {
  my ($self, $tmpl, $mirror) = @_;
  my $symlinks;
  my @slist;

  $symlinks = $self->{lp}->Symlinks;
  if ( !$symlinks ) {
    $tmpl->param(SymlinksNo => 0);
  }
  else {
    $tmpl->param(SymlinksNo => scalar(keys(%{ $symlinks })));
  
    foreach(keys(%{ $symlinks })) {
      my %s;

      $s{Name} = $_;
      $s{Target} = $symlinks->{$_};

      push @slist, \%s;
    }

    $tmpl->param(SymlinksList => \@slist);
  }

  $symlinks = $self->{lp}->RealSymlinks;
  if ( !$symlinks ) {
    $tmpl->param(RealSymlinksNo => 0);
  }
  else {
    $tmpl->param(RealSymlinksNo => scalar(keys(%{ $symlinks })));

    foreach(keys(%{ $symlinks })) {
      my %s;

      $s{Name} = $_;
      $s{Target} = $symlinks->{$_};

      push @slist, \%s;
    }

    $tmpl->param(RealSymlinksList => \@slist);
  }
  
  return $tmpl;
}

sub _add_files {
  my ($self, $tmpl, $mirror, $hd) = @_;

  $self->_add_transfered_files($tmpl, $mirror, $hd);
  $self->_add_deleted_files($tmpl, $mirror, $hd);
  $self->_add_symlinks($tmpl, $mirror);

  if ($hd->{max_transfered}) {
    $tmpl->param(MaxTransfered => $hd->{max_transfered});
  }
  if ($hd->{max_deleted}) {
    $tmpl->param(MaxDeleted => $hd->{max_deleted});
  }

  #This is useful because the HTML::Template does not support
  #conditions on TMPL_IF and there is no way (at least I can't
  #found one) to exclude something when limits are off

  if ($hd->{max_transfered} && $hd->{max_deleted}) {
    $tmpl->param(MaxLimits => 1);
  }

  return $tmpl;
}

sub _add_excluded {
  my ($self, $tmpl, $mirror) = @_;
  my ($exo, @items, @loop_data);

  $exo = new RoPkg::Simba::Exclude(
               MirrorID  => $mirror->id,
               CommandID => $mirror->CommandID,
               dbo       => $mirror->dbo,
               dbo_method => $mirror->dbo_method
             );

  eval {
    $exo->Load();
    @items = $exo->GetItems();
  };

  if ( Exception::Class->caught('DB::NotFound') ) {
    $tmpl->param(ExcludedItemsNo => 0);
    return $tmpl;
  }

  $tmpl->param(ExcludedItemsNo => scalar(@items));
  foreach(@items) {
    my %ex;

    $ex{ExcludeName} = $_;
    push @loop_data, \%ex;
  }
  $tmpl->param(ExcludedItems => \@loop_data);

  return $tmpl;
}

sub _write_to_file {
  my ($self, $hd, $tmpl, $mirror) = @_;
  my ($mirror_name, $outfile);

  $mirror_name = $mirror->Name();
  $outfile = $hd->{outfile};
  $outfile =~ s{__name__}{$mirror_name}xm;

  return CreateFile($outfile, $tmpl->output);
}

sub _gen_html_page {
  my ($self, $hd, $mirrors) = @_;
  my $tmpl;

  foreach(@{ $mirrors }) {
    my $mirror;
    my %template_data = ();

    $mirror  = $_;
    $tmpl = HTML::Template->new(
              filename => ($self->{templatesDir} . $hd->{template}),
              loop_context_vars => 1,
              die_on_bad_params => 0
            );
    $self->{lp}->Log($mirror->StdOut);
    $self->{lp}->Parse();

    $self->_add_general_details($tmpl);
    $self->_add_mirror_details($tmpl, $mirror);
    $self->_add_files($tmpl, $mirror, $hd);
    $self->_add_symlinks($tmpl, $mirror);
    $self->_add_excluded($tmpl, $mirror);
    $self->_write_to_file($hd, $tmpl, $mirror);
  }

  return 1;
}

##############################
##  Gen page section -  END ##
##############################

############################
## Public methods - BEGIN ##
############################

sub genMirrorsIndex {
  my ($self, $cfg, $mirrors) = @_;
  my $htmls;
 
  if ( !blessed($self) ) {
    OutsideClass->throw('Called outside class instance');
  }

  #do nothing if no configuration items are found
  return 0 if ( !$cfg->{plugins}->{html} );

  #do nothing if plugin is disabled
  return 0 if ( !$self->_enabled($cfg->{plugins}->{html}) );

  #nothing to do if no mirrors are given
  return 0 if ((scalar @{ $mirrors }) <= 0 );

  $self->_load_config($cfg);

  foreach(sort(keys(%{ $self->{cfg} }))) {
    my $item = $_;
    
    next if ( $item !~ m{^h_}xm );
    next if ( $self->{cfg}->{$item}->{type} ne 'index' );

    $self->_gen_html_index($self->{cfg}->{$_}, $mirrors);
  }

  return 1;
}

sub genMirrorPage {
  my ($self, $cfg, $mirror) = @_;
 
  if ( !blessed($self) ) {
    OutsideClass->throw('Called outside class instance');
  }

  #do nothing if no configuration items are found
  return 0 if ( !$cfg->{plugins}->{html} );

  #do nothing if plugin is disabled
  return 0 if ( !$self->_enabled($cfg->{plugins}->{html}) );

  #nothing to do if no mirrors are given
  return 0 if ( ! $mirror < 0 );

  $self->_load_config($cfg);

  foreach(sort(keys(%{ $self->{cfg} }))) {
    my $item = $_;
    
    next if ( $item !~ m{^h_}xm );
    next if ( $self->{cfg}->{$item}->{type} ne 'page' );

    $self->_gen_html_page($self->{cfg}->{$_}, $mirror);
  }

  return 1;
}

############################
##  Public methods -  END ##
############################

1;

__END__

=head1 NAME

RoPkg::Simba::Plugin::GenHTML - html generator plugin for Simba

=head1 VERSION

0.2.4

=head1 PREREQUISITES

GenHTML requires Simba (and all Simba's dependencies). Besides that
GenHTML requires HTML::Template perl module.

=head1 DESCRIPTION

GenHTML is a Simba plugin used to generate html reports. For
details please visit http://simba.packages.ro

=head1 METHODS

=head2 genMirrorPage($cfg, $mirror)

Generate mirror report.

=head2 genMirrorsIndex($cfg, $mirrors)

Generate mirrors report.

=head1 SEE ALSO

L<RoPkg::Simba> http://simba.packages.ro

=head1 AUTHOR

Subredu Manuel <diablo@iasi.roedu.net>

=head1 LICENSE

Copyright (C) 2005 Subredu Manuel.  All Rights Reserved.
This module is free software; you can redistribute it 
and/or modify it under the same terms as Perl itself.
The LICENSE file contains the full text of the license.

=cut
