The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

use Tickit::RectSet;

use Tickit::Rect;

# Distinct regions
{
   my $rectset = Tickit::RectSet->new;

   ok( defined $rectset, '$rectset defined' );
   isa_ok( $rectset, "Tickit::RectSet", '$rectset isa Tickit::RectSet' );

   is( scalar $rectset->rects, 0, '$rectset initially empty' );

   $rectset->add( Tickit::Rect->new( top => 10, left => 10, lines => 5, cols => 20 ) );

   is_deeply( [ $rectset->rects ],
              [ Tickit::Rect->new( top => 10, left => 10, lines => 5, cols => 20 ) ],
              '$rectset contains 1 rect after first add' );

   $rectset->add( Tickit::Rect->new( top => 20, left => 10, lines => 2, cols => 20 ) );

   is_deeply( [ $rectset->rects ],
              [ Tickit::Rect->new( top => 10, left => 10, lines => 5, cols => 20 ),
                Tickit::Rect->new( top => 20, left => 10, lines => 2, cols => 20 ) ],
              '$rectset contains 2 rects after second add' );

   $rectset->clear;

   is( scalar $rectset->rects, 0, '$rectset empty after ->clear' );
}

# Intersect and containment tests
{
   my $rectset = Tickit::RectSet->new;
   $rectset->add( Tickit::Rect->new( top => 1, left => 1, bottom => 5, right => 20 ) );
   $rectset->add( Tickit::Rect->new( top => 5, left => 1, bottom => 10, right => 10 ) );

   ok(  $rectset->intersects( Tickit::Rect->new( top => 0, left => 0, bottom => 5, right => 5 ) ), '$rectset intersects overlap' );
   ok( !$rectset->intersects( Tickit::Rect->new( top => 6, left => 15, bottom => 9, right => 25 ) ), '$rectset no intersect' );

   ok(  $rectset->contains( Tickit::Rect->new( top => 1, left => 5, bottom => 4, right => 15 ) ), '$rectset contains simple' );
   ok(  $rectset->contains( Tickit::Rect->new( top => 2, left => 5, bottom => 9, right => 8 ) ), '$rectset contains split' );
   ok( !$rectset->contains( Tickit::Rect->new( top => 2, left => 5, bottom => 9, right => 12 ) ), '$rectset no contains split' );
   ok( !$rectset->contains( Tickit::Rect->new( top => 6, left => 15, bottom => 9, right => 25 ) ), '$rectset no contains non-intersect' );
}

sub _newrect
{
   local $_ = shift;

   m/^(\d+),(\d+)\+(\d+)x(\d+)$/ and return
      Tickit::Rect->new( left => $1, top => $2, cols => $3, lines => $4 );

   m/^(\d+),(\d+)\.\.(\d+),(\d+)$/ and return
      Tickit::Rect->new( left => $1, top => $2, right => $3, bottom => $4 );

   die "Unrecognised rectangle spec $_\n";
}

my $name;
while( <DATA> ) {
   next if m/^\s*$/;
   $name = $1, next if m/^\s*#\s*(.*)$/;

   my ( $input, $output ) = m/^(.*)\s*=>\s*(.*)$/ or die "Expected =>\n";
   my @input = split m/\s+/, $input;
   my $method = shift @input;
   my @inputrects  = map _newrect($_), @input;
   my @outputrects = map _newrect($_), split m/\s+/, $output;

   if( $method eq "add" ) {
      my $rectset = Tickit::RectSet->new;
      $rectset->add( $_ ) for @inputrects;

      is_deeply( [ $rectset->rects ], \@outputrects, "Output for $name $input" );

      $rectset = Tickit::RectSet->new;
      $rectset->add( $_ ) for reverse @inputrects;

      is_deeply( [ $rectset->rects ], \@outputrects, "Output for $name $input reversed" );
   }
   if( $method eq "subtract" ) {
      my $rectset = Tickit::RectSet->new;
      $rectset->add( shift @inputrects );
      $rectset->subtract( $_ ) for @inputrects;

      is_deeply( [ $rectset->rects ], \@outputrects, "Output for $name $input" );
   }
}

done_testing;

__DATA__

# Distinct regions
add 10,10..30,15 40,10..60,15 => 10,10..30,15 40,10..60,15
add 10,10..30,15 10,20..30,25 => 10,10..30,15 10,20..30,25

# Ignorable regions
add 10,10..30,15 10,10..30,15 => 10,10..30,15
add 10,10..30,15 10,10..20,12 => 10,10..30,15
add 10,10..30,15 20,13..30,15 => 10,10..30,15
add 10,10..30,15 15,11..25,14 => 10,10..30,15

# Overlapping extension top
add 10,10..30,15 10,8..30,12 => 10,8..30,15
add 10,10..30,15 10,8..30,10 => 10,8..30,15
add 10,10..30,12 10,15..30,17 10,12..30,15 => 10,10..30,17

# Overlapping extension bottom
add 10,10..30,15 10,12..30,17 => 10,10..30,17
add 10,10..30,15 10,15..30,17 => 10,10..30,17

# Overlapping extension left
add 10,10..30,15 5,10..25,15 => 5,10..30,15
add 10,10..30,15 5,10..10,15 => 5,10..30,15

# Overlapping extension right
add 10,10..30,15 20,10..35,15 => 10,10..35,15
add 10,10..30,15 30,10..35,15 => 10,10..35,15

# L/T shape top abutting
add 10,10..30,15 10,8..20,10 => 10,8..20,10 10,10..30,15
add 10,10..30,15 15,8..25,10 => 15,8..25,10 10,10..30,15
add 10,10..30,15 20,8..30,10 => 20,8..30,10 10,10..30,15

# L/T shape top overlapping
add 10,10..30,15 10,8..20,12 => 10,8..20,10 10,10..30,15
add 10,10..30,15 15,8..25,12 => 15,8..25,10 10,10..30,15
add 10,10..30,15 20,8..30,12 => 20,8..30,10 10,10..30,15

# L/T shape bottom abutting
add 10,10..30,15 10,15..20,17 => 10,10..30,15 10,15..20,17
add 10,10..30,15 15,15..25,17 => 10,10..30,15 15,15..25,17
add 10,10..30,15 20,15..30,17 => 10,10..30,15 20,15..30,17

# L/T shape bottom overlapping
add 10,10..30,15 10,13..20,17 => 10,10..30,15 10,15..20,17
add 10,10..30,15 15,13..25,17 => 10,10..30,15 15,15..25,17
add 10,10..30,15 20,13..30,17 => 10,10..30,15 20,15..30,17

# L/T shape left abutting
add 10,10..30,15 5,10..10,12 => 5,10..30,12 10,12..30,15
add 10,10..30,15 5,11..10,14 => 10,10..30,11 5,11..30,14 10,14..30,15
add 10,10..30,15 5,13..10,15 => 10,10..30,13 5,13..30,15

# L/T shape left overlapping
add 10,10..30,15 5,10..15,12 => 5,10..30,12 10,12..30,15
add 10,10..30,15 5,11..15,14 => 10,10..30,11 5,11..30,14 10,14..30,15
add 10,10..30,15 5,13..15,15 => 10,10..30,13 5,13..30,15

# L/T shape right abutting
add 10,10..30,15 30,10..35,12 => 10,10..35,12 10,12..30,15
add 10,10..30,15 30,11..35,14 => 10,10..30,11 10,11..35,14 10,14..30,15
add 10,10..30,15 30,13..35,15 => 10,10..30,13 10,13..35,15

# L/T shape right overlapping
add 10,10..30,15 20,10..35,12 => 10,10..35,12 10,12..30,15
add 10,10..30,15 20,11..35,14 => 10,10..30,11 10,11..35,14 10,14..30,15
add 10,10..30,15 20,13..35,15 => 10,10..30,13 10,13..35,15

# Cross shape
add 10,10..30,15 15,5..25,20 => 15,5..25,10 10,10..30,15 15,15..25,20

# Diagonal overlap
add 10,10..30,15 20,12..40,20 => 10,10..30,12 10,12..40,15 20,15..40,20
add 10,10..30,15  0,12..20,20 => 10,10..30,12  0,12..30,15  0,15..20,20


# Distinct regions
subtract 10,10..30,15 10,20..30,22 => 10,10..30,15

# Overlapping truncate left
subtract 10,10..30,15 5,10..20,15 => 20,10..30,15

# Overlapping truncate right
subtract 10,10..30,15 20,10..35,15 => 10,10..20,15

# Overlapping truncate top
subtract 10,10..30,15 10,8..30,12 => 10,12..30,15

# Overlapping truncate bottom
subtract 10,10..30,15 10,13..30,18 => 10,10..30,13

# Overlapping U left
subtract 10,10..30,15 5,11..20,14 => 10,10..30,11 20,11..30,14 10,14..30,15

# Overlapping U right
subtract 10,10..30,15 20,11..35,14 => 10,10..30,11 10,11..20,14 10,14..30,15

# Overlapping U top
subtract 10,10..30,15 15,8..25,12 => 10,10..15,12 25,10..30,12 10,12..30,15

# Overlapping U bottom
subtract 10,10..30,15 15,13..25,18 => 10,10..30,13 10,13..15,15 25,13..30,15

# Remove entirely
subtract 10,10..30,15 8,8..32,17 =>