#------------------------------------------------------------
# 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;    #
###
