using System.Collections.Generic;

using NetPoly = TriangleNet.Geometry.Polygon;
using NetVertex = TriangleNet.Geometry.Vertex;
using NetContour = TriangleNet.Geometry.Contour;
using NetTriangle = TriangleNet.Geometry.ITriangle;
using NetMesh = TriangleNet.Meshing.IMesh;
using ClipperLib;

public class CheesePolygon {
    private readonly Contour contour;
    private readonly List<Contour> holes;
	private List<Segment> segments;

    public CheesePolygon(Contour contour, List<Contour> holes) {
        this.contour = contour;
        this.holes = holes;
    }

    public CheesePolygon(Contour contour) {
        this.contour = contour;
        this.holes = new List<Contour>();
    }

    //Returns true iff the vertex is inside the cheesePolygon
    public bool isPointInside(GameVertex vertex) {
        if (!contour.isPointInside(vertex)) { //is not inside contour
            return false;
        }

        foreach (Contour hole in holes) {
            if (hole.isPointInside(vertex)) { //is in hole
                return false;
            }
        }

        return true;
    }

    //This method should only be called by SlicedPolygon
    public List<List<IntPoint>> getPaths() {
        List<List<IntPoint>> paths = new List<List<IntPoint>>();

        paths.Add(contour.getPath());

        foreach (Contour hole in holes) {
            paths.Add(hole.getPath());
        }

        return paths;
    }

    public Set<GameVertex> getVertices() {
        Set<GameVertex> vertices = new Set<GameVertex>(contour.getVertices());

        foreach (Contour hole in holes) {
            vertices.addRange(hole.getVertices());
        }

        return vertices;
    }

    public List<Segment> getSegments() {
		if (segments != null) {
			return segments;
		}

        segments = new List<Segment>(contour.getSegments());

        foreach (Contour hole in holes) {
            segments.AddRange(hole.getSegments());
        }

        return segments;
    }

    public Set<Contour> getContours() {
        Set<Contour> contours = new Set<Contour>();
        contours.add(contour);
        contours.addRange(holes);
        return contours;
    }

    public NetPoly toNetPoly() {
        NetPoly poly = new NetPoly();

        NetContour contour = this.contour.toNetContour();
        poly.Add(contour, false);

        foreach (Contour hole in holes) {
            poly.Add(hole.toNetContour(), true);
        }

        return poly;
    }

    public SlicedPolygon difference(List<SlicedPolygon> others) {
        return difference(0, others);
    }

    public SlicedPolygon difference(int offset, List<SlicedPolygon> others) {
        return new SlicedPolygon(this).difference(offset, others);
    }

    public CheesePolygon cutLongEdges() {
        Contour newContour = contour.cutLongEdges();
        List<Contour> newHoles = new List<Contour>();

        foreach (Contour hole in holes) {
            newHoles.Add(hole.cutLongEdges());
        }

        return new CheesePolygon(newContour, newHoles);
    }

    public CheesePolygon updateVertex(GameVertex oldVertex, GameVertex newVertex) {
        Contour newContour = contour.updateVertex(oldVertex, newVertex);
        List<Contour> newHoles = new List<Contour>();

        foreach (Contour hole in holes) {
            newHoles.Add(hole.updateVertex(oldVertex, newVertex));
        }

        return new CheesePolygon(newContour, newHoles);
    }

	public float distanceTo(GameVertex v) {
		float minDist = float.MaxValue;

		foreach(Contour hole in holes) {
			float dist = hole.distanceTo(v);
			if (minDist > dist) {
				minDist = dist;
			}
		}

		return UnityEngine.Mathf.Min(minDist, contour.distanceTo(v));
	}
}

public class CheesePolygonBuilder {
    Contour contour;
    List<Contour> holes;

    public CheesePolygonBuilder(Contour contour) {
        this.contour = contour;
        this.holes = new List<Contour>();
    }

    public Contour getContour() {
        return contour;
    }

    public void addHole(Contour hole) {
        holes.Add(hole);
    }

    public CheesePolygon build() {
        return new CheesePolygon(contour, holes);
    }
}
