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

package Vertex;
# Vertex of a Graph

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

use Configurable;
use Vector;
use overload
    '""' => 'stringify',
    'fallback' => 1
#    'eq' => '()',
#    'fallback' => undef
;

sub new {
    my ($class, $host, $pos, %opts) = @_;
    $class = ref($class) if ref($class);
    my ($self) = $class->SUPER::new(%opts);
    $self->{"#host"} = $host;
    my ($cv) = $self->host()->cget(-canvas);
    my ($sh) = $self->cget(-shape);
    if ("\L$sh" eq "oval") {
	$self->{shape_id} = $cv->createOval(0,0,2,2);
    } else {
	$self->{shape_id} = $cv->createRectangle(0,0,2,2);
    }
    $self->{text_id} = $cv->createText(0, 0, -justify=>"center");
    $self->set_pos($pos);
    $self->set_size($self->cget(-size));
    $self->configure($self->get_all_opts());
    # the following is needed for easier binding statements
#    $self->{-host}{-canvas}->addtag($self->{-name}, "withtag", $self->{shape_id});
#    $self->{-host}{-canvas}->addtag($self->{-name}, "withtag", $self->{text_id});
    return $self;
}

sub destroy {
    my ($self) = @_;
    $self->host()->cget(-canvas)->delete(@{$self}{"shape_id","text_id"});
}

sub host {
    return $_[0]->{"#host"};
}

sub _get_cv_geom_ {
    my ($self) = @_;
    my (@t) = $self->host()->cget(-canvas)->coords($self->{shape_id});
    return (
	Vector->new(($t[0] + $t[2]) / 2, ($t[1] + $t[3]) / 2),
	Vector->new(abs($t[0] - $t[2]), abs($t[1] - $t[3])),
    );
}

sub pos {
    my ($self) = @_;
    croak "you probably wanted to call set_pos()?" if $#_>0;
    my ($lt) = $self->host()->cget(-linear_transform);
    my ($pos, undef) = $self->_get_cv_geom_();
    return ($pos - $lt->{-offset})->pw_div($lt->{-scale});
}

sub size {
    my ($self) = @_;
    croak "you probably wanted to call set_size()?" if $#_>0;
    my ($lt) = $self->host()->cget(-linear_transform);
    my (undef, $size) = $self->_get_cv_geom_();
    return $size->pw_div($lt->{-scale});
}

sub set_pos {
    my ($self, $pos) = @_;
    my ($lt) = $self->host()->cget(-linear_transform);
    my (undef, $size) = $self->_get_cv_geom_();
    $size = $size->pw_div(2);
    $pos = $pos->pw_mul($lt->{-scale}) + $lt->{-offset};
    my ($cv) = $self->host()->cget(-canvas);
    $cv->coords($self->{text_id}, @$pos);
    $cv->coords($self->{shape_id},
	@{ $pos-$size }, @{ $pos+$size }
    );
}

sub set_size {
    my ($self, $size) = @_;
    my ($lt) = $self->host()->cget(-linear_transform);
    my ($pos, undef) = $self->_get_cv_geom_();
    $size = $lt->{-scale}->pw_mul($size)->pw_div(2);
    $self->host()->cget(-canvas)->coords($self->{shape_id},
	@{ $pos-$size }, @{ $pos+$size }
    );
}

sub configure {
    my ($self, %opts) = @_;
    my ($k, %shape_opts, %text_opts);
    my ($opt_map) = {
	-text    => [undef, "-text"],
	-fill    => ["-fill", undef],
	-outline => ["-outline", "-fill"],
	-thick   => ["-width", undef],
	-arrow   => ["-arrow", undef],
	-stipple => ["-stipple", undef],
	-outlinestipple => [undef, undef],
	-state   => ["-state", "-state"],
    };

    if (exists $opts{-name}) {
	$self->{-name} = delete $opts{-name};
	$opts{-text} = $self->cget(-display)->($self)
	    if ref $self->cget(-display) eq "CODE";
    }
    if (exists $opts{-content}) {
	$self->{-content} = delete $opts{-content};
	$opts{-text} = $self->cget(-display)->($self)
	    if ref $self->cget(-display) eq "CODE";
    }
    if (exists $opts{-status}) {
	$self->{-status} = delete $opts{-status};
	my ($ha) = $self->host()->cget(-appearance);
	carp "unknown status $self->{-status} ignored"
	    unless exists $ha->{$self->{-status}};
	%opts = (%{ $ha->{$self->{-status}} }, %opts);
    }
    foreach $k (keys %opts) {
	carp "unknown option $k ignored" unless exists($opt_map->{$k});
	$shape_opts{ $opt_map->{$k}[0] } = $opts{$k}
	    if defined $opt_map->{$k}[0];
	$text_opts{ $opt_map->{$k}[1] } = $opts{$k}
	    if defined $opt_map->{$k}[1];
    }
    my ($cv) = $self->host()->cget(-canvas);
    $cv->itemconfigure($self->{shape_id}, %shape_opts);
    $cv->itemconfigure($self->{text_id}, %text_opts);
}

sub display {
    # serves to print or display a vertex
    my ($self) = @_;
    my ($s) = $self->cget(-display)->($self);
    $s =~ s/\n/ /g;
    return "V[$s]";
}

sub stringify {
    # serves to identify a vertex, such as key for hash
    my ($self) = @_;
    return $self->cget(-name);
}

sub get_all_opts {
    my ($self) = @_;
    my (%opts) = $self->SUPER::get_all_opts();
    delete @opts{qw(-display -shape -size)};
    return %opts;
}

$::Config->{Vertex} = {
    -shape => "oval",
    -size => Vector->new(50, 30),
    -status => "init",
    -display => sub { return $_[0]->cget(-name); }
};

1;

