#!/usr/bin/perl
# -*- perl -*-

open(E, "/envy/dj/games/quake3/tools/entities.def");
while (<E>) {
    if (m@^/\*QUAKED (\S+) .*\(.*\(.*\(@) {
	s/[()]//g;
	($junk, $name, $r, $g, $b, $lx, $ly, $lz, $ux, $uy, $uz) = split(' ');
	#print "$name  $lx $ly $lz  $ux $uy $uz\n";
	$entity{$name} = 1;
	$entity{"$name lx"} = $lx;
	$entity{"$name ly"} = $ly;
	$entity{"$name lz"} = $lz;
	$entity{"$name ux"} = $ux;
	$entity{"$name uy"} = $uy;
	$entity{"$name uz"} = $uz;
    }
}
close(E);

sub add_entity {
    my ($class, @rest) = @_;
    push(@map, {});
    $map[$#map]{'classname'} = $class;
    $map[$#map]{'brushes'} = [];
    while ($#rest > 0) {
	my ($key, $val);
	$key = shift @rest;
	$val = shift @rest;
	if ($key eq "origin" && $val =~ /^(above|below) /) {
	    die("unknown entity $class") unless $entity{$class};
	    my ($align, $x, $y, $z) = split(' ', $val);
	    $z -= $entity{"$class lz"} if $align eq "above";
	    $z -= $entity{"$class uz"} if $align eq "below";
	    #print "align $class from $val to $x $y $z\n";
	    $val = "$x $y $z";
	}
	$map[$#map]{$key} = $val;
    }
    $last_entity = $map[$#map];
    return $last_entity;
}

sub add_brush {
    my ($entity) = @_;
    $entity = $last_entity unless ref $entity;
    my $b = $#{$entity->{'brushes'}} + 1;
    $entity->{'brushes'}[$b] = [];
    $last_brush = $entity->{'brushes'}[$b];
    return $last_brush;
}

sub add_plane {
    if (ref $_[0]) {
	$last_brush = $_[0];
	shift @_;
    }
    my ($texture, @points) = @_;
    my $p = $#{$last_brush} + 1;
    $last_brush->[$p]{'texture'} = $texture;
    $last_brush->[$p]{'points'} = [ @points ];
    $last_brush->[$p]{'xscale'} = 1;
    $last_brush->[$p]{'yscale'} = 1;
    $last_plane = $last_brush->[$p];
    return $last_plane;
}

sub cross_product {
    my ($ax, $ay, $az, $bx, $by, $bz, $cx, $cy, $cz) = @_;
    my ($vx, $vy, $vz) = ($ax-$bx, $ay-$by, $az-$bz);
    my ($wx, $wy, $wz) = ($cx-$bx, $cy-$by, $cz-$bz);
    my ($x, $y, $z);
    $x = $vy*$wz - $vz*$wy;
    $y = $vz*$wx - $vx*$wz;
    $z = $vx*$wy - $vy*$wx;
    #print "($vx $vy $vz) x ($wx $wy $wz) = ($x $y $z)\n";
    return ($x, $y, $z);
}

sub normalize {
    my ($length, $x, $y, $z) = @_;
    my ($v) = sqrt ($x*$x + $y*$y + $z*$z);
    #print "norm $length ($x $y $z) = ";
    if ($v > 0) {
	$x *= $length/$v;
	$y *= $length/$v;
	$z *= $length/$v;
    }
    #print "($x $y $z)\n";
    return ($x, $y, $z);
}

sub make_surface {
    my ($texture, @p) = @_;
    my ($mx, $my, $nz, $i);
    &add_brush();
    ($nx, $ny, $nz) = &normalize(4, &cross_product(@p));
    my ($np) = $#p + 1;
    push(@p, @p[0..2]);
    for ($i = 0; $i < $np; $i += 3) {
	&add_plane ("house/wkitchen",
		    $p[$i+3]-$nx, $p[$i+4]-$ny, $p[$i+5]-$nz,
		    $p[$i+3], $p[$i+4], $p[$i+5],
		    $p[$i+0], $p[$i+1], $p[$i+2]);
    }
    &add_plane ("house/wkitchen",
		$p[3]-$nx, $p[4]-$ny, $p[5]-$nz,
		$p[0]-$nx, $p[1]-$ny, $p[2]-$nz,
		$p[6]-$nx, $p[7]-$ny, $p[8]-$nz);
    &add_plane($texture, @p[0..9]);
}

sub save_map {
    my ($file) = @_;
    open (MAP, ">$file");
    for $e (0..$#map) {
	print MAP "// entity $e\n";
	print MAP "{\n";
	for $k (sort keys %{$map[$e]}) {
	    if ($k ne 'brushes') {
		print MAP "  \"$k\" \"", $map[$e]{$k}, "\"\n";
	    }
	}
	for $bi (0..$#{$map[$e]{'brushes'}}) {
	    $b = $map[$e]{'brushes'}[$bi];
	    print MAP "  // brush $bi\n";
	    print MAP "  {\n";
	    for $pi (0..$#{$b}) {
		$p = $b->[$pi];
		printf MAP ("    ( %d %d %d ) ( %d %d %d ) ( %d %d %d ) %s %d %d %d %g %g %d %d %d\n",
			    $p->{'points'}[0], $p->{'points'}[1], $p->{'points'}[2],
			    $p->{'points'}[3], $p->{'points'}[4], $p->{'points'}[5],
			    $p->{'points'}[6], $p->{'points'}[7], $p->{'points'}[8],
			    $p->{'texture'},
			    $p->{'xoff'}, $p->{'yoff'}, $p->{'rot'},
			    $p->{'xscale'}, $p->{'yscale'},
			    $p->{'red'}, $p->{'green'}, $p->{'blue'});
	    }
	    print MAP "  }\n";
	}
	print MAP "}\n";
    }
    close(MAP);
}

1;
