/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//				
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/

#if !defined(ON_POLYEDGECURVE_INC_)
#define ON_POLYEDGECURVE_INC_

class ON_PolyEdgeSegment;

class ON_CLASS ON_PolyEdgeCurve : public ON_PolyCurve
{
  ON_OBJECT_DECLARE(ON_PolyEdgeCurve);

public:
  ON_PolyEdgeCurve();
  ~ON_PolyEdgeCurve();
  // default copy constructor and operator= are fine.

  // virtual ON_Object::DestroyRuntimeCache override
  void DestroyRuntimeCache( bool bDelete = true ) override;

  // virtual ON_Curve::IsClosed override
  bool IsClosed() const override;

  // virtual ON_Curve::DuplicateCurve override
  ON_Curve* DuplicateCurve() const override;

  /*
  Description:
    Create a one segment ON_PolyEdgeCurve curve that uses a 
    single edge.
  */
  bool Create(
           const ON_BrepTrim* trim,
           const ON_UUID& object_id
           );

  /*
  Description:
    Create a one segment ON_PolyEdgeCurve curve that uses a 
    single curve.
  */
  bool Create(
           const ON_Curve* curve,
           const ON_UUID& object_id
           );

  int SegmentCount() const;

  ON_PolyEdgeSegment* SegmentCurve(
    int segment_index
    ) const;

  ON_PolyEdgeSegment* operator[](int) const;

  bool Prepend( ON_PolyEdgeSegment* new_segment ); // Prepend curve.
  bool Append( ON_PolyEdgeSegment* new_segment );  // Append curve.
  bool Insert( 
           int segment_index,
           ON_PolyEdgeSegment* new_segment
           );

  // if the segment is an edge, the following
  // return non-nullptr pointers.
  const ON_BrepEdge* EdgeAt(double t) const;
  const ON_BrepTrim* TrimAt(double t) const;
  const ON_Brep*     BrepAt(double t) const;
  const ON_BrepFace* FaceAt(double t) const;
  const ON_Surface*  SurfaceAt(double t) const;
  ON_Surface::ISO    IsoType( double t) const;

  double EdgeParameter(double t) const;

  // Test if there are any surface edges in the polyedge
  bool ContainsAnyEdges() const;
  // Test if all segments of the polyedge are surface edges
  bool ContainsAllEdges() const;
  

  /*
  Description:
    See if this polyedge has an edge as one of its segments
  Parameters:
    edge - [in] the edge to look for
  Returns:
     -1: edge is not in the polyedge
    >=0: index of first segment that uses the edge
  */
  int FindEdge( const ON_BrepEdge* edge) const;

  /*
  Description:
    See if this polyedge has a trim as one of its segments
  Parameters:
    trim - [in] the trim to look for
  Returns:
     -1: trim is not in the polyedge
    >=0: index of first segment that uses the trim
  */
  int FindTrim( const ON_BrepTrim* trim) const;

  /*
  Description:
    See if this polyedge has a wire curve as one of its segments
  Parameters:
    curve - [in] the curve to look for
  Returns:
     -1: trim is not in the polyedge
    >=0: index of first segment that uses the curve
  */
  int FindCurve( const ON_Curve* curve) const;


  // OBSOLETE and unreliable.  Use FindCurve, FindEdge, or FindTrim
  //bool Contains( const ON_Curve* curve) const;

  // virtual ON_Curve overrides do nothing
  // to prevent changing edge
  bool SetStartPoint(ON_3dPoint start_point) override;
  bool SetEndPoint(ON_3dPoint end_point) override;
  bool ChangeClosedCurveSeam( double t ) override;
  bool PrependAndMatch(ON_Curve*);
  bool AppendAndMatch(ON_Curve*);

  // 7-1-03 lw added override to unset cached closed flag
  // when a segment is removed
  bool Remove(); // remove last
  bool Remove( int index);
};

class ON_CLASS ON_PolyEdgeSegment : public ON_CurveProxy
{
  ON_OBJECT_DECLARE(ON_PolyEdgeSegment);
public:
  // construction

  ON_PolyEdgeSegment();
  ~ON_PolyEdgeSegment();
  // default copy constructor and operator= are fine.

  // virtual ON_Object::DestroyRuntimeCache override
  void DestroyRuntimeCache( bool bDelete = true ) override;

  bool Write( ON_BinaryArchive& ) const override;

  bool Read( ON_BinaryArchive& ) override;

  // virtual ON_Curve::IsClosed override
  bool IsClosed() const override;


  // virtual ON_Curve::DuplicateCurve override
  ON_Curve* DuplicateCurve() const override;

  /*
  Description:
    Creates a polyedge segment that uses the entire edge
    and has the same domain as the edge.
  Parameters:
    trim - [in] 
  Returns:
    true if successful (edge was valid and trim_index was valid)
  Remarks:
    Use ON_Curve::SetDomain, ON_Curve::Trim, ON_Curve::Reverse,
    etc., to tweak the domain, support, direction etc.
  */
  bool Create( 
          const ON_BrepTrim* trim,
          const ON_UUID& object_id
          );

  /*
  Description:
    Creates a polyedge segment that uses the entire curve
    and has the same domain as the curve.
  Parameters:
    curve - [in] 
  Remarks:
    Use ON_Curve::SetDomain, ON_Curve::Trim, ON_Curve::Reverse,
    etc., to tweak the domain, support, direction etc.
  */
  bool Create( 
          const ON_Curve* curve,
          const ON_UUID& object_id
          );

  const ON_BrepEdge* BrepEdge() const;
  const ON_BrepTrim* BrepTrim() const;
  const ON_Brep*     Brep() const;
  const ON_BrepFace* BrepFace() const;
  const ON_Surface*  Surface() const;
  ON_Surface::ISO    IsoType() const;

  double EdgeParameter(double t) const;

  /*
  Returns:
    True if this segment has an ON_BrepEdge and the direction of
    the ON_BrepEdge is the reverse of the direction of the segment.
  */
  bool ReversedEdgeDir() const;

  /*
  Returns:
    True if this segment has an ON_BrepTrim and the direction of
    the ON_BrepTrime is the reverse of the direction of the segment.
  */
  bool ReversedTrimDir() const;

  /*
  Returns:
    subdomain of the edge that this segment uses.  This can
    be different than the domain returned by this->Domain().
  */
  ON_Interval EdgeDomain() const;

  /*
  Returns:
    subdomain of the trim that this segment uses.  This can
    be different than the domain returned by this->Domain().
  */
  ON_Interval TrimDomain() const;


  // m_object_id = id of a brep or curve object in Rhino
  ON_UUID m_object_id; 
  // When the Rhino object is a brep, m_component_index
  // refers to either an edge or a trim.
  ON_COMPONENT_INDEX m_component_index;
  // corresponding domain of the edge - see note below
  ON_Interval m_edge_domain = ON_Interval::EmptyInterval;  
  // corresponding domain of the trim - see note below
  ON_Interval m_trim_domain = ON_Interval::EmptyInterval;   


  // When m_component_index refers to an ON_BrepTrim, there
  // are four domains and 4 classes derived from ON_Curve
  // that play a role in the polyedge segment.  It is possible
  // for all 4 of these domains to be different.
  //
  // "this" ON_PolyEdgeSegment is an ON_ProxyCurve.  The
  // evaluation domain of "this" is 
  //   = this->Domain()
  //   = ON_ProxyCurve::m_this_domain
  //
  // ON_ProxyCurve::m_real_curve points to the curve in the
  // c3 = ON_Brep::m_C3[edge.m_c3i].  "this" is a proxy for some 
  // sub-interval of c3.
  //   = this->ProxyCurveDomain()
  //   = ON_ProxyCurve::m_real_curve_domain
  //
  // The edge, an ON_BrepEdge, is also a proxy based on c3,
  // and the edge's evaluation domain is edge.m_this_domain.  
  // ON_PolyEdgeSegment::m_edge_domain records the increasing
  // subinterval of edge.m_this_domain that corresponds
  // to the portion of c3 "this" is using.
  // 
  // The trim, an ON_BrepTrim, is a proxy based on a curve
  // in ON_Brep::m_C2[].  Some portion of the trim corresponds
  // to the portion of the edge we are using.  m_trim_domain
  // is an increasing, possible subinterval, of the trim's domain
  // ON_BrepTrim::m_this_domain.

  // Runtime helpers
  const void* m_parent_object; // CRhinoBrepObject or CRhinoCurveObject
  const ON_Brep* m_brep;
  const ON_BrepTrim* m_trim;  // 2d trim in m_brep
  const ON_BrepEdge* m_edge;  // 3d edge in m_brep
  const ON_BrepFace* m_face;
  const ON_Surface* m_surface;


private:
  friend class ON_PolyEdgeCurve;
  void ClearEvalCacheHelper();

  // parameter evaluation cache
  double m_t;
  double m_edge_t;
  double m_trim_t;
  double m_srf_uv[2];
  int m_trim_hint;
  int m_edge_hint;

  // surface evaluation cache
  int m_evsrf_hint[2];
  double m_evsrf_uv[2];
  ON_3dPoint m_evsrf_pt;
  ON_3dVector m_evsrf_du;
  ON_3dVector m_evsrf_dv;
  ON_3dVector m_evsrf_duu;
  ON_3dVector m_evsrf_duv;
  ON_3dVector m_evsrf_dvv;
  ON_3dVector m_evsrf_tan;

  void Init();
};

#endif
