package Waypoint;

use strict;
use warnings;
use include;

use Exporter;
our @ISA = ("Exporter");
our @EXPORT = ("preprock", "euclid", "offset");

sub preprock {
  my $map = shift;
  magnetize($map);
  voronoi($map);
}

sub euclid {
  my ($y1, $x1, $y2, $x2) = @_;
  return sqrt((($y1 - $y2) ** 2) + (($x1 - $x2) ** 2));
}

sub offset {
  my $magnet = shift;
  return ($magnet->[0]{Y} + $magnet->[1], $magnet->[0]{X} + $magnet->[2]);
}

sub magnetize {
  my $map = shift;
  my @mags;
  my $sym = "a";
  foreach my $y (1 .. $map->height - 1) {
    foreach my $x (1 .. $map->width - 1) {
      my $tile = $map->{Map}[$y][$x]{_};
      next unless $tile eq "#";

      if ($map->{Map}[$y][$x-1]{_} eq "." and $map->{Map}[$y][$x+1]{_} eq "#") {
        push @mags, { At => [$y, $x - 1], To => [] };
      }

      if ($map->{Map}[$y][$x+1]{_} eq "." and $map->{Map}[$y][$x-1]{_} eq "#") {
        push @mags, { At => [$y, $x + 1], To => [] };
      }

      if ($map->{Map}[$y-1][$x]{_} eq "." and $map->{Map}[$y+1][$x]{_} eq "#") {
        push @mags, { At => [$y - 1, $x], To => [] };
      }

      if ($map->{Map}[$y+1][$x]{_} eq "." and $map->{Map}[$y-1][$x]{_} eq "#") {
        push @mags, { At => [$y + 1, $x], To => [] };
      }
    }
  }

  foreach my $id (0 .. $#mags) {
    my $mag = $mags[$id];
    # See which other magnets $mag is connectable to.
    foreach my $ID (0 .. $#mags) {
      next if $ID == $id;
      next if scalar(grep /^$ID$/, @{ $mag->{To} }); # Already connected.
      my $MAG = $mags[$ID];
      next unless trace($map, @{ $mag->{At} }, @{ $MAG->{At} });
      push @{ $mag->{To} }, $ID;
      push @{ $MAG->{To} }, $id;
    }
  }

  $map->{Magnets} = \@mags;
}

sub voronoi {
  my $map = shift;
  my @colors = ("red", "green", "Blue", "Red", "Green", "yellow", "purple",
                "cyan", "blue", "Cyan");
  foreach my $y (1 .. $map->height - 1) {
    foreach my $x (1 .. $map->width - 1) {
      next unless $map->{Map}[$y][$x]{_} eq ".";
      # Which wallmagnet are we closest to?
      my $best = [];
      my @ls;
      foreach my $magid (0 .. $#{ $map->{Magnets} }) {
        my $mag = $map->{Magnets}[$magid]{At};
        my $dist = euclid(@$mag, $y, $x);
        push @ls, [$dist, $magid];
      }
      my @newls = sort { $a->[0] <=> $b->[0] } @ls;
      foreach my $id (@newls) {
        my $magnet = $map->{Magnets}[ $id->[1] ]{At};
        # Is this magnet reachable from node ($y, $x)?
        next unless trace($map, $y, $x, @$magnet);
        $map->{Map}[$y][$x]{Ref} = $id->[1];
        $map->mod($y, $x, { color => $colors[ $id->[1] ] });
        last;
      }
    }
  }
}

sub trace {
  my ($map, $y, $x, $y2, $x2) = @_;
  1;
  while (1) {
    my $stuck = 0;
    if ($y > $y2 and $map->{Map}[$y-1][$x]{_} eq ".") {
      $y--;
    } elsif ($y < $y2 and $map->{Map}[$y+1][$x]{_} eq ".") {
      $y++;
    } else {
      $stuck++;
    }
    if ($x > $x2 and $map->{Map}[$y][$x-1]{_} eq ".") {
      $x--;
    } elsif ($x < $x2 and $map->{Map}[$y][$x+1]{_} eq ".") {
      $x++;
    } else {
      $stuck++;
    }
    return 1 if $y == $y2 and $x == $x2;
    return if $stuck == 2;
  }
}

42;
