package Lire::GroupOp;

use strict;

use Lire::DataTypes qw( :special );
use Carp;

use vars qw( $VERSION );

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

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

    my %args = @_;

    my $report_spec = $args{report_spec};
    croak "report_spec argument ($report_spec) must be of type Lire::ReportSpec"
      unless UNIVERSAL::isa( $report_spec, "Lire::ReportSpec" );


    croak "missing op parameter"
      unless defined $args{op};

    croak "missing parent parameter"
      unless defined $args{parent};
    croak "parent argument ($args{parent}) must be of type Lire::Aggregator"
      unless UNIVERSAL::isa( $args{parent}, "Lire::Aggregator" );

    croak "missing name parameter"
      unless defined $args{name};

    my $self = bless { report_spec  => $report_spec,
		       parent	    => $args{parent},
		       op	    => $args{op},
		       name	    => "", # Prevent warning in name()
		     }, $class;

    $self->name( $args{name} );

    $self;
}

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

    if (@_ == 2 ) {
	croak "$name isn't valid as field name"
	  unless check_field( $name );

	if (defined $self->{parent}) {
	    croak "$name is already defined"
	      if $name ne $self->{name} &&
		$self->{parent}->is_name_defined( $name );
	}

	$self->{name} = $name;
    }

    $self->{name};
}

sub print {
    my ($self,$fh, $prefix) = @_;
    $fh	    ||= \*STDOUT;
    $prefix ||= 0;

    my $pfx = " " x $prefix;
    my $attrs = $self->xml_attrs;

    print $fh $pfx, qq{<lire:$self->{op} name="$self->{name}"};
    print $fh $attrs if length $attrs;
    print $fh "/>\n";
}

sub xml_attrs {
    return "";
}

package Lire::GroupOp::SimpleStat;

use vars qw( @ISA );

use Lire::DataTypes qw( :basic );
use Carp;

BEGIN {
    @ISA = qw( Lire::GroupOp );
}

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

    my %args = @_;
    croak "missing 'field' argument to " . __PACKAGE__ . "::new"
      unless defined $args{field};

    $self->field(  $args{field} );

    $self;
}

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

    if ( @_ == 2 ) {
	croak "$field isn't a defined field in the ",
	  $self->{report_spec}->schema->id, " schema"
	    unless $self->{report_spec}->schema->has_field( $field );

	croak "$field isn't a numeric type",
	  unless is_numeric_type( $self->{report_spec}->schema()->field( $field )->type );

	$self->{field} = $field;
    }
    $self->{field};
}

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

    return ($self->{report_spec}->schema()->field( $self->field ));
}

sub xml_attrs {
    return qq{ field="$_[0]{field}"};
}

package Lire::GroupOp::Count;

use vars qw( @ISA );

use Carp;

BEGIN {
    @ISA = qw( Lire::GroupOp );
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = $class->SUPER::new( @_, op => "count" );

    my %args = @_;
    $self->fields(  $args{fields} )
      if exists $args{fields};

    $self;
}

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

    if ( @_ == 2 ) {
	if ( defined $fields ) {
	    croak "$fields isn't an array reference"
	      unless UNIVERSAL::isa( $fields, 'ARRAY' );

	    foreach my $f ( @$fields ) {
		croak "$f isn't a defined field in the ",
		  $self->{report_spec}->schema->id, " schema"
		    unless $self->{report_spec}->schema()->has_field( $f );
	    }
	}
	$self->{fields} = $fields;
    }
    $self->{fields};
}

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

    my @fields = ();

    my $names = $self->{fields};
    return () unless defined $names;

    foreach my $f ( @$names ) {
	push @fields, $self->{report_spec}->schema()->field( $f );
    }

    @fields;
}

sub xml_attrs {
    my ($self) = @_;
    if ( exists $self->{fields} ) {
	my $fields = join " ", @{$self->{fields}};
	return qq{ fields="$fields"};
    } else {
	return "";
    }
}

package Lire::GroupOp::Sum;

use vars qw( @ISA );

BEGIN {
    @ISA = qw( Lire::GroupOp::SimpleStat );
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = $class->SUPER::new( @_, op => "sum" );
}

package Lire::GroupOp::Avg;

use Carp;

use vars qw( @ISA );

BEGIN {
    @ISA = qw( Lire::GroupOp );
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = $class->SUPER::new( @_, op => "avg" );

    my %args = @_;
    $self->field(  $args{field} );
    $self->by_fields(  $args{'by-fields'} )
      if exists $args{'by-fields'};

    $self;
}

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

    if ( @_ == 2 ) {
	if ( defined $field ) {
	    croak "$field isn't a defined field in the ",
	      $self->{report_spec}->schema->id, " schema"
		unless $self->{report_spec}->schema->has_field( $field );
	}
	$self->{field} = $field;
    }

    $self->{field};
}

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

    if ( @_ == 2 ) {
	if ( defined $by_fields ) {
	    croak "$by_fields isn't an array reference"
	      unless UNIVERSAL::isa( $by_fields, 'ARRAY' );

	    foreach my $f ( @$by_fields ) {
		croak "$f isn't a defined field in the ",
		  $self->{report_spec}->schema->id, " schema"
		    unless $self->{report_spec}->schema->has_field( $f );
	    }
	}
	$self->{by_fields} = $by_fields;
    }
    $self->{by_fields};
}

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

    my @fields = ();
    push @fields, $self->{report_spec}->schema()->field( $self->field )
      if $self->field;

    my $names = $self->{by_fields};
    return @fields unless defined $names;

    foreach my $f ( @$names ) {
	push @fields, $self->{report_spec}->schema()->field( $f );
    }

    @fields;
}

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

    my $attr = $self->SUPER::xml_attrs;
    my $field = $self->field;;
    $attr .= qq{ field="$field"} if $field;
    if ( exists $self->{by_fields} ) {
	my $by_fields = join " ", @{$self->{by_fields}};
	$attr .= qq{ by-fields="$by_fields"};
    }
    $attr;
}

package Lire::GroupOp::Min;

use vars qw( @ISA );

BEGIN {
    @ISA = qw( Lire::GroupOp::SimpleStat );
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = $class->SUPER::new( @_, op => "min" );
}

package Lire::GroupOp::Max;

use vars qw( @ISA );

BEGIN {
    @ISA = qw( Lire::GroupOp::SimpleStat );
}

sub new {
    my $proto = shift;
    my $class = ref $proto || $proto;
    my $self  = $class->SUPER::new( @_, op => "max" );
}

# keep perl happy
1;

__END__

=pod

=head1 NAME

Lire::GroupOp -

=head1 SYNOPSIS


=head1 DESCRIPTION

=head1 VERSION

$Id: GroupOp.pm,v 1.8 2002/03/23 23:33:54 flacoste Exp $

=head1 COPYRIGHT

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