package Lire::Report::ColumnInfo;

use strict;

use Carp;
use POSIX qw/ ceil /;

use Lire::DataTypes qw/ check_type check_xml_name /;
use Lire::Utils qw/ check_param check_object_param /;

=pod

=head1 NAME

Lire::Report::ColumnInfo - Object that holds column meta-data.

=head1 SYNOPSIS

    my $info = $subreport->column_info_by_idx( 0 );
    print "Column's name: ", $info->name(),   "\n";
    print "Column's index: ", $info->index(), "\n";
    print "Column's type: ", $info->type(),   "\n";
    print "Column's label: ", $info->label(), "\n";

=head1 DESCRIPTION

The Lire::Report::ColumnInfo object contains meta-data information on
the columns of a subreport.

=head1 CONSTRUCTOR

=head2 new( $group, $name, $class, $type, [$label]])

Creates a new Lire::Report::ColumnInfo object. The column info
attributes are initialized based on $name, $class, $type and $label.

=cut

sub new {
    my ( $class, $group, $name, $type_class, $type, $label ) = @_;

    check_object_param( $group, 'group_info', 'Lire::Report::GroupInfo' );
    check_param( $name, 'name', \&check_xml_name,
                 "'name' parameter isn't a valid XML name" );
    check_param( $type_class, 'class', qr/^(numerical|categorical)$/,
                 "'class' parameter should either be 'numerical' or 'categorical'" );
    check_param( $type, 'type', \&check_type,
                 "'type' parameter isn't a valid Lire type" );

    my $self = bless {
                      'name'  => $name,
                      'class' => $type_class,
                      'type'  => $type,
                      'col_width' => 0,
                      'max_chars' => undef,
                      'avg_chars' => undef,
                      'padding' => 0,
                      'index' => undef,
                      'group_info' => $group,
                     }, $class;

    $self->label( $label )
      if ( defined $label );

    return $self;
}

=pod


=head1 OBJECT METHODS

=pod

=head2 name()

Returns the name of this column. This name can be used to find the
group operation (from the report specification) that is responsible
for the column's content.

=cut

sub name {
    return $_[0]{'name'};
}

=pod

=head2 class()

Returns the column's class of data. This will be either 'numerical' or
'categorical'.

=cut

sub class {
    return $_[0]{'class'};
}

=pod

=head2 type()

Returns the column's type. This referes to the type of the DLF field
that was usd to generate this column's values.

=cut

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

=pod

=head2 group_info()

Returns the GroupInfo object which contains this column.

=cut

sub group_info {
    return $_[0]{'group_info'};
}

=pod

=head2 index()

Returns the column's index in the table. This will be undef until the
column info object is added to a Lire::Subreport or Lire::Group
object.

=cut

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

    return $_[0]{'index'};
}

=pod

=head2  col_start()

Returns the display column which is the start of the cell span that
should be used to render this column's values.

=cut

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

    return $_[0]{'col_start'};
}

=pod

=head2  col_end()

Returns the display column which is the end of the cell span that
should be used to render this column's values.

=cut

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

    return $_[0]{'col_end'};
}

=pod

=head2 col_width()

Returns the suggested column width for this column in characters.

=cut

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

    return $_[0]{'col_width'};
}

=pod

=head2 max_chars()

This method returns the length of the longest string in that column
(including the label).

=cut

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

    $self->{'max_chars'} = $_[1] if @_ == 2;

    croak "max_chars() called while stat is undefined. Was subreport's finalize() called?"
      unless defined $self->{'max_chars'};

    return $self->{'max_chars'};
}

=pod

=head2 avg_chars()

This method returns the average length of strings in that column
(including the label). This will be round up to the next integer (1.3
-> 2).

=cut

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

    $self->{'avg_chars'} = $_[1] if @_ == 2;

    croak "avg_chars() called while stat is undefined. Was subreport's finalize() called?"
      unless defined $self->{'avg_chars'};

    return $self->{'avg_chars'};
}

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

    $self->{'total_chars'} = 0;
    $self->{'column_count'} = 0;
    $self->{'max_chars'} = length( $self->label() );

    return;
}

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

    my $len = length $value;
    $self->{'total_chars'} += $len;
    $self->{'column_count'}++;
    $self->{'max_chars'} = $len
      if $len > $self->{'max_chars'};

    return;
}

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

    my $len = length $self->label();

    $self->{'avg_chars'} = ceil( ($self->{'total_chars'} + $len) /
                                 ($self->{'column_count'} + 1) );

    delete $self->{'total_chars'};
    delete $self->{'column_count'};

    return;
}

=pod

=head2 label( [$label] )

Returns the column's label. If the $label parameter is defined, the
column's label will be set to this new value.

=cut

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

    $self->{'label'} = $label
      if ( @_ == 2 );

    return ( $self->{'label'} ? $self->{'label'} : $self->{'name'} );
}

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

    $fh ||= *STDOUT;
    my $pfx = ' ' x $indent;

    print $fh $pfx, qq{<lire:column-info name="$self->{'name'}" class="$self->{'class'}" type="$self->{'type'}"};
    print $fh qq{ col-start="$self->{'col_start'}" col-end="$self->{'col_end'}" col-width="$self->{'col_width'}"};
    print $fh qq{ label="$self->{'label'}"}
      if $self->{'label'};
    print $fh qq{ max-chars="$self->{'max_chars'}" avg-chars="},
      $self->avg_chars, qq{"/>\n};

    return;
}

#------------------------------------------------------------------------
# Method delete()
#
# Remove circular references
sub delete {
    my ( $self ) = @_;

    %$self = ();

    return;
}

# keep perl happy
1;

__END__

=pod

=head1 SEE ALSO

Lire::Report::Subreport(3pm) Lire::Report::GroupInfo(3pm)

=head1 VERSION

$Id: ColumnInfo.pm,v 1.15 2006/07/23 13:16:31 vanbaal 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. 

=head1 AUTHOR

Francis J. Lacoste <flacoste@logreport.org>

=cut
