package Lire::Merge::Group;

use strict;

use vars qw( $VERSION @ISA );

use Lire::Group;
use Lire::Merge::NestableAggregator;
use Lire::DataTypes qw( :basic );
use Lire::Report::Entry;
use Lire::AsciiDlf::Group;

use Carp;

BEGIN {
    ($VERSION)	= '$Revision: 1.5 $' =~ m!Revision: ([.\d]+)!;
    @ISA = qw( Lire::Group Lire::Merge::NestableAggregator );

    # Create alias
    *guess_extra_entries = \&Lire::AsciiDlf::Group::guess_extra_entries;
}

sub init_merge {
    my ( $self ) = shift;

    if ( defined $self->limit ) {
	my $limit = $self->limit;
	if ( $limit =~ /^\$/ ) {
	    $limit = substr $limit, 1;
	    $limit = $self->{report_spec}->param( $limit )->value;
	}
	$self->{limit_view} = $limit;

	# For better report merging, we add some entries.
	$self->{limit_num} = $limit + guess_extra_entries( $limit );
    }

    if ( $self->sort_fields ) {
	# Build sort function
	my ( $a_dflt, $b_dflt, $schwartzian );
	$a_dflt = '$a';
	$b_dflt = '$b';
	$schwartzian = '[1]'; # Using schwartzian transform

	my @sort_ops = ();
	foreach my $f ( @{$self->sort_fields} ) {
	    my ($a, $b) = ($a_dflt, $b_dflt );
	    if ( $f =~ /^-/ ) {
		$f = substr $f, 1;
		($a,$b) = ($b,$a);
	    }

	    my $index;	# This will contains the index of the field in the array
	    my $cmp = '<=>'; # Default to numeric comparison
	    my $i = 0;
	    foreach my $group_field ( @{$self->group_fields} ) {
		if ( $group_field->name eq $f ) {
		    $index = $i;
		    if ( is_numeric_type( $group_field->field->type ) ) {
			$cmp = "<=>";
		    } else {
			$cmp = "cmp";
		    }
		    last;
		}
		$i++;
	    }
	    $i = @{$self->group_fields};
	    unless (defined $index) {
		foreach my $op ( @{$self->ops} ) {
		    if ( $op->name eq $f ) {
			$index = $i;
			last;
		    }
		    $i++;
		}
	    }
	    push @sort_ops, $a ."->" . $schwartzian . "[$index] $cmp " .
	      $b ."->" . $schwartzian . "[$index]";
	}
	my $sort_code = "sub { " . join( " || ", @sort_ops ) . " }";
	$self->{sort_cmp} = eval $sort_code;
	croak "error compiling sort comparison ($sort_code): $@" if $@;
    }

    $self->SUPER::init_merge( @_ );
}

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

    # The group datastructure used to hold the operations' data of the
    # group element is an array. It contains the fields value and the
    # op data: 
    # [ group field, group field, ..., op data, op data, op data ]

    # For better performance, at the cost of higher memory footprint,
    # we don't need sorted input and keeps related keys using an hash.
    return {}
}

sub merge_entries {
    my ( $self, $group, $data ) = @_;

    $data ||= $self->{data};

    foreach my $e ( $group->entries ) {
	my $key = join( ";", map { $_->{value} } $e->names );
	my $key_data = $data->{$key};
	unless ( $key_data ) {
	    $key_data = $data->{$key} = [];

	    my $i = 0;
	    my @names = $e->names;

	    die "wrong number of names: expected ", 
	      scalar @{$self->group_fields}, " but found ", scalar @names
		if @names != @{$self->group_fields};

	    foreach my $f ( @{$self->group_fields} ) {
		$key_data->[$i] = $names[$i]{value};
		$i++;
	    }

	    foreach my $op ( @{$self->ops} ) {
		$key_data->[$i++] = $op->init_group_data();
	    }
	}

	my @values = $e->values;
	die "wrong number of values: expected ",
	  scalar @{$self->ops}, " but found ", scalar @values
	    if @values != @{$self->ops};

	my $i = @{$self->group_fields};
	my $start = $i;
	foreach my $op ( @{$self->ops} ) {
	    $op->merge_entry_value( $values[$i-$start], $key_data->[$i++] );
	}
    }
}

sub item_data2sort_key {
   my $key = [];
   foreach my $f ( @{$_[0]} ) {
       if ( ref $f eq 'SCALAR' ) {
	   push @$key, $$f;
       } elsif (ref $f eq 'ARRAY' ) {
	   push @$key, $f->[0];
       } elsif (ref $f eq 'HASH' ) {
	   push @$key, $f->{value};
       } else {
	   push @$key, $f;
       }
   }
   return $key;
}

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

    foreach my $key ( keys %$data ) {
	my $item = $data->{$key};
	my $i = @{$self->group_fields};
	foreach my $op ( @{$self->ops} ) {
	    $op->end_group_data( $item->[$i++] );
	}
    }

    # Sort the keys according to the sort value
    my @sorted_keys;
    if ( $self->sort_fields ) {
	my $cmp = $self->{sort_cmp};
	# This uses schwartzian transform
	@sorted_keys =  map { $_->[0] } sort $cmp 
	  map { [ $_, item_data2sort_key( $data->{$_} ) ] } keys %$data;
    } else {
	@sorted_keys = keys %$data;
    }

    # Keep only limit records
    if ( $self->{limit_num} ) {
	my $limit = $self->{limit_num};
	splice @sorted_keys, $limit
	  if ($limit < @sorted_keys );
    }

    # Delete unused keys
    %$data = map { $_ => $data->{$_} } @sorted_keys;
    $data->{_lr_sorted_keys} = \@sorted_keys;

    $data;
}

sub create_entries {
    my ( $self, $group, $data ) = @_;

    $data ||= $self->{data};

    $group->show( $self->{limit_view} )
      if $self->{limit_view};

    # Either to the sorted group data
    # or the sorted keys
    my $array_ref = $data->{_lr_sorted_keys};

    my $field_count = @{$self->group_fields};
    foreach my $elmnt ( @$array_ref ) {
	my $item = $data->{$elmnt};

	my $entry = new Lire::Report::Entry;
	my $i = 0;
	while  ( $i < $field_count ) {
	    $entry->add_name( $item->[$i++] );
	}
	foreach my $op ( @{$self->ops} ) {
	    $op->add_entry_value( $entry, $item->[$i++] );
	}
	$group->add_entry( $entry );
    }
}

# keep perl happy
1;

__END__

=pod

=head1 NAME

Lire::Merge::Group -

=head1 SYNOPSIS


=head1 DESCRIPTION

=head1 VERSION

$Id: Group.pm,v 1.5 2002/06/18 19:31:03 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
