package Lire::Report;

use strict;

use vars qw/ $VERSION @ISA /;

use Lire::XMLUtils qw/xml_encode/;
use Lire::DataTypes qw/:basic :special/;

use POSIX qw/ strftime /;

use Carp;

BEGIN {
    ($VERSION)	= '$Revision: 1.5 $' =~ m!Revision: ([.\d]+)!;
}

=pod

=head1 NAME

Lire::Report - Interface to a Lire report.

=head1 SYNOPSIS

    use Lire::ReportParser::ReportBuilder;
    my $parser = new Lire::ReportParser::ReportBuilder;
    my $report = $parser->parse( "report.xml" );
    
    print $report->superservice, " report\n";
    print "Generated on ", scalar localtime $report->date, "\n";
    print "Timespan starts on ", scalar localtime $report->timespan_start, "\n";
    print "Timespan ends on ", scalar localtime $report->timespan_start, "\n";
    print "Hostname: ", $report->hostname, "\n";
    foreach my $s ( $report->sections ) {
	print "Section: '", $s->title, "' has ", scalar $s->subreports(),
	    " subreports in it\n";
    }

=head1 DESCRIPTION

This module offers an API to the report generated by Lire. The
Lire::ReportParser::ReportBuilder(3pm) can be used to create a
Lire::Report object from an XML file that comply with the Lire Report
Markup Language DTD. 

=head1 CONSTRUCTOR

A Lire::Report object can be built from a XML report file, from the
new method or from a Lire::ReportConfig object.

=head2 new( $superservice, $timespan_start, $timespan_end )

Creates a new Lire::Report. The $superservice, $timespan_start and
$timespan_end parameters initialized the attributes of the same name.

=cut

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

    my ( $super, $start, $end ) = @_;

    croak "invalid superservice: $super"
      unless check_superservice( $super );

    croak "invalid timespan_start parameter, it should be an integer: $start"
      unless check_number( $start );

    croak "invalid timespan_end parameter, it should be an integer: $end"
      unless check_number( $end );

    croak "timespan_end is lower than timespan_start: $start > $end"
      if $start > $end;

    bless { superservice => $super,
	    version	 => "1.0",
	    generator	 => __PACKAGE__ . "(3pm)",
	    date	 => time,
	    timespan_start => $start,
	    timespan_end => $end,
	    sections	 => [],
	    descriptions => [],
	  }, $class;
}

=pod

=head1 OBJECT METHODS

=head2 version()

Returns the version number of the report format which was used in the
external representation of this report. This currently should always
be 1.0.

=cut

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

=pod

=head2 generator([$generator)

Returns the generator string that will be outputted in comments when
the write_report() method is used. You can change that value by
passing a new value through the $generator parameter.

=cut

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

=pod

=head2 superservice()

Returns the superservice of this report.

=cut

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

=pod

=head2 hostname([$hostname])

Returns the hostname of this report.

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

=cut

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

=pod

=head2 date()

Returns the date in seconds since epoch on which this report was
generated.

=cut

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

=pod

=head2 timespan_start()

Returns the start of the timespan covered by this report in seconds
since epoch.

=cut

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

=pod

=head2 timespan_end()

Returns the end of the timespan covered by this report in seconds
since epoch.

=cut

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

=pod

=head2 title( [$title] )

Returns the report's title, if it has one.

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

=cut

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

=pod

=head2 target_users()

Returns an array of all the descriptions defined for this report.

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], [$description] )

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 sections()

Returns the report's sections as an array. This will be an array of
Lire::Report::Section objects.

=cut

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

=pod

=head2 add_section( $section )

Adds a section to this report. The $section parameter should be a
Lire::Report::Section object.

=cut

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

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

    push @{$self->{sections}}, $section; 
}

=pod

=head2 subreports_by_type( $type )

Returns all the subreports of a the type $type. The subreport's type
is the id of the report specification used to compute the reports.
This will search for subreports with this type in all the report's
sections. The subreports are returned as an array of
Lire::Report::Subreport objects.

=cut

sub subreports_by_type {
    my ( $self, $type ) = @_;

    my @subreports = ();
    foreach my $s ( $self->sections ) {
	push @subreports, $s->subreports_by_type( $type );
    }

    return @subreports;
}

=pod

=head2 write_report( [FH] );

Write the report in XML format on the FH filehandle or STDOUT if
omitted.

=cut

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

    $fh ||= \*STDOUT;

    my $time  = strftime '%Y-%m-%d %H:%M:%S %Z', localtime $self->date;
    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;

    # FIXME: We explicitely set the encoding to us-ascii because
    # it won't work otherwise. Parsers return data in UTF-8. We need
    # to handle the conversion.
    print $fh <<EOF;
<?xml version="1.0" encoding="us-ascii"?>
<!DOCTYPE lire:report PUBLIC
  "-//LogReport.ORG//DTD Lire Report Markup Language V1.0//EN"
  "http://www.logreport.org/LRML/1.0/lire.dtd">
<lire:report xmlns:lire="http://www.logreport.org/LRML/" version="$self->{version}" superservice="$self->{superservice}">
 <!-- generated by $self->{generator} -->
EOF
    print $fh " <lire:title>", xml_encode( $self->title ), "</lire:title>\n"
      if defined $self->title;

    print $fh <<EOF;
 <lire:date time="$self->{date}">$time</lire:date>
 <lire:timespan start="$self->{timespan_start}" end="$self->{timespan_end}">$stime - $etime</lire:timespan>
EOF

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

    foreach my $desc ( @{$self->{descriptions}} ) {
	print $fh " <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}};

    foreach my $s ( $self->sections ) {
	$s->write_report( $fh, 1 );
    }
    print "</lire:report>\n";
}

=pod

=head2 delete()

Removes all circular references so that the object can be freed.

=cut

sub delete {
    my ( $self ) = @_;
    
    foreach my $s ( $self->sections ) {
	foreach my $r ( $s->subreports ) {
	    $r->delete;
	}
    }
}

# keep perl happy
1;

__END__

=pod

=head1 SEE ALSO

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

Lire::ReportParser(3pm)

=head1 VERSION

$Id: Report.pm,v 1.5 2002/07/28 19:29:11 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
