The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Data::Range::Compare::Stream::Iterator::Consolidate::OverlapAsColumn;

use strict;
use warnings;
use Carp qw(croak);

use base qw(Data::Range::Compare::Stream::Iterator::Consolidate);
use Data::Range::Compare::Stream::Iterator::Array;

use constant NEW_ARRAY_ITERATOR_FROM=>'Data::Range::Compare::Stream::Iterator::Array';
use constant NEW_CHILD_CONSOLIDATOR_FROM=>'Data::Range::Compare::Stream::Iterator::Consolidate::OverlapAsColumn';

sub new {
  my ($class,$it,$cmp,%args)=@_;
  croak('Required Arguments are: $iterator,$compare') unless defined($cmp);
  my $self=$class->SUPER::new($it,compare=>$cmp,buffer=>[],%args);
  return $self;
}

sub get_child { $_[0]->{consolidator} }

sub delete_from_root {
  my ($self)=@_;
  if($self->has_child) {
    my $child=$self->get_child;
    delete $child->{root_iterator};
  }
  if($self->is_child) {
    my $root=$self->get_root;
    delete @{$root}{qw(iterator_array consolidator)};
  }
}

sub get_child_column_id {
  my ($self)=@_;
  return undef unless $self->has_child;
  $self->get_child->get_column_id;
}

sub on_consolidate {
  my ($self,$new_range,$last_range,$next_range)=@_;

  my $cmp=$self->{compare};
  my $iterator;

  if(defined($self->{iterator_array})) { 
     $iterator=$self->{iterator_array};
     $iterator->insert_range($next_range);
  } else {
     $iterator=$self->{iterator_array}=$self->NEW_ARRAY_ITERATOR_FROM->new(sorted=>1);
     $iterator->insert_range($next_range);
     my $consolidator=$self->{consolidator}=$self->NEW_CHILD_CONSOLIDATOR_FROM->new($iterator,$cmp);
     $consolidator->{root_iterator}=$self;
  }

}

sub get_root_column_id {
  my ($self)=@_;
  return $self->get_root->get_column_id if $self->is_child;
  $self->get_column_id
}

sub is_child { defined($_[0]->{root_iterator}) }

sub is_root { !$_[0]->is_child }
sub has_child { defined($_[0]->{iterator_array}) }
sub has_root { defined($_[0]->get_root)}

sub get_compare { $_[0]->{compare} }

sub get_root { 
  my ($self)=@_;
  $self->{root_iterator};
}

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

  return 1 if $#{$self->{buffer}}!=-1;
  return 1 if $self->SUPER::has_next;

  if($self->is_child) {
    my $cmp=$self->get_compare;
    my $root=$self->get_root;
    if($root->SUPER::has_next) {
      $root->push_to_buffer;
      return 1 if $#{$self->{buffer}}!=-1;
    }
  }
  return 0;
}

sub get_current_result { $_[0]->{current_result} }

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

  if($#{$self->{buffer}}==-1 and $self->SUPER::has_next) {
    $self->push_to_buffer;
  }
  my $result=shift @{$self->{buffer}};
  $self->{current_result}=$result;
  return $result;

}

sub buffer_count { 1 + $#{$_[0]->{buffer}} } 

sub get_buffer { $_[0]->{buffer} }

sub iterator_has_next { $_[0]->{iterator}->has_next }

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

  my $overlapping_range;
  if(defined($self->{last_range})) {
    $overlapping_range=$self->{last_range};
    $self->{last_range}=undef;
  } else {
    $overlapping_range=$self->{iterator}->get_next;
    $self->{last_range}=$overlapping_range;
  }
  return 0 unless defined($overlapping_range);

  my $result=$overlapping_range;
  my $pushed_to_child=0;
  if($self->iterator_has_next) {
    OVERLAP_CHECK: while($self->iterator_has_next) {
  
      my $next_range=$self->{iterator}->get_next;
  
      if($overlapping_range->overlap($next_range)) {
  
        $overlapping_range=$overlapping_range->get_overlapping_range([$overlapping_range,$next_range]);
        $self->on_consolidate($overlapping_range,$result,$next_range);
        $pushed_to_child++;
        $self->{last_range}=undef;
  
      } else {
  
        $self->{last_range}=$next_range;
        last OVERLAP_CHECK;
  
      }
    }
  } else {
      $self->{last_range}=undef;
  }

  if($pushed_to_child) {
    my $child=$self->get_child;
    $child->push_to_buffer;
    $self->{compare}->insert_consolidator($child) unless defined($child->get_column_id);
  }


  push @{$self->{buffer}},$self->RESULT_CLASS->new($result,$result,$result); 
  return 1;
}

1;