/*******************************************************************************
* Author    :  Angus Johnson                                                   *
* Date      :  1 January 2023                                                  *
* Website   :  https://www.angusj.com                                          *
* Copyright :  Angus Johnson 2010-2023                                         *
* License   :  https://www.boost.org/LICENSE_1_0.txt                           *
*******************************************************************************/

using System.Reflection;
using System.IO;
using Clipper2Lib;
using System;

namespace ClipperDemo1
{
  public static class Application
  {
    public static void Main()
    {
      //ClipTestPolys();

      //LoopThruTestPolygons();             // summary test all
      //LoopThruTestPolygons(110, 113);     // display test range
      //LoopThruTestPolygons(46);           // display one

      SquaresTest(true);
      TrianglesTest(true);
      DiamondsTest(true);
    }

    public static Paths64 Polytree_Union(Paths64 subjects, FillRule fillrule)
    {
      // of course this function is inefficient, 
      // but it's purpose is simply to test polytrees.
      PolyTree64 polytree = new();
      Clipper.BooleanOp(ClipType.Union, subjects, null, polytree, fillrule);
      return Clipper.PolyTreeToPaths64(polytree);
    }

    public static void SquaresTest(bool test_polytree = false)
    {
      const int size = 10;
      const int w = 800, h = 600;
      const FillRule fillrule = FillRule.NonZero;

      Path64 shape = Clipper.MakePath(new int[] { 0, 0, size, 0, size, size, 0, size });
      Paths64 subjects = new(), solution;
      Random rand = new();
      for (int i = 0; i < h / size; ++i)
      {
        for (int j = 0; j < w / size; ++j)
        {
          shape = Clipper.TranslatePath(shape, size, 0);
          if (rand.Next(5) != 0) subjects.Add(shape);
        }
        shape = Clipper.TranslatePath(shape, (-w / size) * size, size);
      }

      if (test_polytree)
        solution = Polytree_Union(subjects, fillrule);
      else
        solution = Clipper.Union(subjects, fillrule);

      SvgWriter svg = new();
      SvgUtils.AddSubject(svg, subjects);
      SvgUtils.AddSolution(svg, solution, false);
      const string filename = @"..\..\..\squares.svg";
      SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10);
      ClipperFileIO.OpenFileWithDefaultApp(filename);
    }

    public static void TrianglesTest(bool test_polytree = false)
    {
      const int size = 10;
      const int w = 800, h = 600;
      const FillRule fillrule = FillRule.NonZero;

      Path64 tri1 = Clipper.MakePath(new int[] { 0,0, size * 2,0, size,size * 2 });
      Path64 tri2 = Clipper.MakePath(new int[] { size * 2, 0, size, size * 2, size*3, size*2 });

      Paths64 subjects = new(), solution;
      Random rand = new();
      for (int i = 0; i < h / size / 2; ++i)
      {
        for (int j = 0; j < w / size / 2; ++j)
        {
          if (rand.Next(4) != 0) subjects.Add(tri1);
          if (rand.Next(4) != 0) subjects.Add(tri2);
          tri1 = Clipper.TranslatePath(tri1, size * 2, 0);
          tri2 = Clipper.TranslatePath(tri2, size * 2, 0);
        }
        tri1 = Clipper.TranslatePath(tri1, (-w / size) * size, size * 2);
        tri2 = Clipper.TranslatePath(tri2, (-w / size) * size, size * 2);
      }

      if (test_polytree)
        solution = Polytree_Union(subjects, fillrule);
      else
        solution = Clipper.Union(subjects, fillrule);

      SvgWriter svg = new();
      SvgUtils.AddSubject(svg, subjects);
      SvgUtils.AddSolution(svg, solution, false);
      const string filename = @"..\..\..\triangles.svg";
      SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10);
      ClipperFileIO.OpenFileWithDefaultApp(filename);
    }

    public static void DiamondsTest(bool test_polytree = false)
    {
      const int size = 10;
      const int w = 800, h = 600;
      const FillRule fillrule = FillRule.NonZero;

      Path64 shape = Clipper.MakePath(new int[] { size, 0, size * 2, size, size, size * 2, 0, size });
      Paths64 subjects = new(), solution;
      Random rand = new();
      for (int i = 0; i < h / size / 2; ++i)
      {
        for (int j = 0; j < w / size; ++j)
        {
          if (rand.Next(7) != 0) subjects.Add(shape);
          if ((j & 1) == 0)
            shape = Clipper.TranslatePath(shape, size, size);
          else
            shape = Clipper.TranslatePath(shape, size, -size);
        }
        shape = Clipper.TranslatePath(shape, (-w / size) * size, size * 2);
      }

      if (test_polytree)
        solution = Polytree_Union(subjects, fillrule);
      else
        solution = Clipper.Union(subjects, fillrule);

      SvgWriter svg = new();
      SvgUtils.AddSubject(svg, subjects);
      SvgUtils.AddSolution(svg, solution, false);
      const string filename = @"..\..\..\diamonds.svg";
      SvgUtils.SaveToFile(svg, filename, fillrule, w, h, 10);
      ClipperFileIO.OpenFileWithDefaultApp(filename);
    }

    public static void LoopThruTestPolygons(int start = 0, int end = 0)
    {
      Paths64 subject = new();
      Paths64 subject_open = new();
      Paths64 clip = new();
      Paths64 solution = new();
      Paths64 solution_open = new();
      bool do_all = (start == 0 && end == 0);
      if (do_all) { start = 1; end = 0xFFFF; }
      else if (end == 0) end = start;

      if (do_all)
        Console.WriteLine("\nCount and area differences (expected vs measured):\n");
      int test_number = start;
      for (; test_number <= end; ++test_number)
      {
        if (!ClipperFileIO.LoadTestNum(@"..\..\..\..\..\..\Tests\Polygons.txt", 
              test_number, subject, subject_open, clip, 
              out ClipType ct, out FillRule fr, out long area, out int cnt, out _)) break;
        Clipper64 c64 = new();
        c64.AddSubject(subject);
        c64.AddOpenSubject(subject_open);
        c64.AddClip(clip);
        if (!c64.Execute(ct, fr, solution, solution_open)) return;

        if (do_all)
        {
          int measuredCnt = solution.Count;
          double measuredArea = Clipper.Area(solution);

          double count_diff = (cnt <= 0) ? 0 :
            Math.Abs( measuredCnt / (double)cnt - 1.0);

          double area_diff = area <= 0 ? 0 : Math.Abs(measuredArea / area -1.0);

          if (count_diff > 0.05)
            Console.WriteLine($"{test_number}: count {cnt} vs {measuredCnt}");
          if (area_diff > 0.1)
            Console.WriteLine($"{test_number}: area {area} vs {measuredArea}");

          // don't display when looping through every test
          continue; 
        }

        SvgWriter svg = new();
        SvgUtils.AddSubject(svg, subject);
        SvgUtils.AddClip(svg, clip);
        if (fr == FillRule.Negative)
          solution = Clipper.ReversePaths(solution);
        SvgUtils.AddSolution(svg, solution, false);
        SvgUtils.AddCaption(svg, test_number.ToString(), 20, 20);
        string filename = @"..\..\..\poly" + (test_number - 1).ToString() + ".svg";
        SvgUtils.SaveToFile(svg, filename, fr, 800, 600, 10);
        ClipperFileIO.OpenFileWithDefaultApp(filename);
      }

      if (!do_all) return;
      Console.WriteLine($"\ntest ended at polygon {test_number}.\n");
      Console.ReadKey();
    }

    public static Paths64 LoadPathsFromResource(string resourceName)
    {
      using Stream stream = Assembly.GetExecutingAssembly().
        GetManifestResourceStream(resourceName);
      if (stream == null) return new Paths64();
      using BinaryReader reader = new (stream);
      int len = reader.ReadInt32();
      Paths64 result = new (len);
      for (int i = 0; i < len; i++)
      {
        int len2 = reader.ReadInt32();
        Path64 p = new (len2);
        for (int j = 0; j < len2; j++)
        {
          long X = reader.ReadInt64();
          long Y = reader.ReadInt64();
          p.Add(new Point64(X, Y));
        }
        result.Add(p);
      }
      return result;
    }

    public static void ClipTestPolys()
    {
      const FillRule fillrule = FillRule.NonZero;
      Paths64 subject = LoadPathsFromResource("ConsoleDemo.subj.bin");
      Paths64 clip = LoadPathsFromResource("ConsoleDemo.clip.bin");
      Paths64 solution = Clipper.Intersect(subject, clip, fillrule);

      SvgWriter svg = new();
      SvgUtils.AddSubject(svg, subject);
      SvgUtils.AddClip(svg, clip);
      SvgUtils.AddSolution(svg, solution, false);
      SvgUtils.SaveToFile(svg, @"..\..\..\clipperD.svg", fillrule, 800, 600, 20);
      ClipperFileIO.OpenFileWithDefaultApp(@"..\..\..\clipperD.svg");
    }


  } //end Application
} //namespace
