#!/usr/bin/perl -w

# to make cu paths from an enclosure's silk,
# to restrict a polygon to the interiour of an enclosure
# like outline but in cu

use strict;
use Pcb;

#######

my %opt;
my $Distance = Pcb::dim2nm("1mm"); # a polygons closest distance to center of line/arc
my $Thickness = Pcb::dim2nm("1mm");
my $Clearance = 2*$Distance - $Thickness;
my $thickness = Pcb::nm2dim($Thickness);
my $clearance = Pcb::nm2dim($Clearance);
my $sflags = '"clearline,lock"';
my @board_size = ( -1, -1 );
my @element_mark = (-1,-1); # postion of the elements mark

my @obj;

#######

my $pi = 2 * atan2(1,0);
my $s2 = sqrt(2);

sub acos($) { # not in perl, have to add it
    my $x = shift;
    atan2( sqrt(1 - $x * $x), $x );
}

sub asin($) { # not in perl, have to add it
    my $x = shift;
    atan2(  $x, sqrt(1 - $x * $x) );
}

sub tan($) { # not in perl, have to add it
    my $x = shift;
    sin($x) / cos($x);
}

sub rad_deg($) { # conversion from radians to degrees (360° = one full turn)
    my $x = shift;
    180 * $x / $pi;
}

sub deg_rad($) { # conversino from degrees to radians
    my $x = shift;
    $x * $pi / 180;
}
sub Acos($) {
    my $x = shift;
    rad_deg(acos($x));
}
sub Asin($) {
    my $x = shift;
    rad_deg(asin($x));
}
sub Sin($) {
    my $x = shift;
    sin(deg_rad($x));
}
sub Cos($) {
    my $x = shift;
    cos(deg_rad($x));
}
sub Tan($) {
    my $x = shift;
    tan(deg_rad($x));
}
sub rad_normalize1($) { # normalize angle, -$pi < angle <= $pi
    my $da = shift;
    while ($da >   $pi) { $da -= 2*$pi; }
    while ($da <= -$pi) { $da += 2*$pi; }
    $da;
}

sub rad_normalize2($) { # normalize angle, 0 <= angle < 2*$pi
    my $da = shift;
    while ($da >= 2*$pi) { $da -= 2*$pi; }
    while ($da <  0    ) { $da += 2*$pi; }
    $da;
}

sub dbl_eq($$) {
    my $a = shift;
    my $b = shift;

    my $diff = ($a - $b) * 1000;
    if ($diff < 0) { $diff = -$diff; }
    int($diff + 0.5);
}

#######

sub Usage() {
    print <<EOT;
Usage:
    silk2line.pl <arg> ...
Where <arg> is of the form "key=value" or the file to process, keys:
    refdes: is the element name or refdes
    w: use only arcs and lines with this width
EOT

    exit;
}

sub init() {
    $opt{refdes} = "M1";
    $opt{w} = Pcb::dim2nm("0.32mm");
}

sub dimpcb($) {
    my $dim = shift;
    Pcb::nm2dim($dim);
}
sub pcbdim($) {
    my $str = shift;
    Pcb::dim2nm($str);
}

sub check_option($) {
    my $str = shift;

    if ($str =~ m/^([^=]+)(=(.*))/) {
	my $k = $1;
	my $v = $3;
	$opt{$k} = $v;
	return 1;
    } else {
	return 0;
    }
}

sub opt_show() {
    for my $k (sort keys %opt) {
	my $v = $opt{$k};
	print "$k=$v\n";
    }
}

sub proc_line(@) {
    my @fld = @_;
    #ElementLine [X1 Y1 X2 Y2 Thickness]
    #Line        [X1 Y1 X2 Y2 Thickness Clearance SFlags]

    if (@fld != 5) { return undef; }
    my @out = ($fld[0], $fld[1], $fld[2], $fld[3], $thickness, $clearance, $sflags);

    my @line = map { pcbdim($_) } @fld[0..3];
    my $pos = [ sprintf("%.3f %.3f", $line[0], $line[1]), sprintf("%.3f %.3f", $line[2], $line[3]) ];
    push @obj, [ $pos, [ $line[0], $line[1] ], [ $line[2], $line[3] ], 'line' ];

    $out[0] = dimpcb($element_mark[0] + $line[0]);
    $out[1] = dimpcb($element_mark[1] + $line[1]);
    $out[2] = dimpcb($element_mark[0] + $line[2]);
    $out[3] = dimpcb($element_mark[1] + $line[3]);
    my $str = "\tLine[" . join(" ", @out) . "]\n";

    $str;
}

sub arc_endpoints($$$) {
    my $center = shift;
    my $radius = shift;
    my $angle  = shift;

    my $sta = [0,0];
    my $end = [0,0];
    my $angle2 = $$angle[0] + $$angle[1];

    $sta = [ - $$radius[0] * Cos($$angle[0]) + $$center[0], $$radius[1] * Sin($$angle[0]) + $$center[1] ];
    $end = [ - $$radius[0] * Cos($angle2   ) + $$center[0], $$radius[1] * Sin($angle2   ) + $$center[1] ];

    ( $sta, $end );
}

sub proc_arc(@) {
    my @fld = @_;
    #ElementArc [X Y Width Height                     StartAngle DeltaAngle Thickness]
    #Arc        [X Y Width Height Thickness Clearance StartAngle DeltaAngle SFlags]

    if (@fld != 7) { return undef; }

    my @arc = map { pcbdim($_) } @fld[0..3];
    my $center = [ $arc[0], $arc[1] ];
    my $radius = [ $arc[2], $arc[3] ];
    my $angle  = [ $fld[4], $fld[5] ];
    my ($sta, $end) = arc_endpoints($center, $radius, $angle);
    my $pos = [ sprintf("%.3f %.3f", $$sta[0], $$sta[1]), sprintf("%.3f %.3f", $$end[0], $$end[1]) ];
    push @obj, [ $pos, $sta, $end, 'arc', $center, $radius, $angle ];

    my @out = (@fld[0..3], $thickness, $clearance, $fld[4], $fld[5], $sflags);

    $out[0] = dimpcb($element_mark[0] + $arc[0]);
    $out[1] = dimpcb($element_mark[1] + $arc[1]);
    my $str = "\tArc[" . join(" ", @out) . "]\n";

    $str;
}

sub  proc_obj() {
    print "\n";
    my %point;

    for my $r (@obj) {
	my @arr = @$r;
	my ($pos, $sta, $end, $type, @rest) = @arr;

	#if (!defined($point{$$pos[0]})) { $point{$$pos[0]} = []; }
	push @{$point{$$pos[0]}}, $r;
	push @{$point{$$pos[1]}}, $r;
	#print "$type, $$pos[0], $$pos[1], $$sta[0], $$sta[1], $$end[0], $$end[1]";

	if ($type eq "line") {
	    #print "\n";
	} elsif ($type eq "arc") {
	    my ($center, $radius, $angle ) = @rest;
	    #print ", $$center[0], $$center[1], $$radius[0], $$radius[1], $$angle[0], $$angle[1]\n";
	} else {
	    warn(", strgange obj");
	}
    }

    my %dbl;
    my %sng;
    my %otr;
    for my $k (keys %point) {
	my @v = @{$point{$k}};
	my $v = $point{$k};

	if (@v+0 == 1) {
	    $sng{$k} = $v;
	} elsif (@v+0 == 2) {
	    $dbl{$k} = $v;
	} else {
	    $otr{$k} = $v;
	}
	#printf "%s %d\n", $k, @v+0;
    }

    print " number of end   nodes: ", keys(%sng) +0, "\n";
    print " number of chain nodes: ", keys(%dbl) +0, "\n";
    print " number of other nodes: ", keys(%otr) +0, "\n";
    print "\n";

    for my $k (sort keys %dbl) {
	my $v = $dbl{$k};
	print "$k: $$v[0][1][0], $$v[1][1][1]\n";
    }

    print "\n";
    my @list = sort keys %dbl;
    my @polygon;
    if (@list > 1) {
	# choose an initial node and a segment going from it
	my $sta_node = $list[0];
	my $sta_seg  = ${$dbl{$sta_node}}[0];

	my $nxt_node = $sta_node;
	my $nxt_seg  = $sta_seg;
	push @polygon, [ $nxt_node, $nxt_seg ];

	for (my $ix = 0; $ix < @list; $ix++) {
	    my $old_node = $nxt_node;
	    my $old_seg  = $nxt_seg;
	    my ($pos, $sta, $end, $type, @rest) = @$old_seg;

	    $nxt_node = $$pos[0];
	    if ($nxt_node eq $old_node) { $nxt_node = $$pos[1]; }

	    $nxt_seg = ${$dbl{$nxt_node}}[0];
	    if ($nxt_seg == $old_seg) { $nxt_seg = ${$dbl{$nxt_node}}[1]; }

	    push @polygon, [ $nxt_node, $nxt_seg ];
	    printf "sta: %15s // %15s %15s // %s\n", $old_node, $$pos[0], $$pos[1], $nxt_node;
	}
    }

    for (my $ix = 0; $ix < @polygon; $ix++) {
	# TODO
    }

}

sub proc_file($$) {
    my $fh = shift;
    my $file = shift;

    my $refdes = $opt{refdes};
    my $width = Pcb::dim2nm($opt{w});

    print "$file:\n";

    while(<$fh>) {
	if (m/^PCB\s*\[\s*"([^"])*"\s+([^]]+)\s+([^]]+)\s*\]/) {
	    my $name = $1 // "";
	    my $cx = pcbdim($2);
	    my $cy = pcbdim($3);
	    #print "\"$name\" $cx $cy\n";
	    @board_size = ($cx, $cy);
	} elsif (m/^Element/) {
	    my @fld = split('"');
	    #print "<", join("> <", @fld), ">\n";
	    if ($fld[5] eq $refdes) {
		my $str = $fld[8];
		$str =~ tr/[ \t]/ /s;
		$str =~ s/^ //;
		@fld = split(/ /, $str);
		@element_mark = (pcbdim($fld[0]), pcbdim($fld[1]));
		#print "$str\n";
		last;
	    }
	}
    }
    { my $str = <$fh>; } # shuold be a "("
    while(<$fh>) {
	chomp;
	#print "$_\n";
	if (m/^[ \t]*\)/) {
	    last;
	}
	if (m/Element(Line|Arc)\s+\[(.*)\]/) {
	    my $type = $1;
	    my $body = $2;
	    $body =~ tr/[ \t]/ /s;
	    $body =~ s/^ //;
	    my @fld = split(/ /, $body);
	    my $w = pcbdim($fld[$#fld]);
	    next unless ($width == $w);

	    #my $str = "\tElement$type \[" . join(" ", @fld) . "\]";
	    #if ($str ne $_) { warn("mismatch"); }
	    #print "$_\n";
	    #print "$str\n";

	    my $str = "";
	    if ($type eq "Line") {
		$str = proc_line(@fld);
	    } else {
		$str = proc_arc(@fld);
	    }
	    print $str;
	}
    }
    #proc_obj();

}

sub main() {
    if (@ARGV < 1) { Usage(); }
    init();
    #opt_show();

    for my $str (@ARGV) {
	if (check_option($str)) {
	} else {
	    my $fh;
	    if (open($fh, $str)) {
		proc_file($fh, $str);
		close($fh);
	    } else {
		warn("cannot open $str");
	    }

	}
    }
}

#######

main();

__END__
