package Lire::Config::XMLSpecListSpec;

use strict;

use base qw/Lire::Config::ListSpec/;

use Lire::Config::StringSpec;
use Lire::Config::ObjectSpec;
use Lire::Config::ListSpec;
use Lire::Config::ChartSpec;
use Lire::ReportSpec;
use Lire::FilterSpec;
use Lire::Utils qw/check_param/;
use Locale::TextDomain 'lire';

use Carp;

our $NAME_RE = qr/^([\w_-]+):([\w_.-]+)$/;

=pod

=head1 NAME

Lire::Config::XMLSpecListSpec - List of XML specifications.

=head1 DESCRIPTION

This Lire::Config::TypeSpec defines a special kind of ListSpec. It
overrides the has_component(), get(), component_names() and
components() methods so that every XML report specifications or filter
specifications are available. The specifications are created
dynamically at runtime.

=head2 new( 'name' => $name, 'type' => 'filters'|'reports )

Creates a new Lire::Config::XMLSpecListSpec object which will contains
a list of report or filter specifications configuration. The type
parameter is used to select whether Lire::ReportSpec or
Lire::FilterSpec will be creatd.

=cut

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

    my $type = { @_ }->{'type'};
    check_param( $type, 'type', qr/^filters|reports$/,
                 "'type' parameter should be either filters or reports" );

    $self->{'_type'} = $type;
    $self->{'_module'} = ( $type eq 'reports'
                           ? 'Lire::ReportSpec' : 'Lire::FilterSpec' );
    $self->{'_cache'} = {};

    return $self;
}

=pod

=head2 type()

Returns the type of specification contained by this object.

=cut

sub type {
    return $_[0]{'_type'};
}

=pod

=head2 has_component( $name )

This spec will return true when $name is of the form
I<superservice>:I<id> and there is an available I<id> specification
defined in I<superservice>. It returns false otherwise.

=cut

sub has_component {
    my ( $self, $name ) = @_;

    check_param( $name, 'name' );
    my ( $super, $type ) = $name =~ /$NAME_RE/;
    return 0 unless defined $super && defined $type;

    return Lire::DlfSchema->has_superservice( $super ) &&
      $self->{'_module'}->has_spec( $super, $type );
}

=pod

=head2 component_names()

Returns an array containing the configuration name of all available
specifications.

=cut

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

    my @specs = ();
    foreach my $super ( Lire::DlfSchema->superservices() ) {
        foreach my $type ( @{$self->{'_module'}->list_specs( $super )} ) {
            push @specs, "$super:$type";
        }
    }
    return @specs;
}

=pod

=head2 get( $name )

Returns an Lire::Config::ObjectSpec specification for the component
named $name. The ObjectSpec will instantiate a Lire::ReportSpec or
Lire::FilterSpec of the appropriate type.

=cut

sub get {
    my ( $self, $name ) = @_;

    croak "no component named '$name'" unless $self->has_component( $name );
    return $self->{'_cache'}{$name}
      if exists $self->{'_cache'}{$name};

    my ( $super, $type ) = $name =~ /$NAME_RE/;
    my $xml_spec = $self->{'_module'}->load( $super, $type );
    my $spec =
      new Lire::Config::ObjectSpec( 'name' => $name,
                                    'i18n_domain' => "lire-$super",
                                    'class' => $self->{'_module'},
                                    'summary' => $xml_spec->title(),
                                    'description' => $xml_spec->description(),
                                    'label' => 'id',
                                  );
    $spec->add( new Lire::Config::StringSpec( 'name' => 'id',
                                              'valid-re' => '^[\w.:-]+$',
                                              'summary' => N__( 'Identifier' ),
                                              'description' => '<para>' . join( "", N__( 'Identifier that uniquely identifies this specification among the report configuration. When two reports are merged, specifications with the same identifier are merged together.' ) ) . "</para>" ) );

    my $title = new Lire::Config::StringSpec( 'name' => 'title',
                                              'required' => 0,
                                              'summary' => N__( 'Custom Title' ),
                                              'description' => '<para>' . join( "", N__( 'If this parameter is set, its value will be used instead of the display title specified in the specification. This string will interpolate $name_ref with the parameter\'s value when the report is generated.' ) ) . '</para>' );
    $title->default( $title->instance( 'value' => $xml_spec->display_title()));
    $spec->add( $title );
    foreach my $name ( $xml_spec->param_names() ) {
        $spec->add( $xml_spec->param( $name )->as_type_spec() );
    }
    $self->_set_charts_spec( $xml_spec, $spec );

    return $self->{'_cache'}{$name} = $spec;
}

sub _set_charts_spec {
    my ( $self, $xml_spec, $spec ) = @_;

    return unless $xml_spec->isa( 'Lire::ReportSpec' );

    my $charts = new Lire::Config::ListSpec( 'name' => 'charts',
                                             'summary' => N__( 'Charts' ),
                                             'description' => '<para>' . join ( "", N__( "Parameters for the charts that should be generated from this subreport's data" ) ) . "</para>" );
    $spec->add( $charts );

    $charts->add( new Lire::Config::ChartSpec( 'name' => 'chart',
                                               'summary' => N__( 'Chart' ),
                                               'description' => '<para>' . join( "", N__( "Parameters for a chart that should be generated from this subreport's data." ) ) . "</para>" ) );
    my $default = $charts->instance();
    foreach my $cfg ( @{$xml_spec->chart_configs()} ) {
        my $default_chart = $cfg->clone();
        $default_chart->{'spec'} = $charts->get( 'chart' );
        $default->append( $default_chart );
    }
    $charts->default( $default );

    return;
}

=pod

=head2 components()

Returns an array containing all the Lire::Config::ObjectSpec defining
the available specifications.

=cut

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

    my @comps = ();
    foreach my $name ( $self->component_names() ) {
        push @comps, $self->get( $name );
    }
    return @comps;
}

1;

__END__

=pod

=head2 SEE ALSO

  Lire::Config::ReportSectionSpec(3pm), Lire::Config::ReportSpec(3pm),
  Lire::ReportSpec(3pm), Lire::FilterSpec(3pm).

=head1 VERSION

$Id: XMLSpecListSpec.pm,v 1.8 2006/07/23 13:16:31 vanbaal Exp $

=head1 AUTHOR

  Francis J. Lacoste <flacoste@logreport.org>

=head1 COPYRIGHT

Copyright (C) 2004  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.

=cut
