package Lire::Report::Subreport;

use strict;

use vars qw/ $VERSION @ISA /;

use Lire::Report::Group;
use Lire::DataTypes qw/:special :xml /;
use Lire::XMLUtils qw/xml_encode/;

use POSIX qw/ strftime /;

use Carp;

BEGIN {
    ($VERSION)	= '$Revision: 1.4 $' =~ m!Revision: ([.\d]+)!;
    @ISA = qw/Lire::Report::Group/;
}

=pod

=head1 NAME

Lire::Subreport - Interface to a Lire subreport.

=head1 SYNOPSIS

    use Lire::ReportParser::ReportBuilder;
    my $parser = new Lire::ReportParser::ReportBuilder;
    my $report = $parser->parse( "report.xml" );

    foreach my $s ( $report->sections ) {
	print "Section: '", $s->title, "\n\n";
	foreach my $r ( $s->subreports ) {
	    print "Subreport ", $r->title, " has ", scalar $r->entries, 
		" entries\n";
	}
    }

=head1 DESCRIPTION

This module offers an API to the subreports included in the sections
of a Lire's report.

=head1 CONSTRUCTORS

=head2 new( $report, $type)

Creates a new Lire::Report::Subreport object. The $type parameter is
the id of the report specification that is used to generate this
subreport's data. The $report parameter is of type Lire::Report and is
used to fill in the timespan, superservice and type attributes of the
subreport.

=cut

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;

    my ( $report, $type ) = @_;

    croak "report isn't of type Lire::Report ($report)"
      unless UNIVERSAL::isa( $report, "Lire::Report" );

    croak "invalid type: $type"
      unless check_xml_name( $type );

    bless { superservice => $report->superservice,
	    type	 => $type,
	    descriptions => [],
	    entries	 => [],
	    missing	 => 0,
	    report	 => $report,
	  }, $class;
}

=pod

=head2 new_missing( $report, $type, [$reason])

Creates a new Lire::Report::Subreport object marked as missing. The
$type parameter is the id of the report specification that is used to
generate this subreport's data. The $report parameter is of type
Lire::Report and is used to fill in the timespan, superservice and
type attributes of the subreport. The $reason parameter sets the
reason why this subreport is missing.

=cut

sub new_missing {
    my $proto = shift;
    my $class = ref $proto || $proto;

    my ( $report, $type, $reason ) = @_;

    croak "report isn't of type Lire::Report ($report)"
      unless UNIVERSAL::isa( $report, "Lire::Report" );

    croak "invalid type: $type"
      unless check_xml_name( $type );

    bless { superservice => $report->superservice,
	    type	 => $type,
	    descriptions => [],
	    missing	 => 1,
	    reason	 => $reason,
	  }, $class;
}

=pod

=head1 OBJECT METHODS

=head2 superservice()

Returns the superservice of this subreport. This is usually identical
to the report's value.

=cut

sub superservice {
    $_[0]{superservice};
}

=head2 is_missing()

Returns true if this subreport was marked as missing in the XML file.
A missing subreport is a subreport which was present in the report
configure file but which was marked as missing in the XML report. This
happens when the report specification requires fields that weren't
available in the DLF files generated. It can also happen when an error
occurs in the report generation process.

=cut

sub is_missing {
    return $_[0]{missing};
}

=pod

=head2 missing_reason()

Reports why the subreport is missing. This will be undefined when the
subreport is not missing.

=cut

sub missing_reason {
    $_[0]{reason};
}

=head2 type()

Returns the type of this subreport. This is the ID of the report specification that was used to generate this subreport.

=cut

sub type {
    $_[0]{type};
}

=pod

=head2 charttype( [$new_type] )

Returns the suggested chart's type for this subreport. If the
$new_type parameter is set, this subreport's charttype attribute will
be modified accordingly.

=cut

sub charttype {
    if ( defined $_[1]) {
	croak "invalid charttype values: $_[1]"
	  unless check_chart( $_[1]);
	$_[0]{charttype} = $_[1];
    }
    $_[0]{charttype};
}

=pod

=head2 hostname([$hostname])

Returns the hostname of this subreport. This is usually identical to
the report's value.

If the $hostname is parameter is set, the subreport's hostname will be
set to this new value.

=cut

sub hostname {
    $_[0]{hostname} = $_[1] if @_ == 2;
    $_[0]{hostname} || $_[0]{report}->hostname;
}

=pod

=head2 date()

Returns the date in seconds since epoch on which this subreport was
generated. This is usually identical to the report's value.

=cut

sub date {
    $_[0]{date} || $_[0]{report}->date;
}

=pod

=head2 timespan_start()

Returns the start of the timespan covered by this subreport in seconds
since epoch. This is usually identical to the report's value.

=cut

sub timespan_start {
    $_[0]{timespan_start} || $_[0]{report}->timespan_start;
}

=pod

=head2 timespan_end()

Returns the end of the timespan covered by this subreport in seconds
since epoch. This is usually identical to the report's value.

=cut

sub timespan_end {
    $_[0]{timespan_end} || $_[0]{report}->timespan_end;
}

=pod

=head2 title( [$title] )

Returns the subreport's title.

If the $title is parameter is set, the subreport's title will be set to
this new value.

=cut

sub title {
    $_[0]{title} = $_[1] if defined $_[1];
    $_[0]{title};
}

=pod

=head2 target_users()

Returns the user class supported by the descriptions of this subreport. 

Lire's reports can contains several descriptions, each tailored for a
specific class of users like 'sysadmin' or 'manager'. This returns the
list of target users' available.

=cut

sub target_users {
    my ( $self ) = @_;

    return map { $_->{name} } @{$self->{descriptions}};
}

=pod

=head2 description( [$target_user] )

Returns the description for a particular class of user. This should be
one of the values returned by target_users(). If the $target_user
parameter is missing or the requested class isn't available, the
'default' description will be returned. That's the first one that
appears in the XML file.

The description is encoded in DocBook XML.

If the $description parameter is set, this method will set the
$target_user description to this new value. If the $description
parameter is undef, that description will be removed.

=cut

sub description {
    my ( $self, $target_user, $new_desc ) = @_;

    my $desc_idx = 0;
    if ( defined $target_user ) {
	# Look for that target_user or use the first one
	my $i	    = 0;
	foreach my $d ( @{$self->{descriptions}} ) {
	    if ($d->{name} eq $target_user) {
		$desc_idx	= $i;
		last;
	    }
	    $i++
	}
	# $desc_idx defaults to 0
    } # else $desc_idx defaults to 0

    # Mutator
    if ( @_ == 3 ) {
	if ( defined $target_user && $desc_idx == 0 ) {
	    # Check that $desc_idx is really target_user and not just the
	    # default one
	    unless ( defined $self->{descriptions}[0]{name} && 
		     $target_user eq $self->{descriptions}[0]{name} ) 
	    {
		push @{$self->{descriptions}}, { name => $target_user, };
		$desc_idx = $#{$self->{descriptions}};
	    }
	}
	if ( defined $new_desc ) {
	    $self->{descriptions}[$desc_idx]{desc} = $new_desc;
	} else {
	    splice @{$self->{descriptions}}, $desc_idx, 1;
	    return undef;
	}
    }

    $self->{descriptions}[$desc_idx]{desc};
}

=pod

=head2 show( [$show] )

Returns the number of entries that should be displayed in the report.
There may be more entries than that in the subreport. When this
parameter is zero or undefined, all available entries should be
displayed.

If the $show is parameter is set, the subreport's show parameter will
be set to this new value.

=cut

sub show {
    $_[0]{show} = $_[1] if @_ == 2;
    $_[0]{show};
}

=pod

=head2 entries()

Returns the subreport's entries as an array. This will be an array of
Lire::Report::Entry objects.

=cut

sub entries {
    @{$_[0]{entries}};
}

=pod

=head2 add_entry( $entry )

Adds a data entry to this subreport. The $entry parameter should be a
Lire::Report::Entry object.

=cut

sub add_entry {
    my ( $self, $entry ) = @_;

    croak ( "entry should be of type Lire::Report::Entry (not $entry)" )
      unless UNIVERSAL::isa( $entry, "Lire::Report::Entry" );

    push @{$self->{entries}}, $entry; 
}

=pod

=head2 image( [$image] )

If an image was generated for this subreport, this methods returns the
Lire::Report::Image that contains the image's information.

If the $image parameter is set, the subreport's image will be set to
this new value.

=cut

sub image {
    if ( @_ == 2 ) {
	croak( "invalid image parameter. Should be of type Lire::Image and not $_[1]" )
	  unless UNIVERSAL::isa( $_[1], "Lire::Image" );
	$_[0]{image} = $_[1];
    }
    $_[0]{image};
}

sub write_report {
    my ( $self, $fh, $indent ) = @_;

    $fh ||= *STDOUT;
    my $pfx = ' ' x $indent;

    if ( $self->{missing} ) {
	print $fh qq!$pfx<lire:missing-subreport type="$self->{type}" !,
	  'reason="', xml_encode( $self->{reason} ), qq!"/>\n\n!;    

    } else {
	print $fh qq!$pfx<lire:subreport type="$self->{type}" superservice="$self->{superservice}">\n!;

	print $fh "$pfx <lire:title>", xml_encode( $self->{title} ), 
	  "</lire:title>\n\n";

	if ( $self->{date} ) {
	    my $time  = strftime '%Y-%m-%d %H:%M:%S %Z', localtime $self->date;
	    print $fh qq!$pfx <lire:date time="$self->{date}">$time</lire:date>\n!	    
	}

	if ( $self->{timespan_start} ) {
	    my $stime = strftime '%Y-%m-%d %H:%M:%S %Z', localtime $self->timespan_start;
	    my $etime = strftime '%Y-%m-%d %H:%M:%S %Z', localtime $self->timespan_end;
	    print $fh qq!$pfx <lire:timespan start="$self->{timespan_start}" end="$self->{timespan_end}">$stime - $etime</lire:timespan>\n!
	}

	print $fh "$pfx <lire:hostname>", xml_encode( $self->{hostname} ), 
	  "</lire:hostname>\n"
	  if defined $self->{hostname};

	foreach my $desc ( @{$self->{descriptions}} ) {
	    print $fh "$pfx <lire:description";
	    print $fh qq! target-user="$desc->{name}"! 
	      if defined $desc->{name};
	    print $fh ">$desc->{desc}</lire:description>\n";
	}

	print $fh "\n" if @{$self->{descriptions}};

	print $fh "$pfx <lire:table";
	print $fh qq! charttype="$self->{charttype}"! 
	  if defined $self->{charttype};
	print $fh qq! show="$self->{show}"! 
	  if defined $self->{show};
	print $fh ">\n";

	foreach my $e ( $self->entries ) {
	    $e->write_report( $fh, $indent + 2 );
	}

	print $fh "$pfx </lire:table>\n";

	$self->{image}->write_report( $fh, $indent + 1 )
	  if $self->{image};

	print $fh "$pfx</lire:subreport>\n\n";
    }
}

sub delete {
    my ( $self ) = @_;

    delete $self->{report};
}

# keep perl happy
1;

__END__

=pod

=head1 SEE ALSO

Lire::ReportParser::ReportBuilder(3pm) Lire::Report::Section(3pm)
Lire::Report::Report(3pm) Lire::Report::Entry(3pm),
Lire::Report::Group(3pm), Lire::Report::Image(3pm)

=head1 VERSION

$Id: Subreport.pm,v 1.4 2002/06/18 19:27:21 flacoste Exp $

=head1 COPYRIGHT

Copyright (C) 2002 Stichting LogReport Foundation LogReport@LogReport.org

This file is part of Lire.

Lire is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

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 (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html or write to the Free Software 
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.

=head1 AUTHOR

Francis J. Lacoste <flacoste@logreport.org>

=cut
