/**
 * @modified_by Ikaros Kappler
 * @date_init   2012-10-17
 * @date        2017-07-31
 * @modified    2020-08-17 Ported from vanilla JS to TypeScript.
 * @version     2.0.1
 **/


import { Triangle } from "../../Triangle";
import { Vertex } from "../../Vertex";
import { Line as Edge } from "../../Line";

// TODO: cleanup? This seems not to be used.
// const EPSILON : number = 1.0e-6;


/**
 * @classdesc A Delaunay pointset triangulation implementation.  Inspired by
 *    http://www.travellermap.com/tmp/delaunay.htm
 *
 * License: Public Domain
 *          Original C++ code by Joshua Bell
 *
 * @requires Triangle
 * @requires Line
 * @requires Vertex
 */
export class Delaunay {

    private pointList : Array<Vertex>;

    
    constructor( pointList:Array<Vertex> ) {
	this.pointList = pointList;
    }

    triangulate(): Array<Triangle> {
	return this.Triangulate( this.pointList ); 
    }


    ////////////////////////////////////////////////////////////////////////////////
    //
    // Delaunay Triangulation Code, by Joshua Bell
    //
    // Inspired by: http://www.codeguru.com/cpp/data/mfc_database/misc/article.php/c8901/
    //
    // This work is hereby released into the Public Domain. To view a copy of the public 
    // domain dedication, visit http://creativecommons.org/licenses/publicdomain/ or send 
    // a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, 
    // California, 94105, USA.
    //
    ////////////////////////////////////////////////////////////////////////////////



    //------------------------------------------------------------
    // Triangulate
    //
    // Perform the Delaunay Triangulation of a set of vertices.
    //
    // vertices: Array of Vertex objects
    //
    // returns: Array of Triangles
    //------------------------------------------------------------
    Triangulate( vertices:Array<Vertex> ) : Array<Triangle> {
	const triangles : Array<Triangle> = [];

	// First, create a "supertriangle" that bounds all vertices  
	var st : Triangle = this.createBoundingTriangle( vertices );
	
	triangles.push( st );

	// begin the triangulation one vertex at a time
	for( var i in vertices ) {
	    // NOTE: This is O(n^2) - can be optimized by sorting vertices
	    // along the x-axis and only considering triangles that have 
	    // potentially overlapping circumcircles

	    this.AddVertex( vertices[i], triangles );
	}

	// Remove triangles that shared edges with "supertriangle"
	for( var i in triangles ) {
	    var triangle : Triangle = triangles[i];

	    if( triangle.a == st.a || triangle.a == st.b || triangle.a == st.c ||
		triangle.b == st.a || triangle.b == st.b || triangle.b == st.c ||
		triangle.c == st.a || triangle.c == st.b || triangle.c == st.c ) {
		delete triangles[i];
	    }
	}

	return triangles;
	
    }; // Triangulate


    // Internal: create a triangle that bounds the given vertices, with room to spare
    createBoundingTriangle( vertices:Array<Vertex> ) {
	// NOTE: There's a bit of a heuristic here. If the bounding triangle 
	// is too large and you see overflow/underflow errors. If it is too small 
	// you end up with a non-convex hull.
	
	var minx:number, miny:number, maxx:number, maxy:number;
	for( var i in vertices )
	{
	    var vertex = vertices[i];
	    if( minx === undefined || vertex.x < minx ) { minx = vertex.x; }
	    if( miny === undefined || vertex.y < miny ) { miny = vertex.y; }
	    if( maxx === undefined || vertex.x > maxx ) { maxx = vertex.x; }
	    if( maxy === undefined || vertex.y > maxy ) { maxy = vertex.y; }
	}

	var dx = ( maxx - minx ) * 10;
	var dy = ( maxy - miny ) * 10;
	
	var stv0 : Vertex = new Vertex( minx - dx,   miny - dy*3 );
	var stv1 : Vertex = new Vertex( minx - dx,   maxy + dy   );
	var stv2 : Vertex = new Vertex( maxx + dx*3, maxy + dy   );

	return new Triangle( stv0, stv1, stv2 );	
    }; // END createBoundingTriangle


    // Internal: update triangulation with a vertex 
    AddVertex( vertex:Vertex, triangles:Array<Triangle> ) : void {
	var edges : Array<Edge> = [];
	
	// Remove triangles with circumcircles containing the vertex
	for( var i in triangles ) {
	    var triangle : Triangle = triangles[i];

	    if( triangle.inCircumcircle( vertex ) ) {
		edges.push( new Edge( triangle.a, triangle.b ) );
		edges.push( new Edge( triangle.b, triangle.c ) );
		edges.push( new Edge( triangle.c, triangle.a ) );

		delete triangles[i];
	    }
	}

	edges = this.mkUniqueEdges( edges );

	// Create new triangles from the unique edges and new vertex
	for( var i in edges ) {
	    // console.log( 'adding triangle' );
	    var edge = edges[i];
	    triangles.push( new Triangle( edge.a, edge.b, vertex ) );
	}	
    }; // END AddVertex


    // Internal: remove duplicate edges from an array
    mkUniqueEdges( edges:Array<Edge> ) : Array<Edge> {
	// TODO: This is O(n^2), make it O(n) with a hash or some such
	const uniqueEdges : Array<Edge> = [];
	for( var i in edges ) {
	    let edge1 : Edge = edges[i];
	    let unique = true;

	    for( var j in edges ) {
		if( i != j ) {
		    let edge2 = edges[j];

		    if( ( edge1.a == edge2.a && edge1.b == edge2.b ) ||
			( edge1.a == edge2.b && edge1.b == edge2.a ) ) {
			unique = false;
			break;
		    }
		}
	    }
	    
	    if( unique ) { 
		uniqueEdges.push( edge1 );
	    }
	}

	return uniqueEdges;
    }; // END mkUniqueEdges
}; // END class


