# Author:  Chao-Kuei Hung
# For more info, including license, please see doc/index.html

package RBTree;
# Red-Black Tree

use strict;
use Carp;
use vars qw(@ISA);
@ISA = qw(BST);

use BST;

# sub new { my ($self) = shift; $self->SUPER::new(@_); }

sub insert {
    my ($self, $sk_cont, %opts) = @_;
    # $sk_cont is search key, should have the same structure as -content=>...
    my ($nn, $r, $focus, $grand, $parent, $uncle);
    $nn = $self->SUPER::insert($sk_cont, %opts);
    $nn->configure(-status=>"discard");
    $self->cget(-canvas)->set_mark(0);
    $focus = $nn;
    while (1) {
	$parent = $focus->parent();
	last if $parent->cget(-status) ne "discard";
	die if $parent->level() <= 0; # impossibe, because root is always black
	$grand = $parent->parent();
	$uncle = $grand->child(1 - $parent->rank());
	if (ref $uncle and $uncle->cget(-status) eq "discard") {
	    # then parent is not the root
	    $parent->configure(-status=>"done");
	    $uncle->configure(-status=>"done");
	    $grand->configure(-status=>"discard");
	    $self->cget(-canvas)->set_mark(0);
	    $focus = $grand;
	} else {
	    if ($focus->rank() != $parent->rank()) {
		if ($parent->rank() == 0) {
		    $parent->rotate_ccw();
		} else {
		    $parent->rotate_cw();
		}
		$self->cget(-canvas)->set_mark(0);
		($focus, $parent) = ($parent, $focus);
	    }
	    if ($parent->rank() == 0) {
		$grand->rotate_cw();
	    } else {
		$grand->rotate_ccw();
	    }
	    $parent->configure(-status=>"done");
	    $grand->configure(-status=>"discard");
	    $focus->configure(-status=>"discard");
	    $self->cget(-canvas)->set_mark(0);
	    last;
	}
    }
    # make sure root is always black
    $self->root()->configure(-status=>"done")
	unless $self->root()->cget(-status) eq "done";
    return $nn;
}

sub remove {
    my ($self, $node) = @_;
    print STDERR "remove() not implemented yet, ignored\n";
    return undef;
}

$::Config->{RBTree} = {
    -appearance => {
	%{ ::deep_copy(Configurable::cget("BST", -appearance)) },
	done    => { -outline=>"Black",-fill=>"Gray",
		     -thick=>3, -stipple=>"" },
	discard => { -outline=>"DarkRed", -fill=>"LightCoral",
		     -thick=>3, -stipple=>"gray25" },
	focus   => { -outline=>"DarkBlue", -fill=>"LightBlue",
		     -thick=>3, -stipple=>"gray25" },
    },
};

if ($0 =~ /RBTree.pm$/) {
# being tested as a stand-alone program, so run test code.

require "utilalgo";
my ($mw, $ctrl, $can);
$mw = MainWindow->new(-title=>"main_test");
$can->{main} = gen_can($mw, undef, -elevation=>1, -maxlevel=>2);
$ctrl = gen_ctrl($mw, $can);
my ($tr) = RBTree->new(-canvas=>$can->{main}, %{ do "data/countries.gr" });

# $can->{main}->set_mark(1);
$ctrl->configure(-recorder=>0);
# If the canvas refuses to show any change, remember to verify that:
# - set_mark() was called at least once
# - -recorder is set to zero before entering MainLoop
# Failing to do either of the above will result in a mysterious bug
# that takes days to figure out !@#$%
Tk::MainLoop();

}

1;

