# Copyright (c) 2010 Ars Aperta, Itaapy, Pierlis, Talend.
#
# Author: Jean-Marie Gouarné <jean-marie.gouarne@arsaperta.com>
#
# This file is part of lpOD (see: http://lpod-project.org).
# Lpod is free software; you can redistribute it and/or modify it under
# the terms of either:
#
# a) the GNU General Public License as published by the Free Software
#    Foundation, either version 3 of the License, or (at your option)
#    any later version.
#    Lpod 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 lpOD.  If not, see <http://www.gnu.org/licenses/>.
#
# b) the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#    http://www.apache.org/licenses/LICENSE-2.0
#-----------------------------------------------------------------------------
use     5.010_000;
use     strict;
#-----------------------------------------------------------------------------
#       Tables
#-----------------------------------------------------------------------------
package ODF::lpOD::Table;
use base 'ODF::lpOD::Element';
our $VERSION    = 0.1;
use constant PACKAGE_DATE => '2010-06-19T19:23:38';
use ODF::lpOD::Common;
#=============================================================================

our $ROW_FILTER         = 'table:table-row';
our $COLUMN_FILTER      = 'table:table-column';

#-----------------------------------------------------------------------------

sub	translate_coordinates   # taken from OpenOffice::OODoc (Genicorp)
	{
	my $arg	= shift; return ($arg, @_) unless defined $arg;
	my $coord = uc $arg;
	return ($arg, @_) unless $coord =~ /[A-Z]/;

	$coord	=~ s/\s*//g;
	$coord	=~ /(^[A-Z]*)(\d*)/;
	my $c	= $1;
	my $r	= $2;
	return ($arg, @_) unless ($c && $r);

	my $rownum	= $r - 1;
	my @csplit	= split '', $c;
	my $colnum	= 0;
	foreach my $p (@csplit)
		{
		$colnum *= 26;
		$colnum	+= ((ord($p) - ord('A')) + 1);
		}
	$colnum--;

	return ($rownum, $colnum, @_);
	}

#--- constructor -------------------------------------------------------------

sub     create
        {
        my $name        = shift;
        unless ($name)
                {
                alert "Missing table name";
                return FALSE;
                }

        my %opt = process_options
                (
                style           => undef,
                display         => undef,
                protected       => undef,
                key             => undef,
                @_
                );

        my $width       = $opt{width}   // 0;
        my $height      = $opt{height}  // 0;
        if ($width < 0 || $height < 0)
                {
                alert "Wrong table size ($height x $width)";
                return FALSE;
                }

        my $t = odf_element->new('table:table');
        $t->set_attribute('name', $name);
        $t->set_attribute('style name', $opt{style});
        $t->set_attribute('protected', odf_boolean($opt{protected}));
        $t->set_attribute('protection key', $opt{key});
        $t->set_attribute('display', odf_boolean($opt{display}));
        $t->set_attribute('print', odf_boolean($opt{print}));
        $t->set_attribute('print ranges', $opt{print_ranges});
        
        $t->add_column(number => $width);
        for (my $i = 0 ; $i < $height ; $i++)
                {
                my $r = $t->add_row();
                $r->add_cell(number => $width);
                }
        
        return $t;
        }

#-----------------------------------------------------------------------------

sub     get_row
        {
        my $self        = shift;
        my $position    = shift || 0;
        my $height      = $self->get_height;

        if ($position < 0)
                {
                $position += $height;
                }
        if (($position >= $height) || ($position < 0))
                {
                alert "Row position $position out of range";
                return undef;
                }

        my $row = $self->first_child($ROW_FILTER)
                or return undef;
        my $p = 0;
        do      {
                my $next_elt = $row->next_sibling($ROW_FILTER);
                $p += $row->repeat(); $p++;
                $row = $next_elt;
                } until $p >= $position;
        return $self->child($position, $ROW_FILTER);
        }

sub     get_column
        {
        my $self        = shift;
        my $position    = shift || 0;
        my $width       = $self->get_column_count;
        if ($position < 0)
                {
                $position += $width;
                }
        if (($position >= $width) || ($position < 0))
                {
                alert "Column position $position out of range";
                return undef;
                }

        my $col = $self->first_child($COLUMN_FILTER)
                or return undef;
        my $p = 0;
        do      {
                my $next_elt = $col->next_sibling($COLUMN_FILTER);
                $p += $col->repeat(); $p++;
                $col = $next_elt;
                } until $p >= $position;
        return $self->child($position, $COLUMN_FILTER);
        }

sub     get_cell
        {
        my $self        = shift;
        my ($r, $c) = ODF::lpOD::Table::translate_coordinates(@_);
        my $row = $self->get_row($r)    or return undef;
        return $row->get_cell($c);
        }

sub     add_row
        {
        my $self        = shift;
        my %opt         =
                (
                number          => 1,
                @_
                );
        my $ref_elt     = $opt{before} || $opt{after};
        my $expand      = $opt{expand};
        my $position    = undef;
        if ($ref_elt)
                {
                if ($opt{before} && $opt{after})
                        {
                        alert "'before' and 'after' are mutually exclusive";
                        return FALSE;
                        }
                $position = $opt{before} ? 'before' : 'after';
                unless  (
                        $ref_elt->isa(odf_row)
                                &&
                        $ref_elt->parent() == $self
                        )
                        {
                        alert "Wrong $position reference";
                        return FALSE;
                        }
                }
        my $number = $opt{number};
        return undef unless $number && ($number > 0);
        delete @opt{qw(number before after expand)};
        my $elt = odf_create_row(%opt);
        if ($ref_elt)
                {
                $elt->paste($position, $ref_elt);
                }
        else
                {
                $elt->paste_last_child($self);
                }
        if (defined $number && $number > 1)
                {
                if (is_true($expand))
                        {
                        $elt->repeat($number);
                        }
                else
                        {
                        $elt->set_repeated($number);
                        }
                }
        return $elt;
        }

sub     add_column
        {
        my $self        = shift;
        my %opt         =
                (
                number          => 1,
                @_
                );
        my $ref_elt     = $opt{before} || $opt{after};
        my $expand      = $opt{expand};
        my $position    = undef;
        if ($ref_elt)
                {
                if ($opt{before} && $opt{after})
                        {
                        alert "'before' and 'after' are mutually exclusive";
                        return FALSE;
                        }
                $position = $opt{before} ? 'before' : 'after';
                unless  (
                        $ref_elt->isa(odf_column)
                                &&
                        $ref_elt->parent() == $self
                        )
                        {
                        alert "Wrong $position reference";
                        return FALSE;
                        }
                }
        my $number = $opt{number};
        return undef unless $number && ($number > 0);
        delete @opt{qw(number before after expand)};
        my $elt = odf_create_column(%opt);
        if ($ref_elt)
                {
                $elt->paste($position, $ref_elt);
                }
        else
                {
                $elt->paste_last_child($self);
                }
        if (defined $number && $number > 1)
                {
                if (is_true($expand))
                        {
                        $elt->repeat($number);
                        }
                else
                        {
                        $elt->set_repeated($number);
                        }
                }
        return $elt;
        }

#-----------------------------------------------------------------------------

sub     get_height
        {
        my $self        = shift;
        my $height      = 0;
        my $row         = $self->first_child($ROW_FILTER);
        while ($row)
                {
                $height += $row->get_repeated;
                $row = $row->next_sibling($ROW_FILTER);
                }
        return $height;
        }

sub     get_column_count
        {
        my $self        = shift;
        my $count       = 0;
        my $col         = $self->first_child($COLUMN_FILTER);
        while ($col)
                {
                $count += $col->get_repeated;
                $col = $col->next_sibling($COLUMN_FILTER);
                }
        return $count;        
        }

sub     get_size
        {
        my $self        = shift;
        my $height      = 0;
        my $width       = 0;
        my $row         = $self->first_child($ROW_FILTER);
        while ($row)
                {
                $height += $row->get_repeated;
                my $row_width = $row->get_width;
                $width = $row_width if $row_width > $width;
                $row = $row->next_sibling($ROW_FILTER);
                }
        return ($height, $width);
        }

sub     contains
        {
        my $self        = shift;
        my $expr        = shift;
        my $segment     = $self->first_descendant(TEXT_SEGMENT);
        while ($segment)
                {
                my %r = ();
                my $t = $segment->get_text;
                return $segment if $t =~ /$expr/;
                $segment = $segment->next_elt($self, TEXT_SEGMENT);
                }
        return FALSE;
        }

#=============================================================================
#       Table columns
#-----------------------------------------------------------------------------
package ODF::lpOD::Column;
use base 'ODF::lpOD::Element';
our $VERSION    = 0.1;
use constant PACKAGE_DATE => '2010-06-10T12:38:06';
use ODF::lpOD::Common;
#-----------------------------------------------------------------------------

sub     create
        {
        my %opt = process_options
                (
                style   => undef,
                @_
                );

        my $col = odf_element->new('table:table-column') or return undef;
        $col->set_attribute('style name', $opt{style})
                        if defined $opt{style};
        delete $opt{style};
        foreach my $a (keys %opt)
                {
                $col->set_attribute($a, $opt{$a});    
                }
        return $col;
        }

#-----------------------------------------------------------------------------

sub     get_repeated
        {
        my $self        = shift;
        return $self->get_attribute('table:number-columns-repeated') // 1;  #/
        }

sub     set_repeated
        {
        my $self        = shift;
        return $self->set_attribute('table:number-columns-repeated', shift);
        }

#=============================================================================
#       Table rows
#-----------------------------------------------------------------------------
package ODF::lpOD::Row;
use base 'ODF::lpOD::Element';
our $VERSION    = 0.1;
use constant PACKAGE_DATE => '2010-06-14T21:37:23';
use ODF::lpOD::Common;
#-----------------------------------------------------------------------------

our     $CELL_FILTER    = qr'table:(covered-|)table-cell';

#-----------------------------------------------------------------------------

sub     create
        {
        my %opt = process_options
                (
                style   => undef,
                @_
                );

        my $row = odf_element->new('table:table-row') or return undef;
        $row->set_attribute('style name', $opt{style})
                        if defined $opt{style};
        delete $opt{style};
        foreach my $a (keys %opt)
                {
                $row->set_attribute($a, $opt{$a});    
                }
        return $row;
        }

#-----------------------------------------------------------------------------

sub     get_cell
        {
        my $self        = shift;
        my $position    = shift || 0;
        my $width       = $self->get_width;
        if ($position < 0)
                {
                $position += $width;
                }
        if (($position >= $width) || ($position < 0))
                {
                alert "Cell position $position out of range";
                return undef;
                }

        my $cell = $self->first_child($CELL_FILTER)
                or return undef;
        my $p = 0;
        do      {
                my $next_elt = $cell->next_sibling($CELL_FILTER);
                $p += $cell->repeat(); $p++;
                $cell = $next_elt;
                } until $p >= $position;
        return $self->child($position, $CELL_FILTER);
        }

sub     get_width
        {
        my $self        = shift;
        my $width       = 0;
        my $cell        = $self->first_child($CELL_FILTER);
        while ($cell)
                {
                $width += $cell->get_repeated;
                $cell = $cell->next_sibling($CELL_FILTER);
                }
        return $width;
        }

sub     add_cell
        {
        my $self        = shift;
        my %opt         =
                (
                number          => 1,
                @_
                );
        my $ref_elt    = $opt{before} || $opt{after};
        my $expand      = $opt{expand};
        my $position    = undef;
        if ($ref_elt)
                {
                if ($opt{before} && $opt{after})
                        {
                        alert "'before' and 'after' are mutually exclusive";
                        return FALSE;
                        }
                $position = $opt{before} ? 'before' : 'after';
                unless  (
                        $ref_elt->isa(odf_cell)
                                &&
                        $ref_elt->parent() == $self
                        )
                        {
                        alert "Wrong $position reference";
                        return FALSE;
                        }
                }
        my $number = $opt{number};
        return undef unless $number && ($number > 0);
        delete @opt{qw(number before after expand)};
        my $elt = odf_create_cell(%opt);
        if ($ref_elt)
                {
                $elt->paste($position, $ref_elt);
                }
        else
                {
                $elt->paste_last_child($self);
                }
        if (defined $number && $number > 1)
                {
                if (is_true($expand))
                        {
                        $elt->repeat($number);
                        }
                else
                        {
                        $elt->set_repeated($number);
                        }
                }
        return $elt;
        }

#-----------------------------------------------------------------------------

sub     get_repeated
        {
        my $self        = shift;
        return $self->get_attribute('table:number-rows-repeated') // 1;  #/
        }

sub     set_repeated
        {
        my $self        = shift;
        return $self->set_attribute('table:number-rows-repeated', shift);
        }

#=============================================================================
#       Table cells
#-----------------------------------------------------------------------------
package ODF::lpOD::Cell;
use base 'ODF::lpOD::Field';
our $VERSION    = 0.1;
use constant PACKAGE_DATE => '2010-06-19T19:59:45';
use ODF::lpOD::Common;
#-----------------------------------------------------------------------------
our     %ATTRIBUTE;
#-----------------------------------------------------------------------------

sub     create
        {
        my $cell = odf_create_field('table:table-cell', @_);
        return $cell ? bless($cell, __PACKAGE__) : undef;
        }

#-----------------------------------------------------------------------------

sub     set_repeated
        {
        my $self        = shift;
        return $self->set_attribute('table:number-columns-repeated', shift);
        }

sub     get_repeated
        {
        my $self        = shift;
        return $self->get_attribute('table:number-columns-repeated') // 1;  #/
        }

#-----------------------------------------------------------------------------

sub     get_content
        {
        my $self        = shift;
        return $self->get_children_elements;
        }

sub     set_content
        {
        my $self        = shift;
        $self->clear;
        foreach my $elt (@_)
                {
                if (ref $elt && $elt->isa(odf_element))
                        {
                        $self->append_element($elt);
                        }
                }
        }

#=============================================================================
1;