// Created on: 1992-04-03
// Created by: Isabelle GRIGNON
// Copyright (c) 1992-1999 Matra Datavision
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _IntWalk_PWalking_HeaderFile
#define _IntWalk_PWalking_HeaderFile

#include <Adaptor3d_Surface.hxx>
#include <gp_Dir.hxx>
#include <IntImp_ConstIsoparametric.hxx>
#include <IntSurf_PntOn2S.hxx>
#include <gp_Dir2d.hxx>
#include <IntWalk_TheInt2S.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <IntWalk_StatusDeflection.hxx>

class gp_Pnt;

//! This class implements an algorithm to determine the
//! intersection between 2 parametrized surfaces, marching from
//! a starting point. The intersection line
//! starts and ends on the natural surface's  boundaries .
class IntWalk_PWalking
{
public:
  DEFINE_STANDARD_ALLOC

  //! Constructor used to set the data to compute intersection
  //! lines between Caro1 and Caro2.
  //! Deflection is the maximum deflection admitted between two
  //! consecutive points on the resulting polyline.
  //! TolTangency is the tolerance to find a tangent point.
  //! Func is the criterion which has to be evaluated at each
  //! solution point (each point of the line).
  //! It is necessary to call the Perform method to compute
  //! the intersection lines.
  //! The line found starts at a point on or in 2 natural domains
  //! of surfaces. It can be closed in the
  //! standard case if it is open it stops and begins at the
  //! border of one of the domains. If an open line
  //! stops at the middle of a domain, one stops at the tangent point.
  //! Epsilon is SquareTolerance of points confusion.
  Standard_EXPORT IntWalk_PWalking(const Handle(Adaptor3d_Surface)& Caro1,
                                   const Handle(Adaptor3d_Surface)& Caro2,
                                   const Standard_Real              TolTangency,
                                   const Standard_Real              Epsilon,
                                   const Standard_Real              Deflection,
                                   const Standard_Real              Increment);

  //! Returns the intersection line containing the exact
  //! point Poin. This line is a polygonal line.
  //! Deflection is the maximum deflection admitted between two
  //! consecutive points on the resulting polyline.
  //! TolTangency is the tolerance to find a tangent point.
  //! Func is the criterion which has to be evaluated at each
  //! solution point (each point of the line).
  //! The line found starts at a point on or in 2 natural domains
  //! of surfaces. It can be closed in the
  //! standard case if it is open it stops and begins at the
  //! border of one of the domains. If an open line
  //! stops at the middle of a domain, one stops at the tangent point.
  //! Epsilon is SquareTolerance of points confusion.
  Standard_EXPORT IntWalk_PWalking(const Handle(Adaptor3d_Surface)& Caro1,
                                   const Handle(Adaptor3d_Surface)& Caro2,
                                   const Standard_Real              TolTangency,
                                   const Standard_Real              Epsilon,
                                   const Standard_Real              Deflection,
                                   const Standard_Real              Increment,
                                   const Standard_Real              U1,
                                   const Standard_Real              V1,
                                   const Standard_Real              U2,
                                   const Standard_Real              V2);

  //! calculate the line of intersection
  Standard_EXPORT void Perform(const TColStd_Array1OfReal& ParDep);

  //! calculate the line of intersection. The regulation
  //! of steps is done using min and max values on u and
  //! v.  (if this data is not presented as in the
  //! previous method, the initial steps are calculated
  //! starting from min and max uv of faces).
  Standard_EXPORT void Perform(const TColStd_Array1OfReal& ParDep,
                               const Standard_Real         u1min,
                               const Standard_Real         v1min,
                               const Standard_Real         u2min,
                               const Standard_Real         v2min,
                               const Standard_Real         u1max,
                               const Standard_Real         v1max,
                               const Standard_Real         u2max,
                               const Standard_Real         v2max);

  //! calculate the first point of a line of intersection
  Standard_EXPORT Standard_Boolean PerformFirstPoint(const TColStd_Array1OfReal& ParDep,
                                                     IntSurf_PntOn2S&            FirstPoint);

  //! Returns true if the calculus was successful.
  Standard_Boolean IsDone() const;

  //! Returns the number of points of the resulting polyline.
  //! An exception is raised if IsDone returns False.
  Standard_Integer NbPoints() const;

  //! Returns the point of range Index on the polyline.
  //! An exception is raised if IsDone returns False.
  //! An exception is raised if Index<=0 or Index>NbPoints.
  const IntSurf_PntOn2S& Value(const Standard_Integer Index) const;

  const Handle(IntSurf_LineOn2S)& Line() const;

  //! Returns True if the surface are tangent at the first point
  //! of the line.
  //! An exception is raised if IsDone returns False.
  Standard_Boolean TangentAtFirst() const;

  //! Returns true if the surface are tangent at the last point
  //! of the line.
  //! An exception is raised if IsDone returns False.
  Standard_Boolean TangentAtLast() const;

  //! Returns True if the line is closed.
  //! An exception is raised if IsDone returns False.
  Standard_Boolean IsClosed() const;

  const gp_Dir& TangentAtLine(Standard_Integer& Index) const;

  Standard_EXPORT IntWalk_StatusDeflection TestDeflection(const IntImp_ConstIsoparametric ChoixIso,
                                                          const IntWalk_StatusDeflection theStatus);

  Standard_EXPORT Standard_Boolean TestArret(const Standard_Boolean     DejaReparti,
                                             TColStd_Array1OfReal&      Param,
                                             IntImp_ConstIsoparametric& ChoixIso);

  Standard_EXPORT void RepartirOuDiviser(Standard_Boolean&          DejaReparti,
                                         IntImp_ConstIsoparametric& ChoixIso,
                                         Standard_Boolean&          Arrive);

  //! Inserts thePOn2S in the end of line
  void AddAPoint(const IntSurf_PntOn2S& thePOn2S);

  //! Removes point with index theIndex from line.
  //! If theIndex is greater than the number of points in line
  //! then the last point will be removed.
  //! theIndex must be started with 1.
  void RemoveAPoint(const Standard_Integer theIndex)
  {
    const Standard_Integer anIdx = Min(theIndex, line->NbPoints());

    if (anIdx < 1)
      return;

    if (anIdx <= myTangentIdx)
    {
      myTangentIdx--;

      if (myTangentIdx < 1)
        myTangentIdx = 1;
    }

    line->RemovePoint(anIdx);
  }

  Standard_EXPORT Standard_Boolean PutToBoundary(const Handle(Adaptor3d_Surface)& theASurf1,
                                                 const Handle(Adaptor3d_Surface)& theASurf2);

  Standard_EXPORT Standard_Boolean SeekAdditionalPoints(const Handle(Adaptor3d_Surface)& theASurf1,
                                                        const Handle(Adaptor3d_Surface)& theASurf2,
                                                        const Standard_Integer theMinNbPoints);

  Standard_Real MaxStep(Standard_Integer theIndex)
  {
    Standard_OutOfRange_Raise_if((theIndex < 0) || (theIndex > 3),
                                 "IntWalk_PWalking::MaxStep() - index is out of range");
    return pasInit[theIndex];
  }

protected:
  Standard_EXPORT void ComputePasInit(const Standard_Real theDeltaU1,
                                      const Standard_Real theDeltaV1,
                                      const Standard_Real theDeltaU2,
                                      const Standard_Real theDeltaV2);

  //! Uses Gradient method in order to find intersection point between the given surfaces
  //! Arrays theInit (initial point to be precise) and theStep0 (steps-array) must contain
  //! four items and must be filled strictly in following order:
  //! {U-parameter on S1, V-parameter on S1, U-parameter on S2, V-parameter on S2}
  Standard_EXPORT Standard_Boolean
    DistanceMinimizeByGradient(const Handle(Adaptor3d_Surface)& theASurf1,
                               const Handle(Adaptor3d_Surface)& theASurf2,
                               TColStd_Array1OfReal&            theInit,
                               const Standard_Real*             theStep0 = 0);

  //! Finds the point on theASurf which is the nearest point to theP0.
  //! theU0 and theV0 must be initialized (before calling the method) by initial
  //! parameters on theASurf. Their values are changed while algorithm being launched.
  //! Array theStep0 (steps-array) must contain two items and must be filled strictly in following
  //! order: {U-parameter, V-parameter}
  Standard_EXPORT Standard_Boolean
    DistanceMinimizeByExtrema(const Handle(Adaptor3d_Surface)& theASurf,
                              const gp_Pnt&                    theP0,
                              Standard_Real&                   theU0,
                              Standard_Real&                   theV0,
                              const Standard_Real*             theStep0 = 0);

  //! Searches an intersection point which lies on the some surface boundary.
  //! Found point (in case of successful result) is added in the line.
  //! theU1, theV1, theU2 and theV2 parameters are initial parameters in
  //! for used numeric algorithms. If isTheFirst == TRUE then
  //! a point on theASurf1 is searched. Otherwise, the point on theASurf2 is searched.
  //!
  //! ATTENTION!!!
  //!   This method can delete some points from the curve if it is necessary
  //!   (in order to obtain correct result after insertion).
  //!   Returns TRUE in case of success adding (i.e. can return FALSE even after
  //!   removing some points).
  Standard_EXPORT Standard_Boolean SeekPointOnBoundary(const Handle(Adaptor3d_Surface)& theASurf1,
                                                       const Handle(Adaptor3d_Surface)& theASurf2,
                                                       const Standard_Real              theU1,
                                                       const Standard_Real              theV1,
                                                       const Standard_Real              theU2,
                                                       const Standard_Real              theV2,
                                                       const Standard_Boolean           isTheFirst);

  // Method to handle single singular point. Sub-method in SeekPointOnBoundary.
  Standard_EXPORT Standard_Boolean
    HandleSingleSingularPoint(const Handle(Adaptor3d_Surface)& theASurf1,
                              const Handle(Adaptor3d_Surface)& theASurf2,
                              const Standard_Real              the3DTol,
                              TColStd_Array1OfReal&            thePnt);

  Standard_EXPORT Standard_Boolean
    ExtendLineInCommonZone(const IntImp_ConstIsoparametric theChoixIso,
                           const Standard_Boolean          theDirectionFlag);

private:
  Standard_Boolean         done;
  Handle(IntSurf_LineOn2S) line;
  Standard_Boolean         close;
  Standard_Boolean         tgfirst;
  Standard_Boolean         tglast;

  //! Index of point on the surface boundary.
  //! It is used for transition computation
  Standard_Integer myTangentIdx;

  //! Tangent to WLine in the point with index myTangentIdx
  gp_Dir tgdir;

  Standard_Real             fleche;
  Standard_Real             pasMax;
  Standard_Real             tolconf;
  Standard_Real             myTolTang;
  Standard_Real             pasuv[4];
  Standard_Real             myStepMin[4];
  Standard_Real             pasSav[4];
  Standard_Real             pasInit[4];
  Standard_Real             Um1;
  Standard_Real             UM1;
  Standard_Real             Vm1;
  Standard_Real             VM1;
  Standard_Real             Um2;
  Standard_Real             UM2;
  Standard_Real             Vm2;
  Standard_Real             VM2;
  Standard_Real             ResoU1;
  Standard_Real             ResoU2;
  Standard_Real             ResoV1;
  Standard_Real             ResoV2;
  Standard_Integer          sensCheminement;
  IntImp_ConstIsoparametric choixIsoSav;
  IntSurf_PntOn2S           previousPoint;
  Standard_Boolean          previoustg;
  gp_Dir                    previousd;
  gp_Dir2d                  previousd1;
  gp_Dir2d                  previousd2;
  gp_Dir2d                  firstd1;
  gp_Dir2d                  firstd2;
  IntWalk_TheInt2S          myIntersectionOn2S;
  Standard_Integer          STATIC_BLOCAGE_SUR_PAS_TROP_GRAND;
  Standard_Integer          STATIC_PRECEDENT_INFLEXION;
};

#include <IntWalk_PWalking.lxx>

#endif // _IntWalk_PWalking_HeaderFile
