#ifndef D3TESS_H
#define D3TESS_H

#include <cassert>
#include <vector>
#include <set>
#include <iosfwd>
using namespace std;

#include <d3fan.h>
#include <d3fan2.h>
#include <d3minoperator.h>
#include <point.h>
#include <simplexD2linked.h>
#include <simplexface.h>
#include <virtualtriangle.h>


typedef unsigned int uint;
typedef unsigned int const uintc;
typedef point3<double> pt3;
typedef point3<double> const pt3c;

/*!
  \brief A 2D tessellation with linked simplexes.

  The third value is a function value at a point in 2D space.
*/
class d3tess
{
public:

  vector<pt3> pt;

  vector<simplexD2linked> vi;

  /* Current Pointer. */
  uint cp;
  /* Current Orientation - virtual simplex. */
  virtualtriangle vs;

  /* Decide which fan you are plugging in.
     Not templated because then this whole class has to be
       templated. */
  d3fan2 fan;

  // Upon insertion of a new primitive shape, it is minimized
  //   with the existing mesh.  This is a pointer to the binary 
  /*   operator. */
  d3minoperator* minimizer;

  bool debugenable;

  /* Statistics: count the number of moves a search takes. */
  uint movecounter;

  /* Add a simplex */
  void viadd
  ( 
    uintc v0, uintc v1, uintc v2,
    uintc n0=0, uintc n1=0, uintc n2=0
  );

  //! Deletes the previous minimizer.
  void minimizerSet( d3minoperator* m);

  /* Move to the neighbouring simplex relative to vs. */
  bool const moveleft();
  /* Move to the neighbouring simplex relative to vs. */
  bool const moveright();
  /* Move to the neighbouring simplex relative to vs. */
  bool const movedown();

  /* Orient the base triangle of vs to the boundary. */
  bool const boundaryorient();
  /* Assuming base of vs on the boundary, move right */
  void surfaceright();
  /* Assuming base of vs on the boundary, move left */
  void surfaceleft();

  /* Get the current simplex. */
  simplexD2linked const & cpsimplex() const  
   { assert((cp!=0) && (cp<vi.size())); return vi[cp]; }

  /* Get the current cp orientation as a cp and face pair. */
  simplexface const cpsimplexfaceget() const;
  /* Set the current cp and orientation. */
  void cpsimplexfaceset( simplexface const & sf);

  /* Greedy search minimizing towards point p in the mesh.
     If the point is outside the mesh the virtual triangle vs
     is oriented to have its base on the viewable face. */
  bool const searchminimizetowardspoint( uintc p );

  //! One move - greedy.
  bool const move_terminated
  (
    bool & insidesimplex,
    uint & viewableface,
    uintc p
  );


  //! Uses the first three points build the first simplex, 
  //!   assumes the points can form a simplex.
  void initialize();

  //! Add an existing point to mesh creating new simplexes
  void addpoint(uintc k);

  //! Write the tessellation out. 
  void print(string & s) const;
  //! Write the tessellation out. 
  ostream & print(ostream & os) const;
  //! Read the tessellation in.
  istream & serializeInverse(istream & is);

  // Error checking code.
  bool const valid(uintc k) const;
  bool const debugcheck() const;

  //! Constructor - reserves element size. The mesh is reset().
  d3tess( uintc arrayMax );

  //! Destructor
  ~d3tess();

  //! Reset the mesh. vi[0] and pi[0] are the only
  //!   items in vi and pi and are dummy values.
  void reset();

  //! Are the simplexes adjacent?
  bool const isconnected(uintc a, uintc b) const; 
  //! Are the simplexes adjacent and together convex?
  bool const isconvex(uintc a, uintc b) const;

  //! Is the point viewable from vs's base?
  bool const surfaceviewable(uintc k) const;

  //! Transform the virual triangle and base by flipping
  //!   their triangles.
  bool const flip();
  //! Transform a and b by a flip if they are adjacent
  //!   and together form a convex shape.
  bool const flip(uintc a, uintc b);

  //! Delete the simplex.
  bool const simplexdelete(uintc s);
  //! Swap two simplexes.
  void simplexswap(uintc s0, uintc s1);
  //! Remove null simplexes if they exist by swapping and 
  //!   deleting end simplexes.
  void removenullsimplexes();

  //! Return the length of the cp's base line. 
  double const cpbasemeasure() const;

//  bool enablesplit;
//  bool enablefan;

  //! Save the current cp and orientation.
  void cppreserve(uint & cp0, virtualtriangle & vs0) const;
  //! Restore the cp and orientation.
  void cppreserveInverse
  (
    uint const & cp0, 
    virtualtriangle const & vs0
  );

  //
  //  Splitting Operators
  //

  //  splitonboundary and spliton1D have equivalent functionality but
  //    the former is more efficient, but more difficult to use and 
  //    has less error checking.

  //! Split the cp with the point w which is inside the triangle.
  void split2D( uintc w );

  //! Split simplex s on an edge at point w0 indexed by p0.
  void split1D( uintc s, uintc p0, uintc w0 );

  // I have avoided this routine. 
  //void split1D( uint & , uint * si, uintc s, uintc p0, uintc w0 );


  //! Two points w0 and w1 on the boundary split the triangle. p0 and p1 index the points. 
  void splitwithline( uintc s, uintc p0, uintc w0, uintc p1, uintc w1 );


  //! Split the simplex on the 1D boundary in two. p1 is the point opposite the bounary edge.
  void splitmidpoint1D(uintc s, uintc p1);


private:

  // Two triangles produced.
  void splitonboundarynoneighbor
  ( 
    uintc s,
    uintc j, 
    uintc w 
  );
};

//  Brief:  Save the current cp orientation and restore when
//          the object dies.
class d3tesspreserve
{
  d3tess & tess;
  uint cp0;
  virtualtriangle vs0;
public:

  // Store the current cp and orientation.
  d3tesspreserve( d3tess & _tess);

  // Restore the saved cp and orientation.
  ~d3tesspreserve();
};
  
    
  

  


ostream & operator << (ostream& os, d3tess const & x);

istream & operator >> (istream & is, d3tess & x);


class maxEdgeLength
{
  d3tess & tess;
  vector<simplexD2linked> & vi;
  vector<pt3> & pt;
  double const delta;
public:

  maxEdgeLength(d3tess & _tess, double const _delta)
    : tess(_tess), vi(tess.vi), pt(tess.pt), delta(_delta) {}

  bool const eval(uintc s, uintc j)
  {
    assert(j<3);

    simplexD2linked * x = & vi[s];
    pt3c & A(pt[ x->pi[(j+1)%3] ]);
    pt3c & B(pt[ x->pi[(j+2)%3] ]);
    if ( (A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y) > delta )
    {
      tess.splitmidpoint1D(s,x->pi[j]);
      return true;
    }

    return false;
  }

};




#endif



