#include <stdlib.h>
#include <iostream.h>
#include <assert.h>

#include "Subdivision.hpp"

Edge *Subdivision::makeEdge(Vec2& org, Vec2& dest)
{
    Edge *e = new Edge();
    e->EndPoints(org, dest);
    return e;
}

Edge *Subdivision::makeEdge()
{
    return new Edge();
}

void Subdivision::initMesh(const Vec2& A,const Vec2& B,
			   const Vec2& C,const Vec2& D)
{
    Vec2& a = A.clone();
    Vec2& b = B.clone();
    Vec2& c = C.clone();
    Vec2& d = D.clone();

    Edge *ea = makeEdge();
    ea->EndPoints(a, b);

    Edge *eb = makeEdge();
    splice(ea->Sym(), eb);
    eb->EndPoints(b, c);

    Edge *ec = makeEdge();
    splice(eb->Sym(), ec);
    ec->EndPoints(c, d);

    Edge *ed = makeEdge();
    splice(ec->Sym(), ed);
    ed->EndPoints(d, a);
    splice(ed->Sym(), ea);

    Edge *diag = makeEdge();
    splice(ed->Sym(),diag);
    splice(eb->Sym(),diag->Sym());
    diag->EndPoints(a,c);

    startingEdge = ea;

    first_face = NULL;

    makeFace(ea->Sym()).update(*this);
    makeFace(ec->Sym()).update(*this);

}



void Subdivision::deleteEdge(Edge *e)
{
    splice(e, e->Oprev());
    splice(e->Sym(), e->Sym()->Oprev());

    delete e;
}

Edge *Subdivision::connect(Edge *a, Edge *b)
{
    Edge *e = makeEdge();
    splice(e, a->Lnext());
    splice(e->Sym(), b);
    e->EndPoints(a->Dest(), b->Org());

    return e;
}

void Subdivision::swap(Edge *e)
{
    Triangle *f1 = e->Lface();
    Triangle *f2 = e->Sym()->Lface();

    Edge *a = e->Oprev();
    Edge *b = e->Sym()->Oprev();

    splice(e, a);
    splice(e->Sym(), b);
    splice(e, a->Lnext());
    splice(e->Sym(), b->Lnext());
    e->EndPoints(a->Dest(), b->Dest());


    f1->reshape(e);
    f2->reshape(e->Sym());
}



//
// Subdivision iterators
//

static unsigned int timestamp = 0;

static void overEdge(Edge *e, edge_callback fn, void *closure)
{
    if( e->token != timestamp )
    {
	e->token = timestamp;
	e->Sym()->token = timestamp;

	(*fn)(e, closure);

	overEdge(e->Onext(), fn, closure);
	overEdge(e->Oprev(), fn, closure);
	overEdge(e->Dnext(), fn, closure);
	overEdge(e->Dprev(), fn, closure);
    }
}

void Subdivision::overEdges(edge_callback fn, void *closure)
{
    if( ++timestamp == 0 )
	timestamp = 1;

    overEdge(startingEdge, fn, closure);
}

void Subdivision::overFaces(face_callback fn, void *closure)
{
    Triangle *t = first_face;

    while( t )
    {
	(*fn)(*t, closure);
	t = t->getLink();
    }
}




//
// Random predicates
//

bool Subdivision::ccwBoundary(const Edge *e)
{
    return !rightOf(e->Oprev()->Dest(), e);
}


bool Subdivision::onEdge(const Vec2& x, Edge *e)
{
    real t1, t2, t3;

    t1 = (x - e->Org()).length();
    t2 = (x - e->Dest()).length();

    if (t1 < EPS || t2 < EPS)
	return true;

    t3 = (e->Org() - e->Dest()).length();

    if (t1 > t3 || t2 > t3)
	return false;

    Line line(e->Org(), e->Dest());
    return (fabs(line.eval(x)) < EPS);
}


bool Subdivision::isInterior(Edge *e)
//
// Tests whether e is an interior edge.
// 
// WARNING: This topological test will not work if the boundary is
//          a triangle.  This is not a problem here; the boundary is
//          always a rectangle.  But if you try to adapt this code, please
//          keep this in mind.
{
    return (e->Lnext()->Lnext()->Lnext() == e &&
            e->Rnext()->Rnext()->Rnext() == e );
}

bool Subdivision::shouldSwap(const Vec2& x, Edge *e)
{
        Edge *t = e->Oprev();
        return inCircle(e->Org(), t->Dest(), e->Dest(), x);
}





Edge *Subdivision::locate(const Vec2& x, Edge *start)
{
    Edge *e = start;
    real t = triArea(x, e->Dest(), e->Org());

    if (t>0) {                  // x is to the right of edge e
        t = -t;
        e = e->Sym();
    }


    while (true)
    {
        Edge *eo = e->Onext();
        Edge *ed = e->Dprev();

        real to = triArea(x, eo->Dest(), eo->Org());
        real td = triArea(x, ed->Dest(), ed->Org());

        if (td>0)                       // x is below ed
            if (to>0 || to==0 && t==0) {// x is interior, or origin endpoint
                startingEdge = e;
                return e;
            }
            else {                      // x is below ed, below eo
                t = to;
                e = eo;
            }
        else                            // x is on or above ed
            if (to>0)                   // x is above eo
                if (td==0 && t==0) {    // x is destination endpoint
                    startingEdge = e;
                    return e;
                }
                else {                  // x is on or above ed and above eo
                    t = td;
                    e = ed;
                }
            else                        // x is on or below eo
                if (t==0 && !leftOf(eo->Dest(), e))
		    // x on e but subdiv. is to right
                    e = e->Sym();
                else if (rand()&1) {  // x is on or above ed and
                    t = to;             // on or below eo; step randomly
                    e = eo;
                }
                else {
                    t = td;
                    e = ed;
                }
    }
}


Edge *Subdivision::spoke(Vec2& x, Edge *e)
{
    Triangle *new_faces[4];
    int facedex = 0;

    //
    // NOTE: e is the edge returned by locate(x)
    //

    if ( (x == e->Org()) || (x == e->Dest()) ) {
        // point is already in the mesh
	//
/*
        cerr << "WARNING: Tried to reinsert point: " << x << endl;
		cerr << "         org: " << e->Org() << endl;
		cerr << "        dest: " << e->Dest() << endl;
*/
        return NULL;
    }

    Edge *boundary_edge = NULL;

    Triangle *lface = e->Lface();
    lface->dontAnchor(e);
    new_faces[facedex++] = lface;

    if( onEdge(x,e) )
    {
	if( ccwBoundary(e) ) {
	    //
	    // e lies on the boundary
	    // Defer deletion until after new edges are added.
	    boundary_edge = e;
	}
	else {
	    Triangle *sym_lface = e->Sym()->Lface();
	    new_faces[facedex++] = sym_lface;
	    sym_lface->dontAnchor(e->Sym());


	    e = e->Oprev();
	    deleteEdge(e->Onext());
	}
    }
    else
    {
	// x lies within the Lface of e
    }

    Edge *base = makeEdge(e->Org(), x.clone());

    splice(base, e);

    startingEdge = base;
    do {
	base = connect(e, base->Sym());
	e = base->Oprev();
    } while( e->Lnext() != startingEdge );

    if( boundary_edge )
	deleteEdge(boundary_edge);

    // Update all the faces in our new spoked polygon.
    // If point x on perimeter, then don't add an exterior face


    base = boundary_edge ? startingEdge->Rprev() : startingEdge->Sym();
    do {
	if( facedex )
	    new_faces[--facedex]->reshape(base);
	else
	    makeFace(base);

	base = base->Onext();

    } while( base != startingEdge->Sym() );

    return startingEdge;
}


//
// s is a spoke pointing OUT from x
//
void Subdivision::optimize(Vec2& x, Edge *s)
{
    Edge *start_spoke = s;
    Edge *spoke = s;

    do {

	Edge *e = spoke->Lnext();
	Edge *t = e->Oprev();

	if( isInterior(e) && shouldSwap(x, e) )
	    swap(e);
	else
	{
	    spoke = spoke->Onext();
	    if( spoke == start_spoke )
		break;
	}

    } while( true );

    //
    // Now, update all the triangles

    spoke = start_spoke;

    do {
	Edge *e = spoke->Lnext();
	Triangle *t = e->Lface();

	if( t ) t->update(*this);

	spoke = spoke->Onext();
    } while( spoke != start_spoke );
}

Edge *Subdivision::insert(Vec2& x, Triangle *tri)
{
    Edge *e = tri?locate(x, tri->getAnchor()):locate(x);

    Edge *start_spoke = spoke(x, e);

    if( start_spoke )
	optimize(x, start_spoke->Sym());

    return start_spoke;
}



Triangle *Subdivision::allocFace(Edge *e)
{
    return new Triangle(e);
}

Triangle& Subdivision::makeFace(Edge *e)
{
    Triangle *t = allocFace(e);

    first_face = t->linkTo(first_face);

    return *t;
}







void Triangle::dontAnchor(Edge *e)
{
    if( anchor == e )
    {
	anchor = e->Lnext();
    }
}

void Triangle::reshape(Edge *e)
{
    anchor = e;

    e->set_Lface(this);
    e->Lnext()->set_Lface(this);
    e->Lprev()->set_Lface(this);

}


void Triangle::update(Subdivision&)
// called by reshape to update stuff
//
// the default method will do nothing
{


}
