/*
 * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
 * http://code.google.com/p/poly2tri/
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * * Neither the name of Poly2Tri nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
 * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
 * International Journal of Geographical Information Science
 *
 * "FlipScan" Constrained Edge Algorithm invented by Thomas ?hl?n, thahlen@gmail.com
 */

#ifndef SWEEP_H
#define SWEEP_H

#include <chrono>
#include <vector>

#define POLY2TRI_MAXTIME 2e4 // Time limit in microseconds

namespace p2t {

    class SweepContext;
    struct Node;
    struct Point;
    struct Edge;
    class Triangle;

    class Sweep
    {
	public:

	    /**
	     * Triangulate
	     */
	    void Triangulate(SweepContext& tcx, bool finalize = true, int num_points = -1);

	    /**
	     * Destructor - clean up memory
	     */
	    ~Sweep();

	private:

	    /**
	     * Start sweeping the Y-sorted point set from bottom to top
	     */
	    void SweepPoints(SweepContext& tcx, int num_points = -1);

	    /**
	     * Find closes node to the left of the new point and
	     * create a new triangle. If needed new holes and basins
	     * will be filled to.
	     *
	     */
	    Node& PointEvent(SweepContext& tcx, Point& point);

	    void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node);

	    void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point);

	    /**
	     * Creates a new front triangle and legalize it
	     *
	     */
	    Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node);
	    void UpdateNodeAngleCircum(Node& n);
	    /**
	     * Adds a triangle to the advancing front to fill a hole.
	     * @param tcx - sweep context
	     * @param node - middle node, that is the bottom of the hole
	     */
	    void Fill(SweepContext& tcx, Node& node);

	    /**
	     * Returns true if triangle was legalized
	     */
	    bool Legalize(SweepContext& tcx, Triangle& t);

	    /**
	     * <b>Requirement</b>:<br>
	     * 1. a,b and c form a triangle.<br>
	     * 2. a and d is know to be on opposite side of bc<br>
	     * <pre>
	     *                a
	     *                +
	     *               / \
	     *              /   \
	     *            b/     \c
	     *            +-------+
	     *           /    d    \
	     *          /           \
	     * </pre>
	     * <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
	     *  a,b and c<br>
	     *  d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
	     *  This preknowledge gives us a way to optimize the incircle test
	     * @param pa - triangle point, opposite d
	     * @param pb - triangle point
	     * @param pc - triangle point
	     * @param pd - point opposite a
	     * @return true if d is inside circle, false if on circle edge
	     */
	    bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd);
	    bool Circumcircle(const Point& pa, const Point& pb, const Point& pc, Point& center, double& radius);

	    /**
	     * Rotates a triangle pair one vertex CW
	     *<pre>
	     *       n2                    n2
	     *  P +-----+             P +-----+
	     *    | t  /|               |\  t |
	     *    |   / |               | \   |
	     *  n1|  /  |n3           n1|  \  |n3
	     *    | /   |    after CW   |   \ |
	     *    |/ oT |               | oT \|
	     *    +-----+ oP            +-----+
	     *       n4                    n4
	     * </pre>
	     */
	    void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op);

	    /**
	     * Fills holes in the Advancing Front
	     */
	    void CheckCircleEvent(SweepContext& tcx, double currentheight);
	    void FillAdvancingFront(SweepContext& tcx, Node& n);

	    // Decision-making about when to Fill hole.
	    // Contributed by ToolmakerSteve2
	    bool LargeHole_DontFill(const Node* node) const;
	    bool AngleIsNegative(const Point* origin, const Point* pa, const Point* pb) const;
	    bool AngleExceeds90Degrees(const Point* origin, const Point* pa, const Point* pb) const;
	    bool AngleExceedsPlus90DegreesOrIsNegative(const Point* origin, const Point* pa, const Point* pb) const;
	    double Angle(const Point& origin, const Point& pa, const Point& pb) const;

	    /**
	     *
	     * @param node - middle node
	     * @return the angle between 3 front nodes
	     */
	    double HoleAngle(Node& node);

	    /**
	     * The basin angle is decided against the horizontal line [1,0]
	     */
	    double BasinAngle(Node& node);

	    /**
	     * Fills a basin that has formed on the Advancing Front to the right
	     * of given node.<br>
	     * First we decide a left,bottom and right node that forms the
	     * boundaries of the basin. Then we do a reqursive fill.
	     *
	     * @param node - starting node, this or next node will be left node
	     */
	    void FillBasin(SweepContext& tcx, Node& node);

	    /**
	     * Recursive algorithm to fill a Basin with triangles
	     *
	     * @param node - bottom_node
	     */
	    void FillBasinReq(SweepContext& tcx, Node* node);

	    bool IsShallow(SweepContext& tcx, Node& node);

	    bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq);

	    void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);

	    void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);

	    void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node);

	    void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node);

	    void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p);

	    /**
	     * After a flip we have two triangles and know that only one will still be
	     * intersecting the edge. So decide which to contiune with and legalize the other
	     *
	     * @param o - should be the result of an orient2d( eq, op, ep )
	     * @param t - triangle 1
	     * @param ot - triangle 2
	     * @param p - a point shared by both triangles
	     * @param op - another point shared by both triangles
	     * @return returns the triangle still intersecting the edge
	     */
	    Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle&  t, Triangle& ot, Point& p, Point& op);

	    /**
	     * When we need to traverse from one triangle to the next we need
	     * the point in current triangle that is the opposite point to the next
	     * triangle.
	     */
	    Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op);

	    /**
	     * Scan part of the FlipScan algorithm<br>
	     * When a triangle pair isn't flippable we will scan for the next
	     * point that is inside the flip triangle scan area. When found
	     * we generate a new flipEdgeEvent
	     *
	     * @param ep - last point on the edge we are traversing
	     * @param eq - first point on the edge we are traversing
	     * @param flip_triangle - the current triangle sharing the point eq with edge
	     */
	    void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p);

	    Triangle* FindInternalTriangle(Triangle* ext_tri);

	    void FinalizationPolygon(SweepContext& tcx);

	    std::vector<Node*> nodes_;

	    int flip_edge_event_cnt = 0;

	    std::chrono::time_point<std::chrono::steady_clock> tri_start_time;
	    std::chrono::time_point<std::chrono::steady_clock> curr_time;
    };

}

#endif

// Local Variables:
// tab-width: 8
// mode: C++
// c-basic-offset: 4
// indent-tabs-mode: t
// c-file-style: "stroustrup"
// End:
// ex: shiftwidth=4 tabstop=8

