#
#  $Id: CardPile.pm,v 4.6 1999/06/14 05:13:13 kono Exp $
#
package CardPlay::CardPile;

use strict;

use vars qw($VERSION @ISA @EXPORT @EXPORT_OK
    $CardPileTag
);

require Exporter;
require AutoLoader;

@ISA = qw(Exporter AutoLoader
    CardPlay::SimpleCardPile
);

@EXPORT = qw( );

$VERSION = "0.2";

use Tk;
use Carp;
use CardPlay::Card;
use CardPlay::SimpleCardPile;
use CardPlay::CardPileUndo;
# use CardPlay::CardBoard;

$CardPlay::CardPileTag = 'cardpile000';

sub new {
    my $type = shift;
    my %arg = @_;
    my $self;
    
    $self = CardPlay::SimpleCardPile->new(@_);
    defined($arg{'-tag'}) or $self->{'-tag'} = $CardPlay::CardPileTag++;
    defined($arg{'-deltax'}) or $self->{'-deltax'} = 0;
    defined($arg{'-deltay'}) or $self->{'-deltay'} = 
	($CardPlay::Card::size eq '-large')?30:20;

    defined($arg{'-contents'}) or $self->{'-contents'} = [];
    $self->{'-bind'} = {};
    bless $self;
}

sub bind {
    my ($self) = shift;
    my (%args) = @_;
    my ($c) = $self->{'-canvas'};
    my ($tag) = $self->{'-tag'};
    my ($bind) = $self->{'-bind'};

    if ($#_ == -1) {
# print STDERR "binding...\n";
	foreach my $key ( keys %{$bind} ) {
	    $c->bind($tag, $key => $bind->{$key});
	}
    } else {
	foreach my $key ( keys %args ) {
	    if (defined( $args{$key} )) { 
		$bind->{$key} = $args{$key}; 
	    }
	}
    }
}

sub movable {
    my ($self) = shift;
    my ($c) = $self->{'-canvas'};

    $self->bind('<1>' => sub {$self->plot_down(@_)});
#    $self->bind('<2>' => sub {$self->hide(@_)});
#    $self->bind('<3>' => sub {$self->open(@_)});
    $self->bind('<ButtonRelease-1>' => sub {shift->dtag('selected')});
    $self->bind('<B1-Motion>' => sub {$self->plot_move(@_)});
    $self->bind();
}

sub unbind {
    my ($self) = shift;
    my ($c) = $self->{'-canvas'};
    my ($tag) = $self->{'-tag'};
    foreach my $key ( keys %{$self->{'-bind'}} ) {
	$c->bind($tag, $key => "");
    }
}

sub display {
    my ($self,%args) = @_;
    $self->merge_args(%args);

    my ($tag) = $self->{'-tag'};
    my ($x) = defined($self->{'-x'})?$self->{'-x'}:0;
    my ($y) = defined($self->{'-y'})?$self->{'-y'}:0;
    my ($dx) = $self->{'-deltax'};
    my ($dy) = $self->{'-deltay'};
    my ($xx,$yy);
    my ($ctag,$basetag);
    my ($first) = 0;
    my ($c) = $self->{'-canvas'} or  carp "needs -canvas\n";

    if (defined( $self->{'-base'} )) { 
	if (defined( $self->{'-basetag'} )) {
	    ($xx,$yy) = $c->coords($self->{'-basetag'});
	    if($xx != $x || $yy != $y) {
		$c->move($self->{'-basetag'},$x-$xx,$y-$yy);
	    }
	} else {
	    $self->{'-basetag'} = $basetag = 
		$c->create('rectangle',$x,$y,$x+$CardPlay::Card::cx,$y+$CardPlay::Card::cy);
	    $c->addtag($tag, withtag => $basetag);
	}
    }
    foreach my $i ( @{$self->{-contents}} ) {
	$first = $i if (! $first);
	if (defined( $i->{'-tag'})) {
	    ($xx,$yy) = $i->coords();
	    if($xx != $x || $yy != $y) {
		$c->move($i->{'-tag'},$x-$xx,$y-$yy);
	    }
	} else {
	    $i->display('-x'=>$x,'-y'=>$y);
	    $c->addtag($tag, withtag => ($i->{'-tag'}));
	}
	$i->raise();
	$x += $dx;
	$y += $dy;
    } 
    # if (defined( $self->{'-basetag'} ) && $first) { 
    # 	$c->lower($self->{'-basetag'},$first->{'-tag'});
    # }
    if ($self->{'-movable'}) {
	$self->movable();
    } elsif ($self->{'-splitable'}) {
	$self->splitable();
    }
    if ($self->{'-mergeable'}) {
	$self->mergeable();
    }
    if ($self->{'-auto_open'}) {
	$self->open();
    }
    $self->bind();
    return $self;
}
 
sub raise {
    my ($self) = shift;
    my ($c) = $self->{'-canvas'};
    my ($tag) = $self->{'-tag'};
    $c->raise($tag);
}

sub plot_down {
    my ($self) = shift;
    my ($w) = @_;
    my ($e) = $w->XEvent;
    my ($x, $y) = ($e->x, $e->y);
    my ($tag) = $self->{'-tag'};

    $w->dtag('selected');
    $w->addtag('selected', withtag => $self->{'-tag'});
    $w->raise($tag);
    $self->{'-lastX'} = $x;
    $self->{'-lastY'} = $y;
} # end plot_down


sub plot_move {
    my ($self) = shift;
    my ($w) = @_;
    my $e = $w->XEvent;
    my ($x, $y) = ($e->x, $e->y);
    $w->move('selected',  $x-$self->{'-lastX'}, $y-$self->{'-lastY'});
    $self->{'-lastX'} = $x;
    $self->{'-lastY'} = $y;
} # end plot_move

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

    my ($card) = $self->last();
    if (defined($card)) {
	if ($card->{'-state'} eq 'open') {
	    $card->hide();
	}
    }
}

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

    my ($card) = $self->last();
    if (defined($card)) {
	if ($card->{'-state'} eq 'hide') {
	    $card->open();
	}
    }
}


sub splitable {
    my ($self) = shift;
    my ($c) = $self->{'-canvas'};
    my ($tag) = $self->{'-tag'};
    my ($i,$j);
    $j = 0;
    foreach $i ( @{$self->{-contents}} ) {
	my ($jj) = $j;
	$c->bind($i->{'-tag'}, '<1>' => sub {$self->split_event($jj,@_)});
	$j++;
    } 
#    $self->bind('<2>' => sub {$self->hide(@_)});
#    $self->bind('<3>' => sub {$self->open(@_)});
#    $self->bind('<ButtonRelease-1>' => sub {$self->merge_event(@_)});
#    $self->bind('<B1-Motion>' => sub {$self->plot_move(@_)});
    $self->bind();
}

sub unsplitable {
    my ($self) = shift;
    my ($c) = $self->{'-canvas'};
    my ($tag) = $self->{'-tag'};
    my ($i);
    foreach $i ( @{$self->{-contents}} ) {
	$c->bind($i->{'-tag'}, '<1>' => "");
    } 
    $self->unbind();
}

sub clear {
    my ($self) = @_;
    my ($card);

    foreach $card ( @{$self->{'-contents'}} ) {
	$card->clear;
    }
    $self->SUPER::clear();
    $self->unbind();
    
    # $self->{'-canvas'}->delete($self->{'-tag'});
    if (defined($self->{'-mergeable'})) {
	$self->{'-board'}->delete_drag($self);
    }
}

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

    $self->unbind();
    $self->{'-canvas'}->dtag($self->{'-tag'});
    if (defined($self->{'-mergeable'})) {
	$self->{'-board'}->delete_drag($self);
    }
    1;
}

sub mergeable {
    my ($self) = shift;
    $self->{'-mergeable'}=1;
    $self->{'-board'}->add_drag($self);
}

sub merge {
    my ($self,$cardpile) = @_;
    return if ($cardpile==0);
    my ($contents) = $cardpile->{'-contents'};
    my ($c) = $self->{'-canvas'};
    if (defined($self->{'-reverse_merge'})) {
	$cardpile->reverse();
    }
    if (! $c ) {
	push(@{$self->{-contents}},@{$cardpile->{-contents}});
	$cardpile='';
	return;
    }

    $c->addtag($self->{'-tag'}, withtag => $cardpile->{'-tag'});
    $c->dtag($cardpile->{'-tag'},$cardpile->{'-tag'});
    push(@{$self->{-contents}},@{$cardpile->{-contents}});
    $self->display();
    $cardpile = '';
    # if ($self-> {'-splitable'}) {  # this has been done by display
    # 	$self->splitable();
    # } 
}

sub merge_event {
    my ($self,$w) = @_;
    my ($e) = $w->XEvent;
    my ($x, $y) = ($e->x, $e->y);
    my ($draggedPile);

# print STDERR "merge_event ";$self->print;
    if (! $self->{'-prevSplit'}) {
#	print STDERR "reenter merge_event..\n";
	return;
    }
    if (defined($self->{'-board'})) {
	return 0 if ($self->{'-board'}->{'-auto_playing'}) ;
	$self->{'-board'}->log_commit(0);
    }
    $w->dtag('selected');
    if (defined(($draggedPile = $self->{'-board'}->check_drag($x,$y)))) {
# print STDERR "drag on ";$draggedPile->print;
	if (! $draggedPile->can('can_merge')) {
	    $draggedPile->do_merge($self);
	    $draggedPile->auto_play();
	    return;
	} 
	# before checking prevSplit==$self, try intelligent move
        my $ret = $draggedPile->can_merge($self);
	if ($ret == 1) {
	    # yes we can merge
	    if ($draggedPile eq $self->{'-prevSplit'}) {
		# but this is myself
		$self->unsplit();
		return;
	    }
	    $draggedPile->do_merge($self);
	    $draggedPile->auto_play();
	    return;
	} elsif ($ret == 2) {
	    # can_merge done everything forget unsplit
	    $self->{'-prevSplit'}=0;
	    $draggedPile->auto_play();
	    return;
	}
	# go into unsplit
    } 
    $self->unsplit();
}

sub auto_play {
    my ($self) = @_;
    my ($board) = $self->{'-board'};
   
    return if (! $board);	 
    if ($board->{'-auto_play'} && $board->can('auto_play')
        && ! $board->{'-auto_playing'}) {
        $board->{'-auto_playing'}=1;
        $board->auto_play();
        $board->{'-auto_playing'}=0;
    }
}

sub do_merge {
    my ($self,$cardpile) = @_;
    # merge with undo log and -auto_open processing
    my ($prevSplit) = $cardpile->{'-prevSplit'};

    # usually self merge is not allowed but we don't prohibit it here
    $self->merge($cardpile);
    $self->mergeable() if ($self->{'-mergeable'});
    if (defined($self->{'-board'})) {
	$self->{'-board'}->log_move(
	    CardPlay::CardPileUndo->new(
		-from=>$prevSplit,
		-to=>$self,
		-at=> - ($#{$cardpile->{'-contents'}}+1)));
    }
    $cardpile->{'-prevSplit'}=0;
    if ($prevSplit->{'-auto_open'}) {
	$prevSplit->open();
    }
    return;
}

sub split {
    my ($self,$i) = @_;
    my ($new);
    my ($tag,$j);
    my ($x,$y);
    my ($c) = $self->{'-canvas'};
    my (@contents);
    if ($i>=0) {
	@contents = splice(@{$self->{'-contents'}},$i);
    } else {
	@contents = splice(@{$self->{'-contents'}},$#{$self->{'-contents'}}+$i+1);
    }

    return 0 if (! @contents);
    $new = CardPlay::CardPile->new(-contents=>\@contents,-canvas=>$c);
    $new->{'-deltax'} = $self->{'-deltax'};
    $new->{'-deltay'} = $self->{'-deltay'};
    if (defined($self->{'-reverse_merge'})) {
	$new->reverse();
    }
    return $new if (! defined($c));

    if (defined($self->{'-board'})) {
	$new->{'-board'} = $self->{'-board'};
    }
    # push event traces $self's tag, so exchage it 
    $self->unbind();
    ($self->{'-tag'},$new->{'-tag'}) = ($new->{'-tag'},$self->{'-tag'});
    foreach $j ( @{$self->{'-contents'}} ) {
	$tag = $j->{'-tag'};
	$c->addtag($self->{'-tag'},withtag=>$tag);
    }
    $c->dtag($self->{'-tag'},$new->{'-tag'});
    if ($self->{'-basetag'}) {
	$c->dtag($self->{'-basetag'},$new->{'-tag'});
	$c->addtag($self->{'-tag'},withtag=>$self->{'-basetag'});
    }  
    $self->bind();
    if ($self->{'-splitable'}) {
	$new->unsplitable();
    }
    return $new;
}

sub split_event {
    my ($self,$i,$w) = @_;
# print STDERR "split_event ";$self->print;
    my ($e) = $w->XEvent;
    my ($lx, $ly) = ($e->x, $e->y);
    if (defined($self->{'-board'})) {
	return 0 if ($self->{'-board'}->{'-auto_playing'}) ;
    }
    return if ($self->{'-prevSplit'}); # reentering...
    return if ($self->can('can_split') && ! $self->can_split($i));
# print STDERR "splited... ",$new->print;
    my ($new) = $self->split($i); 
    return if (! $new);

    $new->{'-prevSplit'} = $self;
    $new->{'-lastX'} = $lx;
    $new->{'-lastY'} = $ly;
    $new->movable();
    $w->bind($new->{'-tag'}, 
	'<ButtonRelease-1>' => sub {$new->merge_event(@_)});

    $w->dtag('selected');
    $w->addtag('selected',withtag => $new->{-tag});
    $w->raise($new->{-tag});
}

sub do_split {
    my ($self,$i) = @_;
    my ($new) = $self->split($i); 
    return if (! $new);
    $new->{'-prevSplit'} = $self;
    $new->raise() if (defined($self->{'-contents'}));
    return $new;
}

sub unsplit {
    my ($self) = @_;
    my ($prevSplit) = $self->{'-prevSplit'};
    if ($prevSplit) {
# print STDERR "unsplit ",$prevSplit->print," merged ",$self->print;
	$prevSplit->merge($self);
	$self->{'-prevSplit'} = 0;
    } else {
	print STDERR "lost prevSplit\n";
    }
    # we should destory myself
}

sub coords {
    my ($self) = @_;
    my ($first) = $self->first();
    my ($x,$y);
    if (defined($first)) {
	($x,$y) =  $first->coords();
	return ($x,$y);
    } elsif (defined($self->{'-basetag'})) {
	($x,$y) =  $self->{'-canvas'}->coords($self->{'-basetag'});
	return ($x,$y);
    }
    return (undef,undef);
}

sub last_coords {
    my ($self) = @_;
    my ($last) = $self->last();
    my ($x,$y);
    if (defined($last)) {
	($x,$y) =  $last->coords();
	return ($x,$y);
    } elsif (defined($self->{'-basetag'})) {
    	($x,$y) = $self->{'-canvas'}->coords($self->{'-basetag'});
	return ($x-$self->{'-deltax'},$y-$self->{'-deltay'});
    }
    return (undef,undef);
}

sub bbox {
    my ($self) = @_;
    return $self->{'-canvas'}->bbox($self->{'-tag'});
}

1;

# end
