package Lire::Merge::Timeslot;

use strict;

use vars qw/ $VERSION @ISA %DAYS %MONTHS $WEEK_FMT
	     @SLOT_UNIT @MAX_SLOTS @SLOT_INDEX @SLOT_FMT /;

use Lire::Timeslot;
use Lire::Merge::NestableAggregator;
use Lire::DataTypes qw/ :time /;
use Lire::Report::Entry;

use POSIX qw/ strftime /;
use Time::Local;

use Carp;

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

    # FIXME: This is identical than in Lire::AsciiDlf::Timeslot

    # We don't want to use the strftime because of possible
    # locale charsets problems
    my $i = 0;
    %MONTHS = map { $i++ => $_ } qw/ January February March
				     April   May      June
				     July    August   September
				     October November December /;

    $i = 0;
    %DAYS = map { $i++ => $_ } qw/  Sunday   Monday Tuesday  Wednesday
				    Thursday Friday Saturday /;

    if ($Lire::Config::LR_WEEK_STARTS_ON eq 'mon') {
	$WEEK_FMT = '%W';
    } else {
	$WEEK_FMT = '%U';
    }

    @SLOT_UNIT  = qw/1M 1w 1d 1h 1m 1s/;
    @MAX_SLOTS  = qw/12 53 7  24 60 60/;
    @SLOT_INDEX = qw/4  wk 6  2  1  0/;
    @SLOT_FMT	= qw/mo wk dw %H:00 00:%M 00:00:%S/;
}

# FIXME: Near identical as the one in Lire::AsciiDlf::Timeslot
# We only dropped the field index.
sub init_merge {
    my ($self) = shift;

    my $unit = $self->unit;
    if ( $unit =~ /^\$/ ) {
	$unit = substr $unit, 1;
	$unit = $self->{report_spec}->param( $unit )->value;
    }
    my $unit_sec = duration2sec( $unit );

    # Determine if we have a valid duration value for a timeslot
    my $unit_ok = 0;
    for (my $i=0; $i < @SLOT_UNIT; $i++ ) {
	my $sec = duration2sec( $SLOT_UNIT[$i] );
	if ( $unit_sec >= $sec && !( $unit_sec % $sec ) ) {
	    croak "$unit is a too big muliple of $SLOT_UNIT[$i]. ",
	      "Max is $MAX_SLOTS[$i]"
		if $unit_sec > $MAX_SLOTS[$i] * $sec;

	    $self->{slot_index} = $SLOT_INDEX[$i];
	    $self->{time_fmt}	= $SLOT_FMT[$i];
	    $self->{multiplier} = $unit_sec / $sec;

	    croak "$unit isn't a valid divisor of $MAX_SLOTS[$i]"
	      if $MAX_SLOTS[$i] % $self->{multiplier};

	    $unit_ok = 1;
	    last;
	}
    }
    croak "invalid timeslot value: must be an even multiple of 1M, 1d, 1h, 1m or 1s"
      unless $unit_ok;

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

    $self;
}

# FIXME: Identical as the one in Lire::AsciiDlf::Timeslot
sub init_group_data {
    return [];
}

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

    return unless $group->entries;

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

    foreach my $e ( $group->entries ) {
	my @names = $e->names;
	die "wrong number of names for a timeslot subreport: ",
	  scalar @names, "\n" unless @names == 1;
	my $slot = $names[0]{value};
	my $mult = $names[0]{range};

	# Are the multipliers compatible?
	# They are only if the target multiplier is a multiple 
	# of the source one.
	# Non-multiple would need interpolation, which would be hard
	# to achieve for other operation than sum and count
	die "incompatible unit multiplier: $self->{multiplier} isn't a multiple of $mult\n"
	  unless $self->{multiplier} % $mult == 0;

	# Are the used slot compatible?
	my $index;
	if ( $self->{time_fmt} eq 'mo' ) {
	    die "incompatible timeslot unit: $slot\n"
	      unless $slot =~ /^M(\d+)$/;
	    $index = $1;
	} elsif ( $self->{time_fmt} eq 'dw' ) {
	    die "incompatible timeslot unit: $slot\n"
	      unless $slot =~ /^D(\d+)$/;
	    $index = $1;
	} elsif ( $self->{time_fmt} eq 'wk' ) {
	    die "incompatible timeslot unit: $slot\n"
	      unless $slot =~ /^W(\d+)$/;
	    $index = $1;
	} else {
	    my $type;
	    ( $type, $index ) = $slot =~ /^(\d)-(\d+)$/
	      or die "invalid timeslot's value attribute: $slot\n";

	    die "incompatible timeslot unit: $slot\n"
	      if $type != $self->{slot_index};
	}

	# Map index when target multiplier is greater than the original one.
	# For example: original = 2h, target = 4h
	# Original:  0 2 4 6 8 10 12 14 16 18 20 22 -> 12 indices
	# Target:    0   4   8    12    16    20    -> 6  indices

	my $idx = int( $index * $mult / $self->{multiplier} );

	my $data = $timeslots->[$idx];
	unless ( defined $data ) {
	    $data = [];

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

	    $timeslots->[$idx] = $data;
	}

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

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

    }

    $self;
}

# FIXME: Identical as the one in Lire::AsciiDlf::Timeslot
sub end_group_data {
    my ( $self, $timeslots ) = @_;

    # Finalize each timeslice
    # Either create empty one or call end_group_data on them
    my $last_idx;
    if ( $self->{slot_index} eq 'wk' ) {
	$last_idx  = 52;
    } else {
	$last_idx  = ( 59, 59, 23, 0, 11, 0, 6 )[$self->{slot_index}];
    }
    $last_idx = int($last_idx / $self->{multiplier});

    my $i = 0;
    while ( $i <= $last_idx) {
	if ( $timeslots->[$i]) {
	    my $data = $timeslots->[$i];
	    my $j = 0;
	    foreach my $op ( @{$self->ops} ) {
		$op->end_group_data( $data->[$j++] );
	    }
	} else {
	    # Create empty set
	    my $data =[];
	    my $j = 0;
	    foreach my $op ( @{$self->ops} ) {
		$data->[$j] = $op->init_group_data();
		$op->end_group_data( $data->[$j++] );
	    }

	    $timeslots->[$i] = $data;
	}
	$i++;
    }

    $self;
}

# FIXME: Identical as the one in Lire::AsciiDlf::Timeslot
sub create_entries {
    my ( $self, $group, $timeslots ) = @_;

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

    for ( my $i=0; $i < @$timeslots; $i++ ) {
	my $tslice = $timeslots->[$i];
	my $entry = new Lire::Report::Entry;

	# FIXME: Special backward-compatibility hack.
	# This should be done in the stylesheet.
	if ( $self->{time_fmt} eq 'mo' ) {
	    $entry->add_name( $MONTHS{$i}, "M$i", $self->{multiplier} );
	} elsif ( $self->{time_fmt} eq 'dw' ) {
	    $entry->add_name( $DAYS{$i}, "D$i", $self->{multiplier} );
	} elsif ( $self->{time_fmt} eq 'wk' ) {
	    my $wk = sprintf( '%02d', $i + 1);
	    $entry->add_name( "Week $wk", "W$i", $self->{multiplier} );
	} else {
	    my $fmt = $self->{time_fmt};
	    my @time = localtime;
	    for (my $u=0; $u < $self->{slot_index}; $u++ ) {
		$time[$u] = 0;
	    }
	    $time[$self->{slot_index}] = $i * $self->{multiplier};
	    $entry->add_name( strftime( $fmt, @time ),
			      "$self->{slot_index}-$i", $self->{multiplier} );
	}

	my $j = 0;
	foreach my $op ( @{$self->ops} ) {
	    $op->add_entry_value( $entry, $tslice->[$j++] );
	}

	$group->add_entry( $entry );
    }

    $self;
}


# keep perl happy
1;

__END__

=pod

=head1 NAME

Lire::Merge::Timeslot -

=head1 SYNOPSIS


=head1 DESCRIPTION

=head1 VERSION

$Id: Timeslot.pm,v 1.3 2002/06/18 14:42:38 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
