//=============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the above copyright notice for more information.
//=============================================================================
#ifndef __smtk_bridge_polygon_internal_Vertex_h
#define __smtk_bridge_polygon_internal_Vertex_h

#include "smtk/SharedFromThis.h"
#include "smtk/bridge/polygon/internal/Entity.h"

namespace smtk {
  namespace bridge {
    namespace polygon {
      namespace internal {

/**\brief A base class for all internal vertex storage.
  *
  * Every vertex stores a pointer to its parent and its UUID.
  * This class uses smtkEnableSharedPtr so that all entities may be
  * managed via one pool of shared pointers.
  */
class vertex : public entity
{
public:
  smtkTypeMacro(vertex);
  smtkCreateMacro(vertex);
  smtkSharedFromThisMacro(entity);
  virtual ~vertex() { }

  struct incident_edge_data
    {
    Id m_edgeId; // Should never be NULL
    Id m_adjacentFace; // Face immediately CW of m_edgeId. May be NULL.
    bool m_edgeOut; // True when edge points outward from vertex (i.e., edge oriented so beginning vertex is this vertex)

    incident_edge_data(Id edgeId, bool isEdgeOut)
      : m_edgeId(edgeId), m_edgeOut(isEdgeOut) { }
    incident_edge_data(Id faceId)
      : m_adjacentFace(faceId) { }
    incident_edge_data(Id edgeId, bool isEdgeOut, Id faceId)
      : m_edgeId(edgeId), m_adjacentFace(faceId), m_edgeOut(isEdgeOut) { }
    incident_edge_data(const incident_edge_data& other)
      : m_edgeId(other.edgeId()), m_adjacentFace(other.clockwiseFaceId()), m_edgeOut(other.isEdgeOutgoing()) { }
    incident_edge_data()
      : m_edgeOut(false) { }

    Id edgeId() const { return this->m_edgeId; }
    Id clockwiseFaceId() const { return this->m_adjacentFace; }
    bool isEdgeOutgoing() const { return this->m_edgeOut; }
    };
  typedef std::list<incident_edge_data> incident_edges;

  const Point& point() const { return this->m_coords; }
  Point& point() { return this->m_coords; }

  bool canInsertEdge(const Point& neighborhood, incident_edges::iterator* where);
  incident_edges::iterator insertEdgeAt(incident_edges::iterator where, const Id& edgeId, bool edgeOutwards);
  incident_edges::iterator insertEdgeAt(incident_edges::iterator where, const Id& edgeId, bool edgeOutwards, const Id& faceId);
  void removeEdgeAt(incident_edges::iterator where);

  incident_edges::size_type numberOfEdgeIncidences() const { return this->m_edges.size(); }
  incident_edges::const_iterator edgesBegin() const { return this->m_edges.begin(); }
  incident_edges::const_iterator edgesEnd() const { return this->m_edges.end(); }

  incident_edges::iterator edgesBegin() { return this->m_edges.begin(); }
  incident_edges::iterator edgesEnd() { return this->m_edges.end(); }

  const incident_edge_data& edgesFront() const { return this->m_edges.front(); }
  const incident_edge_data& edgesBack() const { return this->m_edges.back(); }

  incident_edge_data& edgesFront() { return this->m_edges.front(); }
  incident_edge_data& edgesBack() { return this->m_edges.back(); }

  incident_edges::const_reverse_iterator edgesRBegin() const { return this->m_edges.rbegin(); }
  incident_edges::const_reverse_iterator edgesREnd() const { return this->m_edges.rend(); }

  incident_edges::reverse_iterator edgesRBegin() { return this->m_edges.rbegin(); }
  incident_edges::reverse_iterator edgesREnd() { return this->m_edges.rend(); }

  bool setFaceAdjacency(const Id& incidentEdge, const Id& adjacentFace, bool isCCW = true, int edgeDir = 0);
  int removeFaceAdjacencies(const Id& face);

  int removeIncidentEdge(const Id& edge);

  void dump();

  /// To be used by SessionIOJSON only (for deserializing from storage):
  void dangerousAppendEdge(const incident_edge_data& rec)
    {
    this->m_edges.insert(this->m_edges.end(), rec);
    }

  /// For use only by pmodel::splitEdge
  void setInsideSplit(bool duringSplit)
    {
    this->m_insideSplit = duringSplit;
    }

protected:
  friend class pmodel;

  vertex() : m_insideSplit(false) { }

  Point m_coords; // position in the plane.
  incident_edges m_edges; // CCW list of incident edges
  bool m_insideSplit; // Is an edge attached here being split? If so, canInsertEdge will behave differently.

  // NB: One extension to this structure would be:
  // Ptr m_prev; // Previous model vertex located at this exact point in the plane
  // Ptr m_next; // Next model vertex located at this exact point in the plane
  // These would allow modeling polygons that were "topologically independent"
  // while still allowing quick lookup of vertices by point location.

  // NB: Another extension to this structure would be:
  // Id m_containingFace; // Face that contains this model vertex as a hard point in its interior.
};

      } // namespace internal
    } // namespace polygon
  } // namespace bridge
} // namespace smtk

#endif // __smtk_bridge_polygon_internal_Vertex_h
