#!/usr/bin/perl -w
# NAME: chkjsac.pl
# AIM: Read a the javsscipt file generated from an AC3D file, and...
# 07/05/2013 geoff mclane http://geoffair.net/mperl
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.1 2013-03-17";
my $load_log = 0;
my $in_file = '';
my $verbosity = 0;
my $out_file = '';
my @in_files = ();

# ### DEBUG ###
my $debug_on = 1;
my $def_file = 'C:\FG\18\blendac3d\cube.js';
my $def_file2 = 'C:\FG\18\blendac3d\cube2.js';

#my $def_file = 'C:\FG\17\fgx-globe\fgx-planes\f16\f16.js';
#my $def_file2 = 'C:\FG\18\blendac3d\f16.js';
###my $def_file3 = 'C:\FG\18\ac3d2gl\build\ac3dloader\tempdump.json';
#my $def_file3 = 'C:\OSGeo4W\apache\htdocs\fgx-globe\cookbook\load-plane-3d\test\test.js';
### program variables
my @warnings = ();
my $cwd = cwd();

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


sub prtw($) {
   my ($tx) = shift;
   $tx =~ s/\n$//;
   prt("$tx\n");
   push(@warnings,$tx);
}


sub isBitSet($$) {
    my ($val, $pos) = @_;
	return ($val & ( 1 << $pos )) ? 1 : 0;
}

sub show_type_flag($) {
    my $type = shift;
	my $isQuad              = isBitSet( $type, 0 );
	my $hasMaterial         = isBitSet( $type, 1 );
	my $hasFaceUv           = isBitSet( $type, 2 );
	my $hasFaceVertexUv     = isBitSet( $type, 3 );
	my $hasFaceNormal       = isBitSet( $type, 4 );
	my $hasFaceVertexNormal = isBitSet( $type, 5 );
	my $hasFaceColor	     = isBitSet( $type, 6 );
	my $hasFaceVertexColor  = isBitSet( $type, 7 );
    prt("Type $type bits ".$isQuad.$hasMaterial.$hasFaceUv.$hasFaceVertexUv.$hasFaceNormal.$hasFaceVertexNormal.$hasFaceColor.$hasFaceVertexColor." ");
    prt(sprintf("isQad=%s ", ($isQuad ? "On" : "Off")));
    prt(sprintf("hasMat=%s ", ($hasMaterial ? "On" : "Off")));
    prt(sprintf("hasFUv=%s ", ($hasFaceUv ? "On" : "Off")));
    prt(sprintf("hasVUv=%s ", ($hasFaceVertexUv ? "On" : "Off")));
    prt(sprintf("hasFNorm=%s ", ($hasFaceNormal ? "On" : "Off")));
    prt(sprintf("hasVNorm=%s ", ($hasFaceVertexNormal ? "On" : "Off")));
    prt(sprintf("hasFC=%s ", ($hasFaceColor ? "On" : "Off")));
    prt(sprintf("hasVC=%s ", ($hasFaceVertexColor ? "On" : "Off")));
    prt("\n");
}



sub check_faces($$$) {
    my ($ra,$faces,$norms) = @_;   # ( \@farr, $faces );
    my $cnt = scalar @{$ra};
    my $offset = 0;
    my ($type,$isQuad,$hasMaterial,$hasFaceUv,$hasFaceVertexUv);
	my ($hasFaceNormal,$hasFaceVertexNormal,$hasFaceColor,$hasFaceVertexColor);
    my ($nVertices,@face,$i);
    my $nUvLayers = 0;
    my $nNormals = scalar @{$norms};
    while ($offset < $cnt) {
        $type = ${$ra}[$offset++];
        show_type_flag($type);
		#//console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
        prt("Type $type bits ".$isQuad.$hasMaterial.$hasFaceUv.$hasFaceVertexUv.$hasFaceNormal.$hasFaceVertexNormal.$hasFaceColor.$hasFaceVertexColor." ");
        @face = ();
        if ( $isQuad ) {
            #face = new THREE.Face4();
            $face[0] = ${$ra}[ $offset ++ ];
            $face[1] = ${$ra}[ $offset ++ ];
            $face[2] = ${$ra}[ $offset ++ ];
            $face[3] = ${$ra}[ $offset ++ ];
            $nVertices = 4;
        } else {
            #face = new THREE.Face3();
            $face[0] = ${$ra}[ $offset ++ ];
            $face[1] = ${$ra}[ $offset ++ ];
            $face[2] = ${$ra}[ $offset ++ ];
            $nVertices = 3;
        }
        prt(join(",",@face)." ");
		if ( $hasMaterial ) {
			my $materialIndex = ${$ra}[ $offset ++ ];
			#	face.materialIndex = materialIndex;
		}
		#	// to get face <=> uv index correspondence
		#	fi = geometry.faces.length;
        if ( $hasFaceUv ) {
            if ($nUvLayers) {
                for ( $i = 0; $i < $nUvLayers; $i++ ) {
                    #uvLayer = json.uvs[ i ];
                    my $uvIndex = ${$ra}[ $offset ++ ];
                    #u = uvLayer[ uvIndex * 2 ];
                    #v = uvLayer[ uvIndex * 2 + 1 ];
                    #geometry.faceUvs[ i ][ fi ] = new THREE.Vector2( u, v );
                }
            } else {
                prt("FACES failed - has FaceUv flag, but NO uvLayers!\n");
                last;
            }
        }
        if ( $hasFaceVertexUv ) {
            if ($nUvLayers) {
                for ( $i = 0; $i < $nUvLayers; $i++ ) {
                    #uvLayer = json.uvs[ i ];
                    #uvs = [];
                    #for ( j = 0; j < nVertices; j ++ ) {
                    my $uvIndex = ${$ra}[ $offset ++ ];
                    #    u = uvLayer[ uvIndex * 2 ];
                    #    v = uvLayer[ uvIndex * 2 + 1 ];
                    #    uvs[ j ] = new THREE.Vector2( u, v );
                    #}
                    #geometry.faceVertexUvs[ i ][ fi ] = uvs;
                }
            } else {
                prt("FACES failed - has FaceVertexUv flag, but NO uvLayers!\n");
                last;
            }
        }
        if ( $hasFaceNormal ) {
            my $normalIndex = ${$ra}[ $offset ++ ] * 3;
            #normal = new THREE.Vector3();
            #normal.x = normals[ normalIndex ++ ];
            #normal.y = normals[ normalIndex ++ ];
            #normal.z = normals[ normalIndex ];
            #face.normal = normal;
            prt("FaceNorm index $normalIndex ");
        }
        if ( $hasFaceVertexNormal ) {
            prt("FaceVertNorm ");
            if ($nNormals) {
                for ( $i = 0; $i < $nVertices; $i++ ) {
                    my $normalIndex = ${$ra}[ $offset ++ ] * 3;
                    #normal = new THREE.Vector3();
                    #normal.x = normals[ normalIndex ++ ];
                    #normal.y = normals[ normalIndex ++ ];
                    #normal.z = normals[ normalIndex ];
                    #face.vertexNormals.push( normal );
                    prt( ${$norms}[ $normalIndex++ ]." " );
                    prt( ${$norms}[ $normalIndex++ ]." " );
                    prt( ${$norms}[ $normalIndex++ ]." " );
                }
            } else {
                prt("FACES failed - has FaceVertexNorm flag, but NO nNorms!\n");
                last;
            }
        }
        if ( $hasFaceColor ) {
            prt("hasFaceColor ");
            my $colorIndex = ${$ra}[ $offset ++ ];
            ###color = new THREE.Color( colors[ colorIndex ] );
            ###face.color = color;
        }
        if ( $hasFaceVertexColor ) {
            prt("hasFaceVertColor ");
            for ( $i = 0; $i < $nVertices; $i++ ) {
                my $colorIndex = ${$ra}[ $offset ++ ];
                #color = new THREE.Color( colors[ colorIndex ] );
                #face.vertexColors.push( color );
            }
        }
        prt("\n");
        ###last;
    }
}

#		"vertices"      : 1393,
#		"faces"         : 2149,
#		"normals"       : 1246,
sub process_in_file($) {
    my ($inf) = @_;
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("\nProcessing $lncnt lines, from [$inf]...\n");
    my ($line,$i,$lnn,$cnt);
    my $verts = 0;
    my $faces = 0;
    my $norms = 0;
    my $vlist = '';
    my $flist = '';
    my $nlist = '';
    my @varr = ();
    my @farr = ();
    my @narr = ();
    my $minx = 99999;
    my $maxx = -99999;
    my $miny = 99999;
    my $maxy = -99999;
    my $minz = 99999;
    my $maxz = -99999;
    my ($vcnt,$vx,$vy,$vz);
    $lnn = 0;
    my $fvc = 0;
    my $fnc = 0;
    my $ffc = 0;
    my $fvl = 0;
    my $fnl = 0;
    my $ffl = 0;
    foreach $line (@lines) {
        chomp $line;
        $lnn++;
        if ($line =~ /^\s*"vertices"\s*:\s*(\d+),/) {
            $verts = $1;
            prt("$lnn: vertices  = $verts\n");
            $fvc = 1;
        } elsif ($line =~ /^\s*"faces"\s*:\s*(\d+),/) {
            $faces = $1;
            prt("$lnn: faces     = $faces\n");
            $ffc = 1;
        } elsif ($line =~ /^\s*"normals"\s*:\s*(\d+),/) {
            $norms = $1;
            prt("$lnn: normals   = $norms\n");
            $fnc = 1;
        } elsif ($line =~ /^\s*"vertices"\s*:\s*\[(.+)\]\s*,/) {
            $vlist = $1;
            @varr = split(',',$vlist);
            $cnt = scalar @varr;
            prt("$lnn: vertices = ");
            if (length($vlist) > 80) {
                prt(substr($vlist,0,80)."...\n");
            } else {
                prt("$vlist\n");
            }
            $vcnt = ($cnt / 3);
            if ($vcnt == $verts) {
                prt("VERTS: Total count $cnt, ie $vcnt sets of 3 verts ($verts)\n");
            } else {
                prtw("WARNING: VERTS total count $cnt, ie $vcnt sets of 3 verts ($verts)\nFile: $inf\n");
            }
            $fvl = 1;

        } elsif ($line =~ /^\s*"faces"\s*:\s*\[(.+)\]\s*,/) {
            $flist = $1;
            @farr = split(',',$flist);
            $cnt = scalar @farr;
            prt("$lnn: faces    = ");
            if (length($flist) > 80) {
                prt(substr($flist,0,80)."...\n");
            } else {
                prt("$flist\n");
            }
            prt("FACES: Total count $cnt ($faces)\n");
            check_faces( \@farr, $faces, \@narr );
            
            $vcnt = ($cnt / 8);
#            if ($vcnt == $faces) {
#                prt("FACES: Total count $cnt, ie $vcnt sets of 8 values ($faces)\n");
#            } else {
#                $vcnt = ($cnt / 3);
#                if ($vcnt == $faces) {
#                    prt("FACES: Total count $cnt, ie $vcnt sets of 3 values ($faces)\n");
#                } else {
#                    prtw("WARNING: FACES total count $cnt, ie $vcnt sets of 3 values ($faces)\nFile: $inf\n");
#                }
#            }
            $ffl = 1;
        } elsif ($line =~ /^\s*"normals"\s*:\s*\[(.+)\]\s*,/) {
            $nlist = $1;
            @narr = split(',',$nlist);
            $cnt = scalar @narr;
            prt("$lnn: normals  = ");
            if (length($nlist) > 80) {
                prt(substr($nlist,0,80)."...\n");
            } else {
                prt("$nlist\n");
            }
            $vcnt = ($cnt / 3);
            if ($vcnt == $norms) {
                prt("NORMS: Total count $cnt ie $vcnt sets of 3 ($norms)\n");
            } else {
                prt("WARNING: NORMS total count $cnt ie $vcnt sets of 3 ($norms)\nFile: $inf\n");
            }
            $fnl = 1;
        }
    }
    if ($fvc && $fnc && $ffc && $fvl && $fnl && $ffl) {
        prt("Appears ALL items FOUND...\n") if (VERB9());
    } else {
        prtw("WARNING: Something NOT found $fvc $fnc $ffc $fvl $fnl $ffl!\n");
    }

    $cnt = scalar @varr;
    $vcnt = int($cnt / 3);
    if (($cnt >= 3) && $vcnt) {
        $minx = $varr[0];
        $maxx = $varr[0];
        $miny = $varr[1];
        $maxy = $varr[1];
        $minz = $varr[2];
        $maxz = $varr[2];

        for ($i = 0; $i < $vcnt; $i++) {
            $vx = $varr[($i * 3) + 0];
            $vy = $varr[($i * 3) + 1];
            $vz = $varr[($i * 3) + 2];
            $minx = $vx if ($vx < $minx);
            $maxx = $vx if ($vx > $maxx);
            $miny = $vy if ($vy < $miny);
            $maxy = $vy if ($vy > $maxy);
            $minz = $vz if ($vz < $minz);
            $maxz = $vz if ($vz > $maxz);
        }

        prt("x,y,z MINS: $minx,$miny,$minz MAXS: $maxx,$maxy,$maxz\n");
    } else {
        prtw("WARNING: Got count $cnt, vert count $vcnt! WHAT IS WRONG?\n");
    }

}

sub process_in_files($) {
    my $ra = shift;
    my ($file);
    my %dupes = ();
    foreach $file (@{$ra}) {
        if (!defined $dupes{$file}) {
            $dupes{$file} = 1;
            process_in_file($file);
        }
    }
}

#########################################
### MAIN ###
parse_args(@ARGV);
process_in_files(\@in_files);
pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                prt("Verbosity = $verbosity\n") if (VERB1());
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if (VERB1());
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if (VERB1());
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            push(@in_files,$in_file);
            prt("Set input to [$in_file]\n") if (VERB1());
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($in_file) ==  0) {
            $in_file = $def_file;
            push(@in_files,$in_file);
            prt("Set DEFAULT input to [$in_file]\n");
            push(@in_files,$def_file2);
            #push(@in_files,$def_file3);
        }
        $load_log = 1;
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

sub get_threejs_json_parse() {
    my $txt = <<EOF;
from : https://github.com/mrdoob/three.js/blob/master/src/loaders/JSONLoader.js
THREE.JSONLoader.prototype.parse = function ( json, texturePath ) {
	var scope = this,
	geometry = new THREE.Geometry(),
	scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0;
	parseModel( scale );
	parseSkin();
	parseMorphing( scale );
	geometry.computeCentroids();
	geometry.computeFaceNormals();
	function parseModel( scale ) {
		function isBitSet( value, position ) {
			return value & ( 1 << position );
		}
		var i, j, fi,
		offset, zLength, nVertices,
		colorIndex, normalIndex, uvIndex, materialIndex,
		type,
		isQuad,
		hasMaterial,
		hasFaceUv, hasFaceVertexUv,
		hasFaceNormal, hasFaceVertexNormal,
		hasFaceColor, hasFaceVertexColor,
		vertex, face, color, normal,
		uvLayer, uvs, u, v,
		faces = json.faces,
		vertices = json.vertices,
		normals = json.normals,
		colors = json.colors,
		nUvLayers = 0;
		// disregard empty arrays
		for ( i = 0; i < json.uvs.length; i++ ) {
			if ( json.uvs[ i ].length ) nUvLayers ++;
		}
		for ( i = 0; i < nUvLayers; i++ ) {
			geometry.faceUvs[ i ] = [];
			geometry.faceVertexUvs[ i ] = [];
		}
		offset = 0;
		zLength = vertices.length;
		while ( offset < zLength ) {
			vertex = new THREE.Vector3();
			vertex.x = vertices[ offset ++ ] * scale;
			vertex.y = vertices[ offset ++ ] * scale;
			vertex.z = vertices[ offset ++ ] * scale;
			geometry.vertices.push( vertex );
		}
		offset = 0;
		zLength = faces.length;
		while ( offset < zLength ) {
			type = faces[ offset ++ ];
			isQuad              = isBitSet( type, 0 );
			hasMaterial         = isBitSet( type, 1 );
			hasFaceUv           = isBitSet( type, 2 );
			hasFaceVertexUv     = isBitSet( type, 3 );
			hasFaceNormal       = isBitSet( type, 4 );
			hasFaceVertexNormal = isBitSet( type, 5 );
			hasFaceColor	    = isBitSet( type, 6 );
			hasFaceVertexColor  = isBitSet( type, 7 );
			//console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);
			if ( isQuad ) {
				face = new THREE.Face4();
				face.a = faces[ offset ++ ];
				face.b = faces[ offset ++ ];
				face.c = faces[ offset ++ ];
				face.d = faces[ offset ++ ];
				nVertices = 4;
			} else {
				face = new THREE.Face3();
				face.a = faces[ offset ++ ];
				face.b = faces[ offset ++ ];
				face.c = faces[ offset ++ ];
				nVertices = 3;
			}
			if ( hasMaterial ) {
				materialIndex = faces[ offset ++ ];
				face.materialIndex = materialIndex;
			}
			// to get face <=> uv index correspondence
			fi = geometry.faces.length;
			if ( hasFaceUv ) {
				for ( i = 0; i < nUvLayers; i++ ) {
					uvLayer = json.uvs[ i ];
					uvIndex = faces[ offset ++ ];
					u = uvLayer[ uvIndex * 2 ];
					v = uvLayer[ uvIndex * 2 + 1 ];
					geometry.faceUvs[ i ][ fi ] = new THREE.Vector2( u, v );
				}
			}
			if ( hasFaceVertexUv ) {
				for ( i = 0; i < nUvLayers; i++ ) {
					uvLayer = json.uvs[ i ];
					uvs = [];
					for ( j = 0; j < nVertices; j ++ ) {
						uvIndex = faces[ offset ++ ];
						u = uvLayer[ uvIndex * 2 ];
						v = uvLayer[ uvIndex * 2 + 1 ];
						uvs[ j ] = new THREE.Vector2( u, v );
					}
					geometry.faceVertexUvs[ i ][ fi ] = uvs;
				}
			}
			if ( hasFaceNormal ) {
				normalIndex = faces[ offset ++ ] * 3;
				normal = new THREE.Vector3();
				normal.x = normals[ normalIndex ++ ];
				normal.y = normals[ normalIndex ++ ];
				normal.z = normals[ normalIndex ];
				face.normal = normal;
			}
			if ( hasFaceVertexNormal ) {
				for ( i = 0; i < nVertices; i++ ) {
					normalIndex = faces[ offset ++ ] * 3;
					normal = new THREE.Vector3();
					normal.x = normals[ normalIndex ++ ];
					normal.y = normals[ normalIndex ++ ];
					normal.z = normals[ normalIndex ];
					face.vertexNormals.push( normal );
				}
			}
			if ( hasFaceColor ) {
				colorIndex = faces[ offset ++ ];
				color = new THREE.Color( colors[ colorIndex ] );
				face.color = color;
			}
			if ( hasFaceVertexColor ) {
				for ( i = 0; i < nVertices; i++ ) {
					colorIndex = faces[ offset ++ ];
					color = new THREE.Color( colors[ colorIndex ] );
					face.vertexColors.push( color );
				}
			}
			geometry.faces.push( face );
		}
	};

EOF
    return $txt;
}


# eof - template.pl
