#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com                     info@plainblack.com
#-------------------------------------------------------------------

use FindBin;
use strict;
use lib "$FindBin::Bin/lib";

use WebGUI::Test;
use WebGUI::Session;
use WebGUI::Utility;

use WebGUI::Paginator;
use Test::More; # increment this value for each test you create
use Test::Deep;
use POSIX qw(ceil);
use Storable qw/dclone/;
use Data::Dumper;

my $session = WebGUI::Test->session;

plan tests => 32; # increment this value for each test you create

my $startingRowNum =   0;
my $endingRowNum   =  99;
my @paginatingData = ($startingRowNum..$endingRowNum);

my $rowCount        = $endingRowNum - $startingRowNum + 1;
my $NumberOfPages   = ceil($rowCount/25); ##Default page size=25
my $url             = "/home";

my $p = WebGUI::Paginator->new($session, $url);

isa_ok($p, 'WebGUI::Paginator', 'paginator object returned');

$p->setDataByArrayRef(\@paginatingData);

is($p->getRowCount,      $rowCount,      'all data returned by paginator');
is($p->getNumberOfPages, $NumberOfPages, 'paginator returns right number of pages (default setting)');

my $page1Data = $p->getPageData(1);
cmp_bag([0..24],  $p->getPageData(1), 'page 1 data correct');
cmp_bag([25..49], $p->getPageData(2), 'page 2 data correct');
cmp_bag([      ], $p->getPageData(5), 'page 5 data correct');

########################################################################
#
# getPageNumber, setPageNumber
#
########################################################################

my $p2 = WebGUI::Paginator->new($session, '/work');

is($p2->getPageNumber, 1, 'pageNumber set to 1 at object creation by default');
is($p2->setPageNumber(0), 0, 'setPageNumber returns the page number set');
is($p2->getPageNumber, 0, 'pageNumber set by setPageNumber');
is($p2->setPageNumber(3), 3, 'setPageNumber returns the page number set');
is($p2->getPageNumber, 3, 'pageNumber set by setPageNumber');

########################################################################
#
# getPageLinks
#
########################################################################

my $expectedPages;
$expectedPages = [  map { +{ 
            'pagination.text'   => ( $_ + 1 ), 
            'pagination.range'  => ( 25 * $_ + 1 ) . "-" . ( $_ * 25 + 25 <= $endingRowNum + 1 ? $_ * 25 + 25 : $endingRowNum + 1 ), # First row number - Last row number
            'pagination.url'    => ( $_ != 0 ? $url . '?pn=' . ( $_ + 1 ) : '' ), # Current page has no URL
        } } (0..$NumberOfPages-1) ];

$expectedPages->[0]->{'pagination.activePage'} = 'true';

cmp_deeply(
    ($p->getPageLinks)[0], 
    $expectedPages, 
    'page links correct',
);

$startingRowNum =   0;
$endingRowNum   = 100;
@paginatingData = ($startingRowNum..$endingRowNum);

$rowCount       = $endingRowNum - $startingRowNum + 1;
$NumberOfPages  = ceil($rowCount/25); ##Default page size=25

$p = WebGUI::Paginator->new($session, '/home');

$p->setDataByArrayRef(\@paginatingData);

is($p->getRowCount,      $rowCount,      '(101) paginator returns correct number of rows');
is($p->getNumberOfPages, $NumberOfPages, '(101) paginator returns right number of pages (default setting)');

$page1Data = $p->getPageData(1);
cmp_bag([0..24],  $p->getPageData(1), '(101) page 1 data correct');
cmp_bag([25..49], $p->getPageData(2), '(101) page 2 data correct');
cmp_bag([100   ], $p->getPageData(5), '(101) page 5 data correct');

is('100', $p->getPage(5), '(101) page 5 stringification okay');

is($p->getPageNumber, 1, 'Default page number is 1'); ##Additional page numbers are specified at instantiation

########################################################################
#
# getPageLinks with limits
#
########################################################################

$expectedPages = [  map { +{ 
            'pagination.text'   => ( $_ + 1 ), 
            'pagination.range'  => ( 25 * $_ + 1 ) . "-" . ( $_ * 25 + 25 <= $endingRowNum + 1 ? $_ * 25 + 25 : $endingRowNum + 1 ), # First row number - Last row number
            'pagination.url'    => ( $_ != 0 ? $url . '?pn=' . ( $_ + 1 ) : '' ), # Current page has no URL
        } } (0..$NumberOfPages-1) ];

$expectedPages->[0]->{'pagination.activePage'} = 'true';

cmp_deeply(
    ($p->getPageLinks)[0], 
    $expectedPages,
    'set of 5 pages looks right',
);

$startingRowNum =   0;
$endingRowNum   = 199;
@paginatingData = ($startingRowNum..$endingRowNum);


$p = WebGUI::Paginator->new($session, '/home', 10);

$rowCount       = $endingRowNum - $startingRowNum + 1;
$NumberOfPages  = ceil($rowCount/10); ##Default page size=25

$p->setDataByArrayRef(\@paginatingData);

$expectedPages = [  map { +{ 
            'pagination.text'   => ( $_ + 1 ), 
            'pagination.range'  => ( 10 * $_ + 1 ) . "-" . ( $_ * 10 + 10 <= $endingRowNum + 1 ? $_ * 10 + 10 : $endingRowNum + 1 ), # First row number - Last row number
            'pagination.url'    => ( '/home?pn=' . ( $_ + 1 ) ), # Current page has no URL
        } } (0..$NumberOfPages-1) ];


my $copy;
$copy = dclone($expectedPages);
$copy->[0]->{'pagination.activePage'} = 'true';
$copy->[0]->{'pagination.url'}        = '';

cmp_deeply(
    ($p->getPageLinks)[0], 
    $copy,
    'set of 20 pages looks right',
);

my @pageWindow;
$copy = dclone($expectedPages);
$copy->[0]->{'pagination.activePage'} = 'true';
$copy->[0]->{'pagination.url'}        = '';
@pageWindow = @{ $copy }[0..9];

cmp_deeply(
    ($p->getPageLinks(10))[0], 
    \@pageWindow,
    'set of first 10 pages selected correctly',
);

$p->setPageNumber(10);
my $copy = dclone($expectedPages);
$copy->[9]->{'pagination.activePage'} = 'true';
$copy->[9]->{'pagination.url'} = '';
@pageWindow = @{ $copy }[4..13];

cmp_deeply(
    ($p->getPageLinks(10))[0], 
    \@pageWindow,
    'set of middle 10 pages @10 selected correctly',
);

$p->setPageNumber(3);
my $copy = dclone($expectedPages);
$copy->[2]->{'pagination.activePage'} = 'true';
$copy->[2]->{'pagination.url'} = '';
delete $copy->[0]->{'pagination.activePage'};
@pageWindow = @{ $copy }[0..9];

cmp_deeply(
    ($p->getPageLinks(10))[0], 
    \@pageWindow,
    'set of 10 pages selected correctly, with off edge page number (3/20)',
);

$p->setPageNumber(17);
my $copy = dclone($expectedPages);
$copy->[16]->{'pagination.activePage'} = 'true';
$copy->[16]->{'pagination.url'} = '';
@pageWindow = @{ $copy }[10..19];

cmp_deeply(
    ($p->getPageLinks(10))[0], 
    \@pageWindow,
    'set of last 10 pages selected correctly, (17/20)',
);

$p->setPageNumber(20);
my $copy = dclone($expectedPages);
$copy->[19]->{'pagination.activePage'} = 'true';
$copy->[19]->{'pagination.url'} = '';
@pageWindow = @{ $copy }[10..19];

cmp_deeply(
    ($p->getPageLinks(10))[0], 
    \@pageWindow,
    'set of last 10 pages selected correctly, (20/20)',
);

########################################################################
#
# iterator based paginator
#
########################################################################

my $callback = sub {
    my ($start, $rowsPerPage) = @_;
    my $state   = $start * 2;
    my $counter = 0;
    my $iterator = sub {
        return 50 if $_[0] eq 'rowCount';
        return if ($counter >= $rowsPerPage);
        $state += 2;
        ++$counter;
        return if $state > 50;
        return $state;
    };
    return $iterator;
};

my $p1 = WebGUI::Paginator->new($session, '/neighborhood', 5);
$p1->setDataByCallback($callback);
my $pIterator = $p1->getPageIterator;
isa_ok($pIterator, 'CODE', 'getPageIterator');
is($pIterator->('rowCount'), 50, 'generated iterator returns the correct maximum number of rows');
is($p1->getNumberOfPages, 10, 'getNumberOfPages works with an iterator');

cmp_deeply(drainIterator($pIterator, 10), [2, 4, 6, 8, 10], 'setDataByCallback: iterator returned page 1 data');

$p1->setPageNumber(2);
$p1->setDataByCallback($callback);
$pIterator = $p1->getPageIterator;

cmp_deeply(drainIterator($pIterator, 10), [12, 14, 16, 18, 20], '... iterator returned page 2 data');

$expectedPages = [  map { +{ 
            'pagination.text'   => ( $_ + 1 ), 
            'pagination.range'  => ( 5 * $_ + 1 ) . "-" . ( ($_+1) * 5 ), # First row number - Last row number
            'pagination.url'    => ( $_ != 1 ? '/neighborhood' . '?pn=' . ( $_ + 1 ) : '' ), # Current page has no URL
        } } (0..9) ];

$expectedPages->[1]->{'pagination.activePage'} = 'true';

cmp_deeply(
    ($p1->getPageLinks())[0],
    $expectedPages,
    'getPageLinks works with a paginator'
);

sub drainIterator {
    my $iterator      = shift;
    my $terminalCount = shift; 
    my $pageData = [];
    my $infiniteLoopCount = 0;
    while (defined(my $item = $pIterator->())) {
        push @{ $pageData }, $item;
        last if ++$infiniteLoopCount >= $terminalCount;
    }
    return $pageData;
}
