The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright (C) 1998 Tuomas J. Lukka
# DISTRIBUTED WITH NO WARRANTY, EXPRESS OR IMPLIED.
# See the GNU Library General Public License (file COPYING in the distribution)
# for conditions of use and redistribution.

package VRML::JS;
require DynaLoader;
@ISA=DynaLoader;
bootstrap VRML::JS;
use strict qw/vars/;
use vars qw/%Types/;

if($VRML::verbose::js) {
	set_verbose(1);
}

# Unlike with the Java interface, we have one object per script
# for javascript.

init(); # C-level init

%Types = (
	SFBool => sub {$_[0] ? "true" : "false"},
	SFFloat => sub {$_[0]+0},
	SFTime => sub {$_[0]+0},
	SFInt32 => sub {$_[0]+0},
	SFString => sub {'"'.$_[0].'"'}, # XXX
	SFNode => sub {'new SFNode("","'.(VRML::Handles::reserve($_[0])).'")'},
);

sub new {
	my($type,$text,$node,$browser) = @_;
	my $this = bless { },$type;
	$this->{GLO} = "";
	$this->{CX} = newcontext($this->{GLO},$this);
	$this->{Node} = $node;
	$this->{Browser} = $browser;
	print "START JS $text\n" if $VRML::verbose::js;
	my $rs;
	print "INITIALIZE $this->{CX} $this->{GLO}\n" if $VRML::verbose::js;
	# Create default functions 
	runscript($this->{CX}, $this->{GLO}, 
		"function initialize() {} function shutdown() {}
		 function eventsProcessed() {} TRUE=true; FALSE=false; ", $rs);
	print "TEXT $this->{CX} $this->{GLO}\n" if $VRML::verbose::js;
	runscript($this->{CX}, $this->{GLO}, $text, $rs);
# Initialize fields.
	my $t = $node->{Type};
	my @k = keys %{$t->{Defaults}};
	print "TY: $t\n" if $VRML::verbose::js;
	print "FIELDS\n" if $VRML::verbose::js;
	for(@k) {
		next if $_ eq "url" or $_ eq "mustEvaluate" or $_ eq "directOutput";
		my $type = $t->{FieldTypes}{$_};
		my $ftype = "VRML::Field::$type";
		print "CONSTR FIELD $_\n" if $VRML::verbose::js;
		if($t->{FieldKinds}{$_} eq "field" or
  		   $t->{FieldKinds}{$_} eq "eventOut") {
			print "JS FIELD $_\n" if $VRML::verbose::js;
			if($Types{$type}) {
				addwatchprop($this->{CX},$this->{GLO},
					$_);
			} else {
				addasgnprop($this->{CX},$this->{GLO},
				    $_, $ftype->js_default);
			}
			if($t->{FieldKinds}{$_} eq "field") {
				my $value = $node->{RFields}{$_};
				print "JS FIELDPROP $_\n" if $VRML::verbose::js;
				if($Types{$type}) {
					print "SET_TYPE $_ '$value'\n" if $VRML::verbose::js;
					my $v = runscript($this->{CX}, $this->{GLO}, 
					  "$_=".$Types{$type}->($value), $rs);
				} else {
					$this->set_prop($_, $value, $_);
				}
			}
			print "CONED\n" if $VRML::verbose::js;
		} elsif($t->{FieldKinds}{$_} eq "eventIn") {
			if($Types{$type}) {
			} else {
				addasgnprop($this->{CX},$this->{GLO},
				    "__tmp_arg_$_", $ftype->js_default);
			}
		} else {
			warn("INVALID FIELDKIND '$_' for $node->{TypeName}");
		}
	}
	# Ignore all events we may have sent while building
	$this->gathersent(1);
	return $this;
}

sub initialize {
	my($this) = @_;
	my $rs;
	runscript($this->{CX}, $this->{GLO}, "initialize()", $rs);
	$this->gathersent();
}

sub sendevent {
	my($this,$node,$event,$value,$timestamp) = @_;
	my $rs;
	my $typ = $node->{Type}{FieldTypes}{$event};
	print "JS: receive event $node $event $value $timestamp ($typ)\n"
		if $VRML::verbose::js;
	my $aname = "__tmp_arg_$event";
	$this->set_prop($event,$value,$aname);
	runscript($this->{CX}, $this->{GLO}, "$event($aname,$timestamp)", $rs);
	return $this->gathersent();

	unless($Types{$typ}) {
		&{"set_property_$node->{Type}{FieldTypes}{$event}"}(
			$this->{CX}, $this->{GLO}, "__evin", $value);
		runscript($this->{CX}, $this->{GLO}, "$event(__evin,$timestamp)", $rs);
	} else {
		print "JS sendevent $event $timestamp\n".
			"$event(".$Types{$typ}->($value).",$timestamp)\n"
				if $VRML::verbose::js;
		my $v = runscript($this->{CX}, $this->{GLO}, 
			"$event(".$Types{$typ}->($value).",$timestamp)", $rs);
		print "GOT: $v $rs\n"
			if $VRML::verbose::js;
	}
	$this->gathersent();
}

sub sendeventsproc {
	my($this) = @_;
	my $rs;
	runscript($this->{CX}, $this->{GLO}, "eventsProcessed()", $rs);
	$this->gathersent();
}

sub gathersent {
	my($this, $ignore) = @_;
	my $node = $this->{Node};
	my $t = $node->{Type};
	my @k = keys %{$t->{Defaults}};
	my @a;
	my $rs;
	for(@k) {
		next if $_ eq "url";
		my $type = $t->{FieldTypes}{$_};
		my $ftyp = $type;
		if($t->{FieldKinds}{$_} eq "eventOut") {
			print "JS EOUT $_\n"
				if $VRML::verbose::js;
			my $v;
			if($type =~ /^MF/) {
				$v = runscript($this->{CX},$this->{GLO},
					"$_.__touched_flag",$rs);
				runscript($this->{CX},$this->{GLO},
					"$_.__touched_flag = 0",$rs);
			} elsif($Types{$ftyp}) {
				$v = runscript($this->{CX},$this->{GLO},
					"_${_}_touched",$rs);
				runscript($this->{CX},$this->{GLO},
					"_${_}_touched = 0",$rs);
				# print "SIMP_TOUCH $v\n";
			} else {
				$v = runscript($this->{CX},$this->{GLO},
					"$_.__touched()",$rs);
			}
			print "GOT $v $rs $_\n"
				if $VRML::verbose::js;
			if($v && !$ignore) {
				push @a, [$node, $_,
					$this->get_prop($type,$_)];
			}
		}
		# $this->{O}->print("$t->{FieldKinds}{$_}\n
	}
	return @a;
}

sub set_prop { # Assigns a value to a property.
	my($this,$field,$value,$prop) = @_;
	my $typ = $this->{Node}{Type};
	my $ftyp;
	if($field =~ s/^____//) { # recurse hack
		$ftyp = $field;
	} else {
		$ftyp = $typ->{FieldTypes}{$field};
	}
	my $rs;
	my $i;
	if($ftyp =~ /^MF/) {
		my $styp = $ftyp; $styp =~ s/^MF/SF/;
		for($i=0; $i<$#{$value}; $i++) {
			$this->set_prop("____$styp", $value->[$i], "____tmp");
			runscript($this->{CX}, $this->{GLO},
				"$prop"."[$i] = ____tmp");
		}
		runscript($this->{CX},$this->{GLO},
		  "$prop.__touched_flag = 0",$rs);
	} elsif($Types{$ftyp}) {
		runscript($this->{CX},$this->{GLO}, 
			"$prop = ".(&{$Types{$ftyp}}($value)),
			$rs);
		runscript($this->{CX},$this->{GLO},"_${prop}__touched=0",$rs);
	} else {
		print "set_property_ CALL: $ftyp $prop $value\n"
			if $VRML::verbose::js;
		&{"set_property_$ftyp"}(
			$this->{CX}, $this->{GLO}, $prop, $value);
		runscript($this->{CX},$this->{GLO},"$prop.__touched()",$rs);
	}
}

sub get_prop {
	my($this,$type,$prop) = @_;
	my $rs;
	print "RS2: $rs\n"
		if $VRML::verbose::js;
	if($type =~ /^SFNode$/) {
		runscript($this->{CX},$this->{GLO},
			"$prop.__id",$rs);
		return VRML::Handles::get($rs);
	} elsif ($type =~ /^MFNode$/) {
		my $l = runscript($this->{CX},$this->{GLO},
			"$prop.length",$rs);
		print "LENGTH: $l, '$rs'\n"
			if $VRML::verbose::js;
		my $fn = $prop;
		my @res = map {
		     runscript($this->{CX},$this->{GLO},
			"$fn",$rs);
		     print "Just mfnode: '$rs'\n"
		     	if $VRML::verbose::js;
		     runscript($this->{CX},$this->{GLO},
			"$fn"."[$_]",$rs);
		     print "Just node: '$rs'\n"
		     	if $VRML::verbose::js;
		     runscript($this->{CX},$this->{GLO},
			"$fn"."[$_][0]",$rs);
		     print "Just node[0]: '$rs'\n"
		     	if $VRML::verbose::js;
		     runscript($this->{CX},$this->{GLO},
			"$fn"."[$_].__id",$rs);
		     print "MFN: Got '$rs'\n"
		     	if $VRML::verbose::js;
		     VRML::Handles::get($rs);
		} (0..$l-1);
		return \@res;
	} elsif ($type =~ /^MFString$/) {
		my $l = runscript($this->{CX},$this->{GLO},
			"$prop.length",$rs);
		my $fn = $prop;
		my @res = map {
		     runscript($this->{CX},$this->{GLO},
			"$fn"."[$_]",$rs);
		     $rs
		} (0..$l-1);
		return \@res;
	}elsif($type =~ /^MF/) {
		my $l = runscript($this->{CX},$this->{GLO},
			"$prop.length",$rs);
		print "LENGTH: $l, '$rs'\n"
			if $VRML::verbose::js;
		my $fn = $prop;
		my $st = $type;
		$st =~ s/MF/SF/;
		my @res = map {
		     runscript($this->{CX},$this->{GLO},
			"$fn"."[$_]",$rs);
		     print "RES: '$rs'\n"
		     	if $VRML::verbose::js;
		     (pos $rs) = 0;
		     "VRML::Field::$st"
		      -> parse(undef, $rs);
		} (0..$l-1);
		print "RESVAL:\n"
			if $VRML::verbose::js;
		for(@res) {
			if("ARRAY" eq ref $_) {
				print "@$_\n"
					if $VRML::verbose::js;
			}
		}
		my $r = \@res;
		print "REF: $r\n"
			if $VRML::verbose::js;
		return $r;
	} elsif($Types{$type}) {
		my $v = runscript($this->{CX},$this->{GLO},
			"_${_}_touched=0; $prop",$rs);
		print "SIMP VAL: $v '$rs'\n"
			if $VRML::verbose::js;
		return $v;
	} else {
		runscript($this->{CX},$this->{GLO},
			"$prop",$rs);
		# print "VAL: $rs\n";
		(pos $rs) = 0;
		return "VRML::Field::$type"->parse(undef,$rs);
	}
}

sub node_setprop {
	my($this) = @_;
	print "SETTING NODE PROP\n"
		if $VRML::verbose::js;
	my ($node, $prop, $val);
	runscript($this->{CX},$this->{GLO},"__node.__id",$node);
	runscript($this->{CX},$this->{GLO},"__prop",$prop);
	print "SETTING NODE PROP R: '$node' '$prop' \n"
		if $VRML::verbose::js;
	$node = VRML::Handles::get($node);
	my $vt = $node->{Type}{FieldTypes}{$prop};
	if(!defined $vt) {
		die("Javascript tried to assign to invalid property!\n");
	}
	my $val = $this->get_prop($vt, "__val");
#	if($vt =~ /Node/) {die("Can't handle yet");}
#	if($Types{$vt}) {
#		runscript($this->{CX},$this->{GLO},"__val",$val);
#		print "GOT '$val'\n";
#		$val = "VRML::Field::$vt"->parse(undef, $val);
#	} else {
#		runscript($this->{CX},$this->{GLO},"__val.toString()",$val);
#		print "GOT '$val'\n";
#		$val = "VRML::Fields::$vt"->parse(undef, $val);
#	}
	print "SETTING TO '$val'\n"
		if $VRML::verbose::js;
	$node->{RFields}{$prop} = $val;

}

sub brow_getName {
	my($this) = @_;
	print "Brow:getname ($this) !\n"
		if $VRML::verbose::js;
	my $n = $this->{Browser}->getName(); my $rs;
	runscript($this->{CX},$this->{GLO},"Browser.__bret = \"$n\"",$rs);
}

sub brow_getVersion {
	my($this) = @_;
	print "Brow:getname ($this) !\n"
		if $VRML::verbose::js;
	my $n = $this->{Browser}->getVersion(); my $rs;
	runscript($this->{CX},$this->{GLO},"Browser.__bret = \"$n\"",$rs);
}

sub brow_getCurrentFrameRate {
	my($this) = @_;
	print "Brow:getname ($this) !\n"
		if $VRML::verbose::js;
	my $n = $this->{Browser}->getCurrentFrameRate(); my $rs;
	runscript($this->{CX},$this->{GLO},"Browser.__bret = $n",$rs);
}

sub brow_createVrmlFromString {
	my($this) = @_; my $rs;
	runscript($this->{CX},$this->{GLO},"Browser.__arg0",$rs);
	print "BROW_CVRLFSTR '$rs'\n"
		if $VRML::verbose::js;
	my $mfn = $this->{Browser}->createVrmlFromString(
		$rs
	);
	my @hs = map {VRML::Handles::reserve($_)} @$mfn;
	my $sc = "Browser.__bret = new MFNode(".
		(join ',',map {qq'new SFNode("","$_")'} @hs).")";
	runscript($this->{CX},$this->{GLO},$sc,$rs);
}