#!/usr/bin/perl -w

use strict;

use Test::More;
plan tests => 63;

use Perl6::Container::Array; 
use Perl6::Value;
use Perl6::Value::List;

# use constant Inf => Perl6::Value::Num::Inf;

{
  # str()
  my $span = Array->new( );
  is( $span->str->unboxed, '()', '... str()' );
  $span->push( 1 );
  is( $span->str->unboxed, '(1)', '...' );
  $span->push( 2 );
  is( $span->str->unboxed, '(1, 2)', '...' );
  $span->push( 3 );
  is( $span->str->unboxed, '(1, 2, 3)', '...' );
  $span->push( 4 );
  is( $span->str->unboxed, '(1, 2, 3, 4)', '...' );
  $span->push( List->new( '$.unboxed' => Perl6::Value::List->from_num_range( start => 0, end => Inf, step => 1  ) ) );
  is( $span->str->unboxed, '(1, 2, 3 ... Inf)', '...' );
  is( $span->fetch(6)->fetch, 2, '...' );
}

{
  # splice: offset > 0, length > 0

  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  isa_ok( $span, 'Array', 'created an Array' );
  is( $span->perl->unboxed, '(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)', '... perl()' );

  my $spliced = $span->splice( 2, 3, 23..25 );
  isa_ok( $spliced, 'Array', '... splice' );

  is( $span->perl->unboxed, '(1, 2, 23, 24, 25, 6, 7, 8, 9, 10)', '... splice: offset > 0, length > 0' );
  is( $spliced->perl->unboxed, '(3, 4, 5)', '... splice' );
}

{
  # splice: offset > 0, length = 0
  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  my $spliced = $span->splice( 2, 0, 99 );
  is( $span->perl->unboxed, '(1, 2, 99, 3, 4, 5, 6, 7, 8, 9, 10)', '... splice: offset > 0, length = 0' );
  is( $spliced->perl->unboxed, '()', '... splice' );
}

{
  # splice: offset > 0, length < 0
  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  my $spliced = $span->splice( 2, -1, 99 );
  is( $span->perl->unboxed, '(1, 2, 99, 10)', '... splice: offset > 0, length < 0' );
  is( $spliced->perl->unboxed, '(3, 4, 5, 6, 7, 8, 9)', '... splice' );
}

{
  # splice: offset < 0, length > 0
  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  my $spliced = $span->splice( -2, 1, 99 );
  is( $span->perl->unboxed, '(1, 2, 3, 4, 5, 6, 7, 8, 99, 10)', '... splice: offset < 0, length > 0' );
  is( $spliced->perl->unboxed, '(9)', '... splice' );
}

{
  # splice: offset < 0, length = 0
  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  my $spliced = $span->splice( -2, 0, 99 );
  is( $span->perl->unboxed, '(1, 2, 3, 4, 5, 6, 7, 8, 99, 9, 10)', '... splice: offset < 0, length = 0' );
  is( $spliced->perl->unboxed, '()', '... splice' );
}

{
  # splice: offset < 0, length < 0
  my $span = Array->new();
  $span->push( Perl6::Value::List->from_single( 1 .. 10 ) );
  my $spliced = $span->splice( -3, -1, 99 );
  is( $span->perl->unboxed, '(1, 2, 3, 4, 5, 6, 7, 99, 10)', '... splice: offset < 0, length < 0' );
  is( $spliced->perl->unboxed, '(8, 9)', '... splice' );
}

{
  # Scalar with an Array inside

    my $scalar = Scalar->new();
    my $array = Array->new();
    $array->push( Perl6::Value::List->from_single( 1 .. 2 ) );
    $scalar->store( $array );

    # use Data::Dumper;
    # $Data::Dumper::Indent = 1;
    # print Dumper( $scalar );
    my $x = $scalar->fetch( 1 );
    #print Dumper( \$x );
    is( $x->fetch, 2, 'fetch from auto-dereferenced scalar works' );

    # store
    $scalar->store( 1, 3 );
    $x = $scalar->fetch( 1 );
    #print Dumper( \$x );
    is( $x->fetch, 3, 'store to auto-dereferenced scalar works' );
}

{
  # fetch

  my $list = Perl6::Value::List->from_num_range( start => 0, end => 1000000 );
  my $array1 = Array->new();
  $array1->push( $list );

  $array1->fetch( 100 );
  is( $array1->fetch( 100 )->fetch, 100, 'fetch' );
  is( $array1->fetch(  99 )->fetch,  99, '... fetch' );
  is( $array1->fetch(  98 )->fetch,  98, '... fetch' );
  is( $array1->fetch(   0 )->fetch,   0, '... fetch' );
  is( $array1->fetch( 1000000 )->fetch, 1000000, '... fetch' );
  is( $array1->store(  50, 99 ), $array1, 'store' );
  is( $array1->fetch(  49 )->fetch,  49, '... fetch' );
  is( $array1->fetch(  50 )->fetch,  99, '... fetch' );
  is( $array1->fetch(  51 )->fetch,  51, '... fetch' );
  is( $array1->fetch(   0 )->fetch,   0, '... fetch' );
  is( $array1->fetch( 1000000 )->fetch, 1000000, '... fetch' );
}

{
  # slicing

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_single( 1 .. 10 ) );

  my $slice = Array->new();
  $slice->push( Perl6::Value::List->from_single( 4, 5, 6 ) );

  my $sliced = $array->slice( $slice );

  isa_ok( $sliced, 'Array', 'slice is an Array' );
  is( $sliced->perl->unboxed, '(5, 6, 7)', '... slice perl()' );

  $sliced->store( 1, 99 );

  is( $array->perl->unboxed, '(1, 2, 3, 4, 5, 99, 7, 8, 9, 10)', '... original array' );

  # fetch returns a lvalue
  my $a = $array->fetch( 2 );
  $a->store( 55 );
  is( $array->perl->unboxed, '(1, 2, 55, 4, 5, 99, 7, 8, 9, 10)', 'fetch returns lvalue' );
  
  # fetch lvalue is a normal scalar - if it is bound to another value,
  # the binding to the array must be lost
  my $b = Scalar->new();
  $a->bind( $b );
  $a->store( 44 );
  is( $b->fetch, 44 );
  is( $array->perl->unboxed, '(1, 2, 55, 4, 5, 99, 7, 8, 9, 10)', 
      'fetch returns a normal scalar' );
}

{
  # lazy slicing - fetch/store elements

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_num_range( start => 1000, end => 100_000 ) );

  my $slice = Array->new();
  # TODO - lazy slice index with step!=1 should die
  $slice->push( Perl6::Value::List->from_num_range( start => 4, end => 1_000_000 ) );

  my $sliced = $array->slice( $slice );

  isa_ok( $sliced, 'Array', 'lazy slice is an Array' );
  is( $sliced->perl(max=>3)->unboxed, 
      '(1004, 1005, 1006 ... undef, undef, undef)',
      '... slice perl()' );

  $sliced->store( 1, 99 );

  is( $array->perl(max=>7)->unboxed, 
      '(1000, 1001, 1002, 1003, 1004, 99, 1006 ... '.
      '99994, 99995, 99996, 99997, 99998, 99999, 100000)', 
      '... original array' );
}

{
  # lazy slicing - fetch whole slices

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_num_range( start => 1000, end => 100_000 ) );

  my $idx = Array->new();
  $idx->push( 2 );
  $idx->push( Perl6::Value::List->from_num_range( start => 4, end => 1_000_000 ) );
  # TODO TEST - should die if a lazy slice index has step!=1 
  my $sliced = $array->slice( $idx );

  # fetch a lazy slice
  my $array2 = Array->new();
  $array2->store( $sliced );

  is( $array2->perl(max=>3)->unboxed, 
      '(1002, 1004, 1005 ... undef, undef, undef)',
      'lazy slice was stored into a new Array' );
      
  is( $array->perl(max=>7)->unboxed, 
      '(1000, 1001, 1002, 1003, 1004, 1005, 1006 ... '.
      '99994, 99995, 99996, 99997, 99998, 99999, 100000)', 
      '... original array is unmodified' );
  
}

{
  # lazy slicing - store to whole slices

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_num_range( start => 1000, end => 100_000 ) );

  my $idx = Array->new();
  $idx->push( 2 );
  $idx->push( Perl6::Value::List->from_num_range( start => 4, end => 100 ) ); 
  my $sliced = $array->slice( $idx );

  # store to a lazy slice
  my $array2 = Array->new();
  $array2->push( 99, Perl6::Value::List->from_num_range( start => 100, end => 200_000_000 ) );
  $sliced->store( $array2 );

  is( $sliced->perl(max=>3)->unboxed, 
      '(99, 100, 101 ... 194, 195, 196)',
      'Array slice was modified' );
      
  is( $idx->perl(max=>3)->unboxed, 
      '(2, 4, 5 ... 98, 99, 100)',
      '... index Array was not modified' );
      
  is( $array->perl(max=>7)->unboxed, 
      '(1000, 1001, 99, 1003, 100, 101, 102 ... '.
      '99994, 99995, 99996, 99997, 99998, 99999, 100000)', 
      '... original array' );
  
}

{
  # lazy slicing - store to whole slices - special case, tests a new feature in splice()

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_num_range( start => 1000, end => 100_000 ) );
  is( $array->perl(max=>3)->unboxed, 
      '(1000, 1001, 1002 ... 99998, 99999, 100000)', 'Array to be sliced' );
  my $array_elems = $array->elems->unboxed;
  is( $array_elems, 99001, '... Array has xxx elements' );

  my $idx = Array->new();
  $idx->push( 2 );
  $idx->push( Perl6::Value::List->from_num_range( start => 4, end => 1_000_000 ) ); 
    # end => 1_000_000 - was not supported before
  is( $idx->perl(max=>3)->unboxed, 
      '(2, 4, 5 ... 999998, 999999, 1000000)', '... Array with indexes' );
  my $elems = $idx->elems->unboxed;
  is( $elems, 999998, '... Array with indexes has xxx elements' );
      
  my $sliced = $array->slice( $idx );
  is( $sliced->perl(max=>3)->unboxed, 
      '(1002, 1004, 1005 ... undef, undef, undef)', '... Resulting Array slice' );
      
  # array that will be stored into lazy slice
  my $array2 = Array->new();
  $array2->push( 99, Perl6::Value::List->from_num_range( start => 100, end => 200_000_000 ) );
  is( $array2->perl(max=>3)->unboxed, 
      '(99, 100, 101 ... 199999998, 199999999, 200000000)', '... Array to be stored into the slice' );
      
  # store to a lazy slice
  $sliced->store( $array2 );

  is( $elems, $idx->elems->unboxed, '... Array with indexes still has xxx elements' );
  is( $elems, $sliced->elems->unboxed, '... Array slice also has xxx elements' );
  is( $sliced->perl(max=>3)->unboxed, 
      '(99, 100, 101 ... undef, undef, undef)',
      '... Array slice was modified; elements without source generate undefs' );
      
  is( $idx->perl(max=>3)->unboxed, 
      '(2, 4, 5 ... 999998, 999999, 1000000)',
      '... index Array was not modified' );
      
  is( $array->perl(max=>7)->unboxed, 
      '(1000, 1001, 99, 1003, 100, 101, 102 ... 99090, 99091, 99092, 99093, 99094, 99095, 99096)', 
      '... original array' );
  is( $array_elems, $array->elems->unboxed, '... Original Array still has xxx elements' );
  
}

{
  # (a,b) = (b,a)
  # lazy slicing - fetch/store to whole slices

  my $array = Array->new();
  $array->push( Perl6::Value::List->from_num_range( start => 1000, end => 1010 ) );

  my $idx1 = Array->new();
  $idx1->push( Perl6::Value::List->from_num_range( start => 1, end => 5 ) ); 
  my $slice1 = $array->slice( $idx1 );

  my $idx2 = Array->new();
  $idx2->push( Perl6::Value::List->from_num_range( start => 3, end => 6 ) ); 
  my $slice2 = $array->slice( $idx2 );

  $slice1->store( $slice2 );

  is( $slice1->perl()->unboxed, 
      '(1003, 1004, 1005, 1006, undef)',
      'Array slice was modified' );
            
  is( $array->perl()->unboxed, 
      '(1000, 1003, 1004, 1005, 1006, undef, 1006, 1007, 1008, 1009, 1010)', 
      '... original array' );
  
}
