#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------
package RPS::Statement::FastRender;

# This will serve as a generic base class for 'paginated' statements.
# These are statements that have been 'rendered' into a row/cell based 
# representation, and are therefore easy to paginate.
#
# These will NOT provide the same interface as other Statement classes. These
# are indented strictly for quick rendering.  Indeed, we'll use the 'rich' Statement
# classes to generate these.
#
# This base class will provide common mechanisms for pagination, and declare abstract
# methods for fetching and rendering the row data.
#
# !!! So, the XML representation we're looking for is:
# <Statement>
#   <Table>
#     <Class>TableClass</Class>
#     <Row>
#       <Class>RowClass</Class>
#       <Column>
#         <Class>ColumnClass</Class>
#         <DisplayValue>Blah</DisplayValue>
#         <LinkValue>rps/someDisplayArea?c=some_command&id=123</LinkValue>
#       </Column>
#       <Column>...</Column>
#     </Row>
#     <Row>...</Row>
#   </Table>
#   <Table>...</Table>
# <Statement>
#

use Data::Dumper;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';
use Common::Assert;
use Common::Log;
use Common::Util;
use Common::FormObject::List::NumericPaged;
use Common::WriteXML;


sub _defaultPageSize() { return 50; }

use base 'Common::XMLObject';


sub _init
{
    my ($self, %args) = @_;

    # We handle the pagination in this class.
    #
    $self->{_page} = Common::Util::returnFirstDefined($args{page}, 1);
    $self->{_pageSize} = Common::Util::returnFirstDefined($args{pageSize}, $self->_defaultPageSize());

    # If we are passed a row, then we will determine which page to show based on the row and page size.
    #
    if ($args{row})
    {    
        $self->{_page} = int($args{row} / $self->{_pageSize}) + 1;
    }
    
    # We're going to use a 'lazy' model here.  Rather than build up a data structure
    # in memory, we're going to just sanity check and store our parameters when we
    # construct this object.    The actual data and formatting will happen at the last moment,
    # when we're outputting XML.

    $self->SUPER::_init(%args);

    return $self;
}

 # So, if I did want to create a 'row' iterator here, what would I need to do?
 #
 sub getNextRow
 {
    my ($self) = @_;

    # If we haven't even _started_ yet, get the collection iterator and build the first row.
    # Then, we build the next row.  You see, we don't know whether we're done building the
    # row until we fetch a row with a different row index.
    if (! $self->{_rowIterator})
    {
        # starting
        #
        $self->{_rowIterator} = $self->getRowCollection();
        $self->{_currentRowItem} = $self->{_rowIterator}->next();
    }

    if ($self->{_currentRowItem})
    {
        my $rowIndex = $self->{_currentRowItem}->row_index();

        my %row;
        while ($self->{_currentRowItem} && $self->{_currentRowItem}->row_index == $rowIndex)
        {
            $row{TableClass} = $self->{_currentRowItem}->table_class();
            $row{RowClass} = $self->{_currentRowItem}->row_class();

            push @{$row{Cells}}, {
                CellClass => $self->{_currentRowItem}->cell_class(),
                DisplayValue => $self->{_currentRowItem}->display_value(),
                LinkValue => $self->{_currentRowItem}->link_value(),
                TitleValue => $self->{_currentRowItem}->title_value(),
            };

            $self->{_currentRowItem} = $self->{_rowIterator}->next();
        }

        return \%row;
    }
    else
    {
        # we're done.
        #
        return undef;
    }
 }



sub writeXML
{
	my ($self, $xw, $tag, %extraParams) = @_;

    # This outputs the XML reprenting our current pagination state.
    #
    my %paginationHash;
#    $self->_initPagination($self->{Pagination}, \%paginationHash);

    Common::FormObject::List::NumericPaged::CreateNumericPagination(\%paginationHash,
     currentPage => $self->{_page},
     numPages => $self->_numPages(),
    );

#    $outputHash->{Pagination}{PageSize} = $self->{_pageSize};

    $xw->starttag('Statement');

    Common::WriteXML::writeXML($xw, \%paginationHash, 'Pagination');

    # So, what we're going to try to do here is output XML as a 'stream'.
    # Rather than construct a bunch of nested objects, we're going to
    # basically just spew out XML as the data comes out of the DB::ItemCollection.
    #
    # This should keep the memory overhead to a minimum. It also constrains us somewhat, since
    # there won't be a lot of abstraction (everything we need to know about the XML document lives
    # right here in this method).
    # 

    my $collection = $self->getRowCollection();
    my $currentTableClass;
    my $currentRowIndex;
    
    # First, let's check for a repeating row if we're not on the first page.
    #
    if ($self->{_page} != 1)
    {
        my $repeatingRowCollection = $self->getFullRepeatingRow();
        while (my $repeatingRow = $repeatingRowCollection->next)
        {
            ($xw, $currentTableClass, $currentRowIndex) = $self->addItemToXML($xw, $repeatingRow, $currentTableClass, $currentRowIndex);
        }
    }

    while (my $item = $collection->next)
    {
        ($xw, $currentTableClass, $currentRowIndex) = $self->addItemToXML($xw, $item, $currentTableClass, $currentRowIndex);
    }

    # Close off our dangling tags.
    #
    $xw->endtag('Row');
    $xw->endtag('Table');

    $xw->endtag('Statement');

    $self->SUPER::writeXML($xw, $tag, %extraParams);
}


sub addItemToXML
{
    my ($self, $xw, $item, $currentTableClass, $currentRowIndex) = @_;
    #Common::Log::Print("ITEM: " . Dumper($item));
    # Starting a new table?
    #
    if (! defined $currentTableClass || $currentTableClass ne $item->table_class)
    {
        if (defined $currentTableClass)
        {
            $xw->endtag('Row');
            $xw->endtag('Table');
        }

        $xw->starttag('Table');
        $currentTableClass = $item->table_class;
        $xw->element('Class', $currentTableClass);
        $currentRowIndex = undef;
    }

    # Starting a new row?
    #
    if (! defined $currentRowIndex || $currentRowIndex != $item->row_index)
    {
        if (defined $currentRowIndex)
        {
            $xw->endtag('Row');
        }

        $xw->starttag('Row');
        $xw->element('Class', $item->row_class);
        $currentRowIndex = $item->row_index;
    }

    # Output the cell data.
    #
    $xw->starttag('Cell');
        $xw->element('Class', $item->cell_class);
        $xw->element('DisplayValue', $item->display_value);
        $xw->element('LinkValue', $item->link_value);
        $xw->element('TitleValue', $item->title_value);
    $xw->endtag('Cell');
    
    return ($xw, $currentTableClass, $currentRowIndex) ;
}

# NOTE!!!
# The built-in pagination in DB::ItemCollection is not utilized here.
# In otherwords, we don't call 'setPagination' on the collection.
# The accessor method is presumed to do whatever limiting is necessary.
# This is because these are complex joined queries - We return a 'row'
# (in the DBI sense) for each _cell_. 
#

# Subclasses probably won't need to touch this, but you never know...
#
sub _initPagination
{
    my ($self, $collection, $outputHash) = @_;

    return if $self->{_page} eq 'ALL';

    Common::FormObject::List::NumericPaged::CreateNumericPagination($outputHash,
     currentPage => $self->{_page},
     numPages => $self->_numPages(),
    );

    $outputHash->{Pagination}{PageSize} = $self->{_pageSize};
}

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

    assert(0, 'override');
}

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

    assert(0, 'override to return DB::ItemCollection');
}

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

    assert(0, 'override');
}



###
1;#
###
