/*=========================================================================

Copyright (c) 1998-2005 Kitware Inc. 28 Corporate Drive, Suite 204,
Clifton Park, NY, 12065, USA.

All rights reserved. No part of this software may be reproduced,
distributed,
or modified, in any form or by any means, without permission in writing from
Kitware Inc.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

=========================================================================*/
// .NAME cmbFaceMeshHelper
// .SECTION Description
// Convert a vtkModelFace to a triangle input for meshing.

#ifndef __cmbFaceMeshHelper_h
#define __cmbFaceMeshHelper_h

#include "vtkCMBMeshingModule.h" // For export macro
#include <map> // Needed for STL map.
#include <set> // Needed for STL set.
#include <list> // Needed for STL list.
#include "vtkType.h"
#include "vtkABI.h"
#include "cmbSystemConfig.h"


class cmbFaceMesherInterface;
class vtkPolyData;

//-----------------------------------------------------------------------------
namespace CmbFaceMesherClasses
{
class VTKCMBMESHING_EXPORT meshVertex
{
public:
  meshVertex(const double& a, const double& b);
  meshVertex(const double& a, const double& b,
      const vtkIdType& ModelId, const int& ModelEntityType);

  double x;
  double y;
  vtkIdType modelId;
  int modelEntityType;

  //comparison operator needed for map storage
  bool operator<(const meshVertex &p) const;
};

class VTKCMBMESHING_EXPORT meshEdge
{
public:
  meshEdge(const vtkIdType& f, const vtkIdType& s);
  meshEdge(const vtkIdType& f, const vtkIdType& s, const vtkIdType& id);

  //comparison operator needed for map storage
  bool operator<(const meshEdge &es) const;

  const vtkIdType& first() const {return First;}
  const vtkIdType& second() const {return Second;}

  void setModelId(const vtkIdType& id){ModelId=id;}
  const vtkIdType& modelId() const {return ModelId;}

  //can't set the entityType as it is always an edge
  int modelEntityType() const;
protected:
  vtkIdType First;
  vtkIdType Second;
  vtkIdType ModelId;
};

class VTKCMBMESHING_EXPORT ModelEdgeRep
{
public:
  ModelEdgeRep(const int &id):Id(id){}

  void addModelVert(const vtkIdType &id, double point[3]);
  void setMeshPoints(vtkPolyData *mesh, vtkIdType offset=0, vtkIdType size=-1);

  //Note: verts and mesh points need to be added
  //before valid result is returned
  int numberOfEdges() const;

  int numberOfVertices() const {return static_cast<int>(this->MeshPoints.size());}

  const std::set<meshVertex>& getModelVerts() const {return this->ModelVerts;}
  const std::list<meshEdge>& getSegments() const {return this->Segments;}
  const std::map<vtkIdType,meshVertex>& getMeshPoints() const {return this->MeshPoints;}

  const vtkIdType& getId() const{return this->Id;}
protected:

  //Updates each locally stored mesh point in the edge mesh with the latest
  //relationship. If the point matches a model vert
  //it will have the id of the model vert and be set to vtkModelVertexType
  //else it will have the id of the model edge and be set to vtkModelEdgeType
  void updateModelRealtionships();

  const vtkIdType Id;
  std::list<meshEdge> Segments;
  std::map<vtkIdType,meshVertex> MeshPoints;
  std::set<meshVertex> ModelVerts;
};

class VTKCMBMESHING_EXPORT ModelLoopRep
{
public:
  ModelLoopRep(const vtkIdType &id, const bool &isInternal)
    :Id(id), IsOuterLoop(!isInternal){}

  //returns true if an edge with the sameUniquePersistentId has already be added
  bool edgeExists(const vtkIdType &e) const;

  //Add an edge to the ModelLoopRep.
  //Only unique edges will be added.
  void addEdge(const ModelEdgeRep &edge);

  //returns: If the point is used in the loop.
  //The pointId is the loop based id of the point
  //
  //If the point is a model vertex, the modelEntityType
  //  will be set to vtkModelVertexType, and the uniqueId will be
  //  set to the UniquePersistentId of the model vertex.
  //If the point is a mesh edge rep point, the modelEntityType
  //  will be set to vtkModelEdgeType, and the uniqueId will be
  //  set to the UniquePersistenId of the edge.
  //Otherwise the modelEntityType and uniqueId will not be modified
  //and we will return false
  bool pointClassification(const vtkIdType &pointId,
    int &modelEntityType, vtkIdType &uniqueId) const;

  //returns: If the poin is used in the loop.
  //The pointId is the loop based id of the point
  //
  //If the point is a model vertex, the modelEntityType
  //  will be set to vtkModelVertexType, and the uniqueId will be
  //  set to the UniquePersistentId of the model vertex.
  //If the point is a mesh edge rep point, the modelEntityType
  //  will be set to vtkModelEdgeType, and the uniqueId will be
  //  set to the UniquePersistenId of the edge.
  //Otherwise the modelEntityType and uniqueId will not be modified
  //and we will return false
  bool pointClassification(const double &x, const double &y,
    int &modelEntityType, vtkIdType &uniqueId) const;

  //returns: True if the edge is used in the loop.
  //The pointIds are the loop based ids of the points
  //if the edge is a mesh edge the modelEntityType
  //  will be set to vtkModelEdgeType, and the uniqueId will be
  //  set to the UniquePersistenId of the edge of the edge
  //Otherwise the modelEntityType and uniqueId will not be modified
  //and we will return false
  bool edgeClassification(const vtkIdType &pointId1, const vtkIdType &pointId2,
    int &modelEntityType, vtkIdType &uniqueId) const;

  //returns true if the edge is contained in the loop.
  //The Ids passed in must be between zero and number of Points - 1
  //if the edge is a mesh edge the modelEntityType
  //  will be set to vtkModelEdgeType, and the uniqueId will be
  //  set to the UniquePersistenId of the edge of the edge
  //Otherwise the modelEntityType and uniqueId will not be modified
  //and we will return false
  bool edgeClassification(const double &x1, const double &y1,
    const double &x2, const double &y2,
    int &modelEntityType, vtkIdType &uniqueId) const;

  //returns the number of unique points in this loop
  int numberOfVertices() const;

  //get the number of line segments in the loop
  int numberOfEdges() const;

  bool isOuterLoop() const;
  bool isDegenerateLoop() const;

  //returns NULL if point is not found
  const meshVertex* getPoint(const vtkIdType &meshVertexId) const;

  //returns NULL if point is not found
  const meshVertex* getPoint(const double &x, const double &y) const;

  //returns the local loop id for the mesh vertex.
  //if not found it will return -1
  vtkIdType getMeshVertexId(const double &x, const double &y) const;

  // adds this loops information to the triangle interface
  // modifies the pointIndex, segment Index, and HoleIndex
  void addDataToTriangleInterface(cmbFaceMesherInterface *ti,
     int &pointIndex, int &segmentIndex, int &holeIndex);

  //returns true when we find the closest segment. It should
  //only return false when no edges have been added to the loop
  //The closest segment will be stored in the variable edge
  //The closest point on the segment will be stored in vertex.
  //NOTE: This method only uses the mid point of the segment
  //to find the closest segment.
  const meshEdge* findClosestSegment(const double &x, const double &y,
    meshVertex &vertex) const;

  //returns true if we find a valid point inside the loop
  //that doesn't lay on a boundary. The valid point coordinates
  //will be placed in the x and y variables that are passed in.
  bool findAPointInside(double &x, double &y) const;

  // Unused functions that were meant for finding a point inside a region
  // The triangle code for that is not implemented.
  //bool findAPointInsideNotIn(double& x, double &y, const std::list<ModelLoopRep>& others) const;

  //returns true if the input coordinate is collinear with
  //any of the edges of the loop
  bool isBoundaryPoint(const double& x, const double& y) const;

  //returns true if the input coordinate is inside the loop.
  //This will not check if the point exists on the loops boundary
  //so you should call isBoundaryPoint first
  bool isPointInside(const double& x, const double& y) const;

  //returns the bounds in the order of:
  //xmin,ymin,xmax,ymax
  void bounds(double bounds[4]) const;

protected:
  //copy the information from the edge into the loop
  void addEdgeToLoop(const ModelEdgeRep &edge);

  //Inserts the point if it doesn't exist, and returns
  //the vtkIdType id of the point.
  vtkIdType insertPoint(const meshVertex &point);

  //Find if a the model edge id is to a model edge
  //that isn't part of the valid loop section of the loop.
  //i.e it is a hanging line
  bool isNonManifoldEdge(const vtkIdType &modelEdgeId) const;

  bool findPointInsideConvex(double& x, double &y) const;
  bool findPointInsideConcave(double& x, double &y) const;
  // Unused functions that were meant for finding a point inside a region
  // The triangle code for that is not implemented.
 // bool findPointInsideConcaveNotIn(double& x,double& y, const std::list<ModelLoopRep>& others) const;

  const vtkIdType Id;

  //stores if this loop represents the outer boundary of a face
  const bool IsOuterLoop;

  //these store ids, so we don't have duplicates. We use the count
  //to determine if this edge is a valid edge to use in raycasting
  //when determing point inside. Note: the reason for this is that
  //edges that are used twice contain no volume so they can't be used
  //when ray tracing
  std::map<vtkIdType, int> ModelEdges;

  // The following is for classification of mesh entities
  // with respects to model edges and vertices

  //Stores all the segments. Stores the segments in a way that
  //is fast to see if an edge already exists. When iterated will not
  //form a sequential list of segments of the loop. This is done this
  //way because our mesher doesn't care about order so that isn't slowed
  //down but this makes mapping the mesh back to the model fast!
  std::set<meshEdge> Segments;

  //bi directional map implemented as two maps
  //PointsToIds needed for easy lookup on duplicate points
  //IdsToPoints needed for correct indexing from the segments, also needed
  //for fast lookup on points mapping back to model
  std::map<meshVertex,vtkIdType> PointsToIds;
  std::map<vtkIdType,meshVertex> IdsToPoints;
};

class VTKCMBMESHING_EXPORT ModelFaceRep
{
  public:
    void addLoop(const ModelLoopRep &loop);
    int numberOfVertices();
    int numberOfEdges();
    int numberOfHoles();
    //returns the bounds in the order of:
    //xmin,ymin,xmax,ymax
    bool bounds(double bounds[4]);

    // Unused functions that were meant for finding a point inside a region
    // The triangle code for that is not implemented.
    //Gets a point that is inside the outerboundry region and not in any inner loops
    //bool findAPointInsidePoly(double& x, double& y);
    //Gets points inside all innerloops that do not also have a point in regionPoints inside of them
    //bool findPointsInsideHoles(std::list< std::pair<double,double> > regionPoints, std::list< std::pair<double,double> > holePoints);

    void fillTriangleInterface(cmbFaceMesherInterface *ti);

    //Add information from the mesher's input to its output
    bool RelateMeshToModel(vtkPolyData *mesh, const vtkIdType &facePersistenId);

    static const char* Get2DAnalysisPointModelIdsString() {return "2DAnalysisPointModelIds";};
    static const char* Get2DAnalysisPointModelTypesString() {return "2DAnalysisPointModelTypes";};
    static const char* Get2DAnalysisCellModelIdsString() {return "2DAnalysisCellModelIds";};
    static const char* Get2DAnalysisCellModelTypesString() {return "2DAnalysisCellModelTypes";};
    static const char* Get2DAnalysisCellPointIdsString() {return "2DAnalysisCellPointIds";};

  protected:
    bool RelateMeshPointsToModel(vtkPolyData *mesh, const vtkIdType &facePersistenId);
    bool RelateMeshCellsToModel(vtkPolyData *mesh, const vtkIdType &facePersistenId);
    bool SetFaceIdOnMesh(vtkPolyData *mesh, const vtkIdType &facePersistenId);
    std::list<ModelLoopRep> Loops;
};
}
#endif
