#!/usr/bin/perl -w

# Copyright 2020 Kevin Ryde
#
# This file is part of Graph-Maker-Other.
#
# Graph-Maker-Other 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, or (at your option) any later
# version.
#
# Graph-Maker-Other 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 Graph-Maker-Other.  If not, see <http://www.gnu.org/licenses/>.

use 5.005;
use strict;
use FindBin;
use File::Slurp;
use File::Spec;
use List::Util 'min','max';
use Math::Geometry::Planar 'Colinear';
$|=1;

my $libdir;
BEGIN { $libdir = File::Spec->catdir($FindBin::Bin, File::Spec->updir, 'lib'); }
use lib $libdir;
use Graph::Maker::MostMaximumMatchingsTree;

use lib File::Spec->curdir;
use MyPrintwrap ':all';

# uncomment this to run the ### lines
# use Smart::Comments;

my $parts_type = 'table';
if (@ARGV) { $parts_type = shift @ARGV; }

print "% generated by $FindBin::Script $parts_type\n";


#------------------------------------------------------------------------------
# paths of points

sub point_str {
  my ($p) = @_;
  return $p->[0].','.$p->[1];
}
sub path_str {
  my ($path) = @_;
  return join(' ', map {point_str($_)} @$path);
}
sub point_equal {
  my ($p,$q) = @_;
  return $p->[0] == $q->[0] && $p->[1] == $q->[1];
}
sub path_pair_join_maybe {
  my ($p_path, $q_path, $colinear) = @_;
  ### path_pair_join_maybe() ...
  ### $colinear
  ### $p_path
  ### $q_path
  if (point_equal($p_path->[0],$q_path->[0])
      || point_equal($p_path->[-1],$q_path->[-1])) {
    ### reverse p ...
    $p_path = [reverse @$p_path];
    ### $p_path
  }
  if (point_equal($q_path->[-1],$p_path->[0])) {  # q_end == p_start
    ($p_path,$q_path) = ($q_path,$p_path);
  }
  if (point_equal($p_path->[-1],$q_path->[0])) {  # p_end == q_start
    ### p end to q start ...
    if ($colinear) {
      (@$p_path==2 && @$q_path==2) or die;
      ### colinear p: $p_path
      ### $q_path
      if (Colinear([$p_path->[0],@$q_path])) {
        ### yes ...
        return [ $p_path->[0], $q_path->[-1] ];
      }
    } else {
      ### yes ...
      return [ @$p_path, @{$q_path}[1..$#$q_path] ];
    }
  }
  return undef;
}
sub paths_join {
  my ($paths, $colinear) = @_;
  ### paths_join() ...
  ### $colinear

  my $more;
  do {
    $more = 0;
    foreach my $i (0 .. $#$paths) {
      for (my $j = $i+1; $j <= $#$paths; $j++) {
        if (defined(my $new = path_pair_join_maybe($paths->[$i],
                                                   $paths->[$j],
                                                   $colinear))) {
          $paths->[$i] = $new;
          splice @$paths,$j,1;
          $j--;
          $more = 1;
        }
      }
    }
  } while ($more);
  return $paths;
}

#------------------------------------------------------------------------------
# Samples

sub Graph_vertex_xy {
  my ($graph,$v) = @_;
  return ($graph->get_vertex_attribute($v,'x'),
          $graph->get_vertex_attribute($v,'y'));
}
sub Graph_bbox {
  my ($graph) = @_;
  my @vertices = $graph->vertices;
  my @x = map {$graph->get_vertex_attribute($_,'x')} @vertices;
  my @y = map {$graph->get_vertex_attribute($_,'y')} @vertices;
  unless (@x) { push @x, 0; }
  unless (@y) { push @y, 0; }
  return (min(@x),min(@y), max(@x),max(@y));
}
sub truncate_decimals {
  my ($x) = @_;
  $x =~ s/(\.\d{6}).*/$1/;
  return $x;
}

# \newcommand\MyScaleZero{.3}
# \newcommand\MyScaleOne{.2}
# \newcommand\MyScaleTwo{.15}
# \newcommand\MyScaleThree{.13}
# \newcommand\MyScaleFour{.13}
# \newcommand\MyScaleFive{.13}
# \newcommand\MyScaleSix{.1}
# \newcommand\MyScaleSeven{.1}
# \newcommand\MyScaleEight{.09}
# \newcommand\MyScaleNine{.09}
# \newcommand\MyScaleTen{.09}
# \newcommand\MyScaleEleven{.07}
# \newcommand\MyScaleTwelve{.07}
# \newcommand\MyScaleThirteen{.07}
# \newcommand\MyScaleFourteen{.07}
# \newcommand\MyScaleFifteen{.065}
# Return list ($scale, $radius).
# Note radius is after scale applied.
sub n_to_sizes {
  my ($n) = @_;
  if ($n < 1*7)  { return (.29, .16); }
  if ($n < 2*7)  { return (.2,  .22); }
  if ($n < 3*7)  { return (.15, .25); }
  if ($n < 6*7)  { return (.13, .26); }
  if ($n < 8*7)  { return (.1,  .27); }
  if ($n < 11*7) { return (.09, .29); }
  if ($n < 14*7) { return (.07,  0); }
  if ($n < 20*7) { return (.065, 0); }
  return (.2, .2);
}

my $total_y_min = 0;

sub one {
  my ($n, %options) = @_;
  my $graph = Graph::Maker->new ('most_maximum_matchings_tree',
                                 N => $n,
                                 coordinate_type => 'HW',
                                 undirected=>1);
  my ($x_min,$y_min, $x_max,$y_max) = Graph_bbox($graph);
  my $x_mid = ($x_min + $x_max) / 2;
  my $y_mid = ($y_min + $y_max) / 2;
  if ($n <= 6) {  # stars
    $x_mid = 0;
    $y_mid = 0;
  }

  my $transform = sub {
    my ($x,$y) = @_;
    $x -= $x_mid;
    $y -= $y_mid;
    return (truncate_decimals($x), truncate_decimals($y));
  };
  {
    my ($tx,$ty) = $transform->(0,$y_min);
    $total_y_min = min($total_y_min, $ty);
  }
  if ($options{'nooutput'}) { return; }

  my @vertices = sort {$a<=>$b} $graph->vertices;
  my $paths = [];
  foreach my $edge ($graph->edges) {
    my ($v1,$v2) = @$edge;
    my ($x1,$y1) = $transform->(Graph_vertex_xy($graph,$v1));
    my ($x2,$y2) = $transform->(Graph_vertex_xy($graph,$v2));
    push @$paths, [ [$x1,$y1], [$x2,$y2] ];
  }
  @$paths = sort {path_str($a) cmp path_str($b)} @$paths;

  $paths = paths_join($paths,1);
  $paths = paths_join($paths);

  $Printwrap = 2;
  printwrap(" \\draw");
  $Printwrap += 2;
  foreach my $path (@$paths) {
    my $join = ' ';
    foreach my $p (@$path) {
      my ($x,$y) = @$p;
      printwrap("$join($x,$y)");
      $join = '--';
    }
  }
  printwrap(";\n");

  my ($scale,$radius) = n_to_sizes($n);
  ### $radius

  $Printwrap = 2;
  if (@vertices == 0) {
    printwrap(" \\node at (0,0) [font=\\tiny] {(empty)};\n");
  } else {
    if ($radius) {
      printwrap(" \\fill foreach \\p in {");
      $Printwrap += 2;
      my $join = '';
      foreach my $v (@vertices) {
        my ($x,$y) = $transform->(Graph_vertex_xy($graph,$v));
        printwrap("$join($x,$y)");
        $join = ',';
      }
      printwrap("} {\\p circle ($radius)};\n");
    }
  }
}

sub row {
  my ($rownum) = @_;

  my $n_row_lo = 7*$rownum;
  my $n_row_hi = $n_row_lo + 6;
  print "% n=$n_row_lo to $n_row_hi\n";
  print "\\noindent\n";
  print "\\begin{tikzpicture}\n";
  # print "  \\node at (0,0) {}; % bbox starting at 0,0\n";
  print "  \\path (0,0); % bbox include 0,0\n";

  $total_y_min = 0;
  foreach my $colnum (0 .. 6) {
    my $n = 7*$rownum + $colnum;
    one($n, nooutput=>1);
  }
  $total_y_min = truncate_decimals($total_y_min);

  my ($scale,$radius) = n_to_sizes($n_row_lo);
  foreach my $colnum (0 .. 6) {
    my $n = 7*$rownum + $colnum;
    my $colmid = $colnum + .5;
    if ($colnum==1) { $colmid += .05; }
    print "  \\begin{scope}[shift={($colmid*\\MyColumn,0)},scale=$scale]\n";
    one($n);
    # print "  \\node at (0,$total_y_min) [below] {\$n{=}$n\$};\n";
    print "  \\end{scope}\n";
  }
  print "\\end{tikzpicture}\n";
}

# one(4);
# row(0);
# exit;

if ($parts_type eq 'table') {
  my $rownum_hi = 15;
  foreach my $rownum (0 .. $rownum_hi) {
    row($rownum);
    if ($rownum != $rownum_hi) {
      print "\n";
      print "\\MyVspace\n";
      print "\n";
    }
  }
} elsif ($parts_type eq '177') {
  one(177);
} elsif ($parts_type eq '181') {
  one(181);
}

#------------------------------------------------------------------------------
print "% end generated\n";
exit 0;
