package tests::ReportConfigTest;

use strict;

use base qw/Lire::Test::TestCase tests::TestStoreFixture/;

use Lire::ReportSpec;
use Lire::FilterSpec;
use Lire::ReportSection;
use Lire::ReportConfig;
use Lire::I18N;
use Lire::Utils qw/tempfile tempdir deep_copy/;
use Lire::ReportParser::ReportBuilder;
use Lire::PluginManager;
use Lire::Config::ReportSpec;
use Lire::Config::SpecParser;
use tests::helpers::TestExtendedAnalyzer;
use tests::helpers::TestDerivedAnalyzer;
use Lire::Test::Mock;
use File::Path qw/rmtree/;
use File::Copy qw/copy/;

#our @TESTS = qw//;

sub new {
    my $self = shift()->SUPER::new( @_ );

    $self->init();

    return $self;
}

sub set_up {
    my $self = shift->SUPER::set_up();

    $self->set_up_test_schema();

    $self->set_up_tz( 'EST5' );

    $self->{'cfg'}{'lr_scale_numbers'} = 0;
    $self->{'cfg'}{'lr_scale_bytes'} = 1;
    $self->{'cfg'}{'lr_scale_seconds'} = 1;

    $self->{'report_cfg'} = $self->{'testdir'} . "/data/test.cfg";
    $self->{'cfg'}{'lr_reports_path'} = [ $self->{'testdir'} . "/reports" ];
    $self->{'cfg'}{'lr_filters_path'} = [ $self->{'testdir'} . "/filters" ];
    ( $self->{'tmp_fh'}, $self->{'tmp_filename'} ) = tempfile();

    return $self;
}

sub set_up_plugins {
    my $self = $_[0];

    $self->set_up_plugin_mgr();
    Lire::PluginManager->register_plugin( new tests::helpers::TestExtendedAnalyzer() );
    Lire::PluginManager->register_plugin( new tests::helpers::TestDerivedAnalyzer() );

    return;
}

sub tear_down {
    my $self = shift->SUPER::tear_down();

    unlink ($self->{'tmp_filename'});
    rmtree( $self->{'tmpdir'} )
      if defined $self->{'tmpdir'};

    $Lire::Config::TEMPLATES = undef;

    return $self;
}

sub add_to_section {
    my ( $self, $section, $specs ) = @_;

    # Specs: [ [ 'id', { param1 => value, ... } ], ... ]
    foreach my $s ( @$specs ) {
        my ( $id, $params ) = @$s;

        my $spec;
        if ( substr( $id, 0, 1) eq '|' ) {
           $spec = Lire::FilterSpec->load( 'test', substr( $id, 1 ) );
           $section->add_filter( $spec );
        } else {
           $spec = Lire::ReportSpec->load( 'test', $id );
           $self->{'_id_cache'}{$id} ||= 0;
           $spec->subreport_id( $id . "." . $self->{'_id_cache'}{$id}++ );
           $section->add_report( $spec );
        }
        for my $param ( keys %$params ) {
            $spec->param( $param )->value( $params->{$param} );
        }
    }
}

sub build_expected_cfg {
    my $self = $_[0];

    my $cfg = new Lire::ReportConfig();
    $cfg->{'_filename'} = $self->{'report_cfg'};

    my $all_section = new Lire::ReportSection( "test", "All Files" );
    $cfg->add_section( $all_section );
    $self->add_to_section( $all_section, 
                           [ [ 'top-files', { 'files_to_show' => 5 } ],
                             [ 'top-dirs', { 'dirs_to_show' => 5 } ],
                             [ 'downloads-by-period',
                               { 'period' => '15m' } ] ] );

    my $jpeg_section = new Lire::ReportSection( "test", "JPEG Files" );
    $cfg->add_section( $jpeg_section );
    $self->add_to_section( $jpeg_section, 
                           [ [ '|select-file', { 'file_match' => '.*.jpg$' }],
                             [ 'top-files', { 'files_to_show' => 5, } ],
                             [ 'top-dirs',  { 'dirs_to_show' => 5 } ] ] );

    my $sess_section = new Lire::ReportSection( "test", "Sessions" );
    $cfg->add_section( $sess_section );
    $self->add_to_section( $sess_section, 
                           [[ 'sessions-by-length', {} ],
                            [ 'sessions-by-user_class',  {} ] ] );

    my $dl_section = new Lire::ReportSection( "test", "Download Reports" );
    $cfg->add_section( $dl_section );
    $self->add_to_section( $dl_section, 
                           [ ['avg-file_size-by-timeslot', { 'unit' => '1h' }],
                             [ 'user_downloads-report', {} ] ] );

    return $cfg;
}

sub test_load_from_file {
    my $self = $_[0];


    my $e_cfg = $self->build_expected_cfg();

    my $cfg = Lire::ReportConfig->new_from_file( "test", $self->{'report_cfg'} );
    $self->assert_deep_equals( $e_cfg, $cfg );
}

sub test_parse_encoding_line {
    my $self = $_[0];

    return unless $Lire::I18N::USE_ENCODING;

    my $fh = $self->{'tmp_fh'};
    print $fh "This is an eacute: \xc3\xa9\n";
    close $fh;
    open $fh, $self->{'tmp_filename'};
    my $cfg = new Lire::ReportConfig( 'test' );
    $cfg->{'_fh'} = $fh;

    $cfg->{'_curr_section'} = '1';
    local $. = 2; # prevents warning
    $self->assert_dies( qr/'encoding' directive must be the first directive/,
                        sub { $cfg->_parse_encoding_line( '=encoding test' ); } );

    $cfg->{'_curr_section'} = undef;

    $self->assert_dies( qr/invalid 'encoding' directive, at line/,
                        sub { $cfg->_parse_encoding_line ( '=encoding' ); } );
    $self->assert_dies(  qr/invalid 'encoding' directive, at line/,
                         sub { $cfg->_parse_encoding_line( '=encoding wa wa' ); });

    $cfg->_parse_encoding_line( '=encoding utf-8' );
    $self->assert_equals( $cfg->{'_encoding'}, 'utf-8' );
    my $line = <$fh>;
    $self->assert_str_equals( "This is an eacute: \x{e9}\n", $line );

    $self->assert_dies( qr/only one 'encoding' directive allowed, at line/,
                        sub { $cfg->_parse_encoding_line ( '=encoding test' ) } );
}

sub test_parse_encoding_line_no_support {
    my $self = $_[0];

    local $Lire::I18N::USE_ENCODING = 0;
    local $. = 2; # prevents warning

    my $cfg = new Lire::ReportConfig( 'test' );
    $self->assert_dies( qr/'encoding' directive requires perl >= 5.8.0, at line/,
                        sub { $cfg->_parse_encoding_line ( '=encoding UTF-8' ) });
}

sub test_schemas {
    my $self = $_[0];

    my $cfg = Lire::ReportConfig->new_from_file("test", $self->{'report_cfg'});
    $self->assert_deep_equals( [ sort 'test', 'test-extended', 'test-derived' ],
                               [ sort @{$cfg->schemas()} ] );
}

sub test_print {
    my $self = $_[0];

    return unless $Lire::I18N::USE_ENCODING;

    my $fh = $self->{'tmp_fh'};
    my $cfg = new Lire::ReportConfig( 'test' );
    $cfg->{'_encoding'} = 'utf-8';
    my $sect = new Lire::ReportSection( 'test', "Eacute: \x{00e9}" );
    $cfg->add_section( $sect );
    $self->add_to_section( $sect, 
                           [ [ '|select-file', { 'file_match' => '.*.jpg$' }],
                             [ 'top-files', { 'files_to_show' => 5, } ] ] );

    $cfg->print( $fh );
    close $fh;
    open $fh, $self->{'tmp_filename'};
    local $/ = undef;
    my $file =  <$fh>;
    $self->assert_equals( <<EOC, $file );
=encoding utf-8

=section Eacute: \xc3\xa9
|select-file file_match=".*.jpg\$"
top-files files_to_show="5"

EOC
}

sub test_create_report {
    my $self = $_[0];

    my $section = new Lire::Test::Mock ( 'Lire::ReportSection' );
    my $cfg = new Lire::ReportConfig();
    $cfg->add_section( $section );
    my $now = time();

    my $report = $cfg->create_report( $now, $now + 3600 );
    $self->assert_isa( 'Lire::Report', $report );
    $self->assert_num_equals( $now, $report->timespan_start() );
    $self->assert_num_equals( $now +3600 , $report->timespan_end() );
    $self->assert_null( $report->title() );
    $self->assert_deep_equals( [ $section, $report ], $section->get_invocation( 'create_report_section' ) );

    $cfg->title( 'My title' );
    $report = $cfg->create_report( $now, $now + 3600 );
    $self->assert_str_equals( 'My title', $report->title() );
}

sub test_generate_report {
    my $self = $_[0];

    $self->set_up_test_store();
    $self->set_up_plugins();

    my $parser = new Lire::ReportParser::ReportBuilder();
    my $report_e = $parser->parsefile( "$self->{'testdir'}/data/test-sqlite.xml" );

    my $cfg = new_from_file Lire::ReportConfig( 'test',
                                                $self->{'report_cfg'} );

    $cfg->create_analyser_streams( $self->{'store'} );
    my $report_t = $cfg->generate_report( $self->{'store'} );
    $self->assert_isa( 'Lire::Report', $report_t );

    # The difference in these fields are normal
    $report_t->date( $report_e->date() );
    $report_t->generator( $report_e->generator() );
    delete $report_e->{'_id_cache'};

    $self->assert_deep_equals( $report_e, $report_t );
}

sub test_create_analyser_streams {
    my $self = $_[0];

    $self->set_up_test_store();
    $self->set_up_plugins();

    $self->assert( $self->{'store'}->has_dlf_stream( 'test' ) );
    $self->assert( ! $self->{'store'}->has_dlf_stream( 'test-extended' ) );
    $self->assert( ! $self->{'store'}->has_dlf_stream( 'test-derived' ) );

    my $cfg = new_from_file Lire::ReportConfig( 'test',
                                                $self->{'report_cfg'} );
    $cfg->create_analyser_streams( $self->{'store'} );

    $self->assert( $self->{'store'}->has_dlf_stream( 'test-extended' ) );
    my $extended = $self->{'store'}->open_dlf_stream( 'test-extended' );
    $self->assert_num_equals( 24, $extended->nrecords() );
    $self->assert_str_equals( '/pictures',
                              $extended->read_dlf()->{'dirname'} );
    $extended->close();

    $self->assert( $self->{'store'}->has_dlf_stream( 'test-derived' ) );
    my $derived = $self->{'store'}->open_dlf_stream( 'test-derived', 'r',
                                                     'connection_id' );
    $self->assert_num_equals( 5, $derived->nrecords() );
    $self->assert_str_equals( 'AABBCCDD',
                              $derived->read_dlf()->{'connection_id'} );
    $derived->close();
}

sub test_new_from_config {
    my $self = $_[0];

    my $cfg_spec = new Lire::Config::ReportSpec( 'name' => 'report' );
    my $report_cfg = $cfg_spec->instance();
    $report_cfg->get( 'id' )->set( 'My Report' );
    $report_cfg->get( 'title' )->set( 'My title' );

    my $section1 = $cfg_spec->get( 'sections' )->get( 'section' )->instance();
    $section1->get( 'superservice' )->set( 'test' );
    $section1->get( 'title' )->set( 'My title' );
    $report_cfg->get( 'sections' )->append( $section1 );

    my $cfg = $report_cfg->as_value();
    $self->assert_isa( 'Lire::ReportConfig', $cfg );
    $self->assert_str_equals( 'My title', $cfg->title() );
    $self->assert_num_equals( 1, scalar $cfg->sections() );
}

sub _set_up_templates {
    my $self = $_[0];

    $self->{'tmpdir'} = tempdir( __PACKAGE__ . "_XXXXXX" );
    copy( "$self->{'testdir'}/data/test_report_cfg.xml",
          "$self->{'tmpdir'}/test_default.xml" );
    $self->{'cfg'}{'lr_templates_path'} = [ $self->{'tmpdir'} ];

    my $parser = new Lire::Config::SpecParser();
    $self->{'templates'} = $parser->parsefile( "$self->{'testdir'}/data/test_report_cfg.xml" );
}

sub test_templates {
    my $self = $_[0];

    $self->_set_up_templates();
    $self->assert_deep_equals( {}, \%Lire::ReportConfig::TEMPLATES );
    local $Lire::ReportConfig::TEMPLATES{'test'} = 1;
    $self->assert_deep_equals( [ 'test', 'test_default', ],
                               [ sort @{Lire::ReportConfig->templates()} ] );
    $self->assert_deep_equals( { 'test' => 1 },
                               \%Lire::ReportConfig::TEMPLATES );
    $self->assert( !Lire::ReportConfig->has_template( 'test_1' ) ? 1 : 0,
                   '!has_template( "test_1" )' );
    $self->assert( Lire::ReportConfig->has_template( 'test' ) ? 1 : 0,
                   'has_template( "test" )' );
    $self->assert( Lire::ReportConfig->has_template( 'test_default' ) ? 1 : 0,
                   'has_template( "test_default" )' );

    $self->assert_num_equals( 1, Lire::ReportConfig->template( 'test' ) );
    my $template = Lire::ReportConfig->template( 'test_default' );
    $self->assert_isa( 'Lire::Config::Object', $template );
    $self->assert_str_equals( 'test_default', $template->name() );
    $self->assert_deep_equals( { 'test_default' => $self->{'templates'}->get( 'test_default' )->instance(), 
                                 'test' => 1 },
                               \%Lire::ReportConfig::TEMPLATES );
    $self->assert_str_equals( $template,
                              Lire::ReportConfig->template( 'test_default' ) );
}

sub test_as_config_value {
    my $self = $_[0];

    my $cfg = new Lire::ReportConfig();
    my $section = new Lire::ReportSection( 'test', 'My section' );
    $cfg->add_section( $section );
    my $select = Lire::FilterSpec->load( 'test', 'select-file' );
    $select->param( 'file_match')->value( 'my_file' );
    $section->add_filter( $select );
    my $top_dirs = Lire::ReportSpec->load( 'test', 'top-dirs' );
    $top_dirs->subreport_id( 'top-dirs.0' );
    $top_dirs->param( 'dirs_to_show' )->value( 5 );
    $section->add_report( $top_dirs );

    my $cfg_value =
      new Lire::Config::ReportSpec( 'name' => 'my_report' )->instance();
    $cfg_value->get( 'id' )->set( 'my_report' );
    my $section_value = $cfg_value->spec()->get( 'sections' )->get( 'section' )->instance();
    $section_value->get( 'title' )->set( 'My section' );
    $section_value->get( 'superservice' )->set( 'test' );
    $cfg_value->get( 'sections' )->append( $section_value );
    my $filter_value = $section_value->spec()->get( 'filters' )->get( 'test:select-file' )->instance();
    $filter_value->get( 'id' )->set( 'select-file.0' );
    $filter_value->get( 'file_match' )->set( 'my_file' );
    $section_value->get( 'filters' )->append( $filter_value );
    my $report_value = $section_value->spec()->get( 'specs' )->get( 'test:top-dirs' )->instance();
    $report_value->get( 'id' )->set( 'top-dirs.0' );
    $report_value->get( 'dirs_to_show' )->set( 5 );
    $section_value->get( 'specs' )->append( $report_value );

    $self->assert_deep_equals( $cfg_value,
                               $cfg->as_config_value( 'my_report' ) );
}

1;


syntax highlighted by Code2HTML, v. 0.9.1