#!/usr/bin/perl

#  Copyright (C) 2012 Brian Standley <brian@brianstandley.com>
#
#  This program is free software: you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation, either version 3 of the License, or (at your
#  option) any later version.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
#  Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program. If not, see <http://www.gnu.org/licenses/>.

# npgsfixer v1.1

# Read filenames (required) and args (optional).

$infilename   = shift @ARGV;
$mainfilename = shift @ARGV;
$fieldname    = "field";        # default
$runfilename  = "runfile.txt";  # default

while (@ARGV > 0)
{
	$arg = shift @ARGV;
	if    ($arg =~ "--fieldbase" && @ARGV > 0) { $fieldname   = shift @ARGV; }
	elsif ($arg =~ "--runfile"   && @ARGV > 0) { $runfilename = shift @ARGV; }
}

# Print usage.

if ($infilename eq "--help" || $infilename eq "-h")
{
	print "\nUsage: npgsfixer [input file] [output file] [arguments]\n\n";
	print "  Arguments:  --fieldbase [file]  Specify base filename of subfields, if applicable. Default: \"field\"\n";
	print "              --runfile   [file]  Specify filename of runfile snippet, if applicable. Default: \"runfile.txt\"\n\n";
	exit 0;
}

# Set up file IO.

print "\nConverting input file to UNIX format:\n";
system "dos2unix --verbose --d2u $infilename";

sub die2
{
	printf "\nConverting input file back to DOS format:\n";
	system "dos2unix --verbose --u2d $infilename";
	$msg = shift @_;
	die $msg;
}

!($infilename =~ $mainfilename) || die2("Input and output files must be different.\n");
open(INFILE,   "< $infilename") || die2("Cannot open file: $infilename.\n");

##########################
######  FIRST PASS  ######
##########################

print "\nFIRST PASS:\n";

# Save header and determine layer info.

$header = "";
while (my $line = <INFILE>)
{
	if ($line =~ /23 (.+) 0 0 0 0/)
	{
		for (my $L = 0; $L < $1; $L++)
		{
			$chop[$L] = 0;         # default is "no" unless we find a bounding box
			$name[$L] = <INFILE>;  # save layer name for second pass
			chomp($name[$L]);
		}
		last;  # layers are the final section of the DC2 header, so we're done
	}
	$header = $header . $line;  # save rest of header for second pass
}

@YELLOW      = (255, 255,   0);  # QCAD: (255, 255,   0)
@BLUE        = (  0,   0, 255);  # QCAD: (  0,   0, 255)
@GREEN       = (  0, 255,   0);  # QCAD: (  0, 255,   0)
@LIGHT_BLUE  = (128, 191, 255);  # QCAD: (175, 175, 255)
@LIGHT_GREEN = (191, 255, 128);  # QCAD: (175, 255, 175)
@DARK_BLUE   = (  0,   0, 166);  # QCAD: (  0,   0, 150)
@DARK_GREEN  = (  0, 166,   0);  # QCAD: (  0, 150,   0)

sub MatchColor
{
	my ($c1, $c2) = @_;
	return ($$c1[0] == $$c2[0] && $$c1[1] == $$c2[1] && $$c1[2] == $$c2[2]);
}

# Search for bounding boxes.

$N_field = 0;  # save number of fields to "chop" for second pass
while (my $line = <INFILE>)
{
	if ($line =~ /21 (.+) 0 0 0 0/)  # start of a layer
	{
		$L = $1;
		print "Scanning layer $L \"$name[$L]\":\n";
	}
	elsif ($line =~ /1 (.+) [68] .+ 0 (.+) (.+) (.+) 10 1/)  # unfixed polyline
	{
		my @color = ($2, $3, $4);
		if (MatchColor(\@color, \@YELLOW))
		{
			my ($x0, $x1, $y0, $y1);
			for (my $i = 0; $i < $1; $i++)
			{
				my ($x, $y, $z) = split(' ', <INFILE>);
				
				if ($i == 0 || $x < $x0) { $x0 = $x; }
				if ($i == 0 || $x > $x1) { $x1 = $x; }
				if ($i == 0 || $y < $y0) { $y0 = $y; }
				if ($i == 0 || $y > $y1) { $y1 = $y; }
			}

			$N_field++;
			$chop[$L] = 1;
			$xc[$L] = ($x1 + $x0) / 2.0;
			$yc[$L] = ($y1 + $y0) / 2.0;

			printf "  Found bounding box. Vertices: %d, Center: (%1.1f, %1.1f)\n", $1 - 1, $xc[$L] / 8.0, $yc[$L] / 8.0;
		}
	}
}

##################################
######  INTERMEDIATE STEPS  ######
##################################

# Calculate stage offsets, write runfile snippet.

if ($N_field > 0)
{
	open(RUNFILE, "> $runfilename") || die2("Cannot open file: $runfilename.\n");

	my ($xs, $yx) = (0.0, 0.0);
	for (my $L = 0; $L < @name; $L++)
	{
		if ($chop[$L])
		{
			printf RUNFILE "$fieldname-$name[$L]\n1\n%d,%d\n", ($xc[$L] - $xs) / 8.0, ($yc[$L] - $ys) / 8.0;
			$xs = $xc[$L];
			$ys = $yc[$L];
		}
	}
	printf RUNFILE "MoveOnly M\n%d,%d\n", $xs / -8.0, $ys / -8.0;

	close(RUNFILE);
	system "dos2unix --u2d $runfilename";
}

# Prepare main output file.

open(MAINFILE, "> $mainfilename") || die2("Cannot open file: $mainfilename.\n");

print  MAINFILE $header;
printf MAINFILE "23 %d 0 0 0 0\n", @name - $N_field;
for (my $L = 0; $L < @name; $L++)
{
	if ($chop[$L] == 0) { print MAINFILE "$name[$L]\n"; }  # skip chopped layers
}

###########################
######  SECOND PASS  ######
###########################

printf "SECOND PASS:\n";

seek(INFILE, 0, 0);  # set file pointer back to start

# Fix origin, separate layers, write files.

$vertex_mode = 0;
$chopped_so_far = 0;
while (my $line = <INFILE>)
{
	if ($line =~ /21 (.+) 0 0 0 0/)  # start of layer
	{
		$L = $1;
		print "Processing layer $L \"$name[$L]\":\n";

		if ($chop[$L])
		{
			if ($chopped_so_far > 0)  # finish off previous chopfile
			{
				close(CHOPFILE);
				system "dos2unix --u2d $chop_filename";
			}

			$chop_filename = "$fieldname-$name[$L].DC2\n";
			open(CHOPFILE, "> $chop_filename") || die2("Cannot open file: $chop_filename.\n");

			print CHOPFILE $header;  # steal (copy) header from input file
			print CHOPFILE "23 2 0 0 0 0\n";
			print CHOPFILE "$name[0]\n";
			print CHOPFILE "$name[$L]\n";
			print CHOPFILE "21 1 0 0 0 0\n";

			$chopped_so_far++;
		}
		else { printf MAINFILE "21 %d 0 0 0 0\n", $L - $chopped_so_far; }

		$vertex_mode = 0;
	}
	elsif ($line =~ /1 (.+) [68] .+ 0 (.+) (.+) (.+) 10 1/)
	{
		my @color = ($2, $3, $4);
		if (MatchColor(\@color, \@YELLOW))
		{
			$vertex_mode = 0;
			print "  Deleted bounding box.\n";
		}
		elsif (MatchColor(\@color, \@BLUE)        ||
		       MatchColor(\@color, \@GREEN)       ||
		       MatchColor(\@color, \@LIGHT_BLUE)  ||
		       MatchColor(\@color, \@LIGHT_GREEN) ||
		       MatchColor(\@color, \@DARK_BLUE)   ||
		       MatchColor(\@color, \@DARK_GREEN))
		{
			my $fixed = "1 $1 16 0 1 12 0 0 0 $2 $3 $4 10 1\n";
			if ($chop[$L]) { printf CHOPFILE $fixed; }
			else           { printf MAINFILE $fixed; }

			$vertex_mode = 1;
			printf "  Fixed filled polyline. Vertices: %d Color: ($2,$3,$4)\n", $1 - 1;
		}
		else
		{
			if ($chop[$L]) { printf CHOPFILE $line; }
			else           { printf MAINFILE $line; }

			$vertex_mode = 1;
			printf "  Skipped nonfilled polyline. Vertices: %d Color: ($2,$3,$4)\n", $1 - 1;
		}
	}
	elsif ($line =~ /1 (.+) 16 0 1 12 0 0 0 (.+) (.+) (.+) 10 1/)
	{
		if ($chop[$L]) { printf CHOPFILE $line; }
		else           { printf MAINFILE $line; }

		$vertex_mode = 1;
		printf "  Skipped filled polyline. Vertices: %d Color: ($2,$3,$4)\n", $1 - 1;
	}
	elsif ($vertex_mode)
	{
		if ($chop[$L])
		{
			my ($x, $y, $z) = split(' ', $line);
			printf CHOPFILE "%d %d 0\n", $x - $xc[$L], $y - $yc[$L];
		}
		else { print MAINFILE $line; }
	}
}

close(INFILE);
close(MAINFILE);
system "dos2unix --u2d $mainfilename";

if ($chopped_so_far > 0)
{
	close(CHOPFILE);
	system "dos2unix --u2d $chop_filename";
}

printf "\nConverting input file back to DOS format:\n";
system "dos2unix --verbose --u2d $infilename";

exit 0;
