// File:	GeomAPI_ExtremaCurveCurve.cxx
// Created:	Fri Mar 18 14:54:40 1994
// Author:	Bruno DUMORTIER
//		<dub@fuegox>


#include <GeomAPI_ExtremaCurveCurve.ixx>

#include <GeomAdaptor_Curve.hxx>
//#include <Extrema_POnCurv.hxx>

#include <Precision.hxx>
#include <GeomAPI_ProjectPointOnCurve.hxx>

//=======================================================================
//function : GeomAPI_ExtremaCurveCurve
//purpose  : 
//=======================================================================

GeomAPI_ExtremaCurveCurve::GeomAPI_ExtremaCurveCurve()
{
  myIsDone = Standard_False;
  myTotalExt = Standard_False;
}


//=======================================================================
//function : GeomAPI_ExtremaCurveCurve
//purpose  : 
//=======================================================================

GeomAPI_ExtremaCurveCurve::GeomAPI_ExtremaCurveCurve
  (const Handle(Geom_Curve)& C1,
   const Handle(Geom_Curve)& C2)
{
  Init(C1,C2);
}


//=======================================================================
//function : GeomAPI_ExtremaCurveCurve
//purpose  : 
//=======================================================================

GeomAPI_ExtremaCurveCurve::GeomAPI_ExtremaCurveCurve
  (const Handle(Geom_Curve)& C1,
   const Handle(Geom_Curve)& C2,
   const Standard_Real       U1min,
   const Standard_Real       U1max,
   const Standard_Real       U2min,
   const Standard_Real       U2max)
{
  Init(C1,C2,U1min,U1max,U2min,U2max);
}


//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::Init
  (const Handle(Geom_Curve)& C1,
   const Handle(Geom_Curve)& C2)
{

  myTotalExt = Standard_False;
  
  Standard_Real Tol = Precision::PConfusion();
  myC1.Load(C1);
  myC2.Load(C2);
  Extrema_ExtCC theExtCC(myC1, myC2, Tol,Tol);
  myExtCC = theExtCC;

  myIsDone = myExtCC.IsDone() && ( myExtCC.NbExt() > 0);

  if ( myIsDone) {

    // evaluate the lower distance and its index;
    
    Standard_Real Dist, DistMin = myExtCC.Value(1);
    myIndex = 1;
    
    for ( Standard_Integer i = 2; i <= myExtCC.NbExt(); i++) {
      Dist = myExtCC.Value(i);
      if ( Dist < DistMin) {
	DistMin = Dist;
	myIndex = i;
      }
    }
  }
}


//=======================================================================
//function : Init
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::Init
  (const Handle(Geom_Curve)& C1,
   const Handle(Geom_Curve)& C2,
   const Standard_Real       U1min,
   const Standard_Real       U1max,
   const Standard_Real       U2min,
   const Standard_Real       U2max)
{
 
  myTotalExt = Standard_False;

  Standard_Real Tol = Precision::PConfusion();
  myC1.Load(C1);
  myC2.Load(C2);
  Extrema_ExtCC theExtCC(myC1,myC2,U1min,U1max,U2min,U2max,Tol,Tol);
  myExtCC = theExtCC;

  myIsDone = myExtCC.IsDone() && ( myExtCC.NbExt() > 0 );

  if ( myIsDone) {

    // evaluate the lower distance and its index;
    
    Standard_Real Dist, DistMin = myExtCC.Value(1);
    myIndex = 1;
    
    for ( Standard_Integer i = 2; i <= myExtCC.NbExt(); i++) {
      Dist = myExtCC.Value(i);
      if ( Dist < DistMin) {
	DistMin = Dist;
	myIndex = i;
      }
    }
  }
}


//=======================================================================
//function : NbExtrema
//purpose  : 
//=======================================================================

Standard_Integer GeomAPI_ExtremaCurveCurve::NbExtrema() const 
{
  if ( myIsDone)
    return myExtCC.NbExt();
  else
    return 0;
}


//=======================================================================
//function : Points
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::Points
  (const Standard_Integer Index,
         gp_Pnt&          P1,
         gp_Pnt&          P2) const 
{
  Standard_OutOfRange_Raise_if( Index < 1 || Index > NbExtrema(),
			       "GeomAPI_ExtremaCurveCurve::Points");

  Extrema_POnCurv PC1, PC2;
  myExtCC.Points(Index,PC1,PC2);

  P1 = PC1.Value();
  P2 = PC2.Value();
}


//=======================================================================
//function : Parameters
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::Parameters
  (const Standard_Integer Index, 
         Standard_Real&   U1,
         Standard_Real&   U2) const 
{
  Standard_OutOfRange_Raise_if( Index < 1 || Index > NbExtrema(),
			       "GeomAPI_ExtremaCurveCurve::Parameters");

  Extrema_POnCurv PC1, PC2;
  myExtCC.Points(Index,PC1,PC2);

  U1 = PC1.Parameter();
  U2 = PC2.Parameter();
}


//=======================================================================
//function : Distance
//purpose  : 
//=======================================================================

Standard_Real GeomAPI_ExtremaCurveCurve::Distance
  (const Standard_Integer Index) const 
{
  Standard_OutOfRange_Raise_if( Index < 1 || Index > NbExtrema(),
			       "GeomAPI_ExtremaCurveCurve::Distance");

  return myExtCC.Value(Index);
}


//=======================================================================
//function : NearestPoints
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::NearestPoints(gp_Pnt& P1, gp_Pnt& P2) const 
{
  StdFail_NotDone_Raise_if
    (!myIsDone, "GeomAPI_ExtremaCurveCurve::NearestPoints");

  Points(myIndex,P1,P2);
}


//=======================================================================
//function : LowerDistanceParameters
//purpose  : 
//=======================================================================

void GeomAPI_ExtremaCurveCurve::LowerDistanceParameters
  (Standard_Real& U1,
   Standard_Real& U2) const 
{
  StdFail_NotDone_Raise_if
    (!myIsDone, "GeomAPI_ExtremaCurveCurve::LowerDistanceParameters");

  Parameters(myIndex,U1,U2);
}


//=======================================================================
//function : LowerDistance
//purpose  : 
//=======================================================================

Standard_Real GeomAPI_ExtremaCurveCurve::LowerDistance() const 
{
  StdFail_NotDone_Raise_if
    (!myIsDone, "GeomAPI_ExtremaCurveCurve::LowerDistance");

  return myExtCC.Value(myIndex);
}


//=======================================================================
//function : Standard_Real
//purpose  : 
//=======================================================================

GeomAPI_ExtremaCurveCurve::operator Standard_Real() const
{
  return LowerDistance();
}


//=======================================================================
//function : Standard_Integer
//purpose  : 
//=======================================================================

GeomAPI_ExtremaCurveCurve::operator Standard_Integer() const
{
  return myExtCC.NbExt();
}


Standard_Boolean GeomAPI_ExtremaCurveCurve::TotalNearestPoints(gp_Pnt& P1,gp_Pnt& P2) 
{

  if(!myTotalExt) {

    TotalPerform();
    myTotalExt = Standard_True;

  }

  if(myIsInfinite) return Standard_False;

  P1 = myTotalPoints[0];
  P2 = myTotalPoints[1];

  return Standard_True;
    
}

Standard_Boolean GeomAPI_ExtremaCurveCurve::TotalLowerDistanceParameters(Quantity_Parameter& U1, 
									 Quantity_Parameter& U2) 
{
  if(!myTotalExt) {

    TotalPerform();
    myTotalExt = Standard_True;

  }

  if(myIsInfinite) return Standard_False;

  U1 = myTotalPars[0];
  U2 = myTotalPars[1];

  return Standard_True;

}

Quantity_Length GeomAPI_ExtremaCurveCurve::TotalLowerDistance() 
{
  if(!myTotalExt) {

    TotalPerform();
    myTotalExt = Standard_True;

  }

  return myTotalDist;

}


void GeomAPI_ExtremaCurveCurve::TotalPerform()

{
//  StdFail_NotDone_Raise_if
//    (!myExtCC.IsDone(), "GeomAPI_ExtremaCurveCurve::TotalPerform");

  Standard_Real u11 = myC1.FirstParameter();
  Standard_Real u12 = myC1.LastParameter();
  Standard_Real u21 = myC2.FirstParameter();
  Standard_Real u22 = myC2.LastParameter();
  
  Standard_Boolean infinite = Precision::IsInfinite(u11) &&
                              Precision::IsInfinite(u12) &&
			      Precision::IsInfinite(u21) &&
			      Precision::IsInfinite(u22);

  myIsInfinite = Standard_False;

  if(infinite && myExtCC.IsParallel()) {
    
    myIsInfinite = Standard_True;

    //calculate distance between any suitable point on C1 and C2
    
    gp_Pnt PonC1 = myC1.Value(0.);
    GeomAPI_ProjectPointOnCurve proj(PonC1, myC2.Curve());
    myTotalDist = proj.LowerDistance();

    return;

  }

  myTotalDist = RealLast();

  if(myIsDone && !myExtCC.IsParallel()) {
    
    Points(myIndex, myTotalPoints[0], myTotalPoints[1]);
    Parameters(myIndex, myTotalPars[0], myTotalPars[1]);
    myTotalDist = myExtCC.Value(myIndex);

    if(myTotalDist <= Precision::Confusion()) return;

  }

  gp_Pnt P11, P12, P21, P22;
  Standard_Real d11, d12, d21, d22;
  myExtCC.TrimmedDistances(d11, d12, d21, d22, P11, P12, P21, P22);
  
  if(myTotalDist > d11) {
    myTotalDist = d11;
    myTotalPoints[0] = P11;
    myTotalPoints[1] = P21;
    myTotalPars[0] = u11;
    myTotalPars[1] = u21;
    
    if(myTotalDist <= Precision::Confusion()) return;
    
  }
  
  if(myTotalDist > d12) {
    myTotalDist = d12;
    myTotalPoints[0] = P11;
    myTotalPoints[1] = P22;
    myTotalPars[0] = u11;
    myTotalPars[1] = u22;
    
    if(myTotalDist <= Precision::Confusion()) return;
    
  }
  
  if(myTotalDist > d21) {
    myTotalDist = d21;
    myTotalPoints[0] = P12;
    myTotalPoints[1] = P21;
    myTotalPars[0] = u12;
    myTotalPars[1] = u21;
    
    if(myTotalDist <= Precision::Confusion()) return;
    
  }
  
  if(myTotalDist > d22) {
    myTotalDist = d22;
    myTotalPoints[0] = P12;
    myTotalPoints[1] = P22;
    myTotalPars[0] = u12;
    myTotalPars[1] = u22;
    
    if(myTotalDist <= Precision::Confusion()) return;
    
  }
 
  // calculate distances between extremities one curve and other curve

  if(!Precision::IsInfinite(u11)) {
    GeomAPI_ProjectPointOnCurve proj(P11, myC2.Curve(), u21, u22);  

    if(proj.NbPoints() > 0) {

      Standard_Real dmin = proj.LowerDistance();
      if(myTotalDist > dmin) {
	myTotalDist = dmin;
	myTotalPoints[0] = P11;
	myTotalPars[0] = u11;
	myTotalPoints[1] = proj.NearestPoint();
	myTotalPars[1] = proj.LowerDistanceParameter();

	if(myTotalDist <= Precision::Confusion()) return;
    
      }
    }
  }

  if(!Precision::IsInfinite(u12)) {
    GeomAPI_ProjectPointOnCurve proj(P12, myC2.Curve(), u21, u22);  

    if(proj.NbPoints() > 0) {

      Standard_Real dmin = proj.LowerDistance();
      if(myTotalDist > dmin) {
	myTotalDist = dmin;
	myTotalPoints[0] = P12;
	myTotalPars[0] = u12;
	myTotalPoints[1] = proj.NearestPoint();
	myTotalPars[1] = proj.LowerDistanceParameter();

	if(myTotalDist <= Precision::Confusion()) return;
    
      }
    }
  }

  if(!Precision::IsInfinite(u21)) {
    GeomAPI_ProjectPointOnCurve proj(P21, myC1.Curve(), u11, u12);  

    if(proj.NbPoints() > 0) {

      Standard_Real dmin = proj.LowerDistance();
      if(myTotalDist > dmin) {
	myTotalDist = dmin;
	myTotalPoints[0] = proj.NearestPoint();
	myTotalPars[0] = proj.LowerDistanceParameter();
	myTotalPoints[1] = P21;
	myTotalPars[1] = u21;

	if(myTotalDist <= Precision::Confusion()) return;
    
      }
    }
  }

  if(!Precision::IsInfinite(u22)) {
    GeomAPI_ProjectPointOnCurve proj(P22, myC1.Curve(), u11, u12);  

    if(proj.NbPoints() > 0) {

      Standard_Real dmin = proj.LowerDistance();
      if(myTotalDist > dmin) {
	myTotalDist = dmin;
	myTotalPoints[0] = proj.NearestPoint();
	myTotalPars[0] = proj.LowerDistanceParameter();
	myTotalPoints[1] = P22;
	myTotalPars[1] = u22;
    
      }
    }
  }


}
