// File Extrema_ExtElCS.cxx

//  Modified by skv - Thu Jul  7 14:37:05 2005 OCC9134

#include <Extrema_ExtElCS.ixx>
#include <Extrema_ExtPElS.hxx>
#include <Extrema_ExtPElC.hxx>
#include <Extrema_ExtElC.hxx>
#include <Extrema_POnCurv.hxx>
#include <Standard_NotImplemented.hxx>
#include <StdFail_InfiniteSolutions.hxx>
#include <Precision.hxx>
#include <ElSLib.hxx>
#include <ElCLib.hxx>
#include <gp_Vec.hxx>


Extrema_ExtElCS::Extrema_ExtElCS() 
{
  myDone = Standard_False;
  myIsPar = Standard_False;
}


Extrema_ExtElCS::Extrema_ExtElCS(const gp_Lin& C,
				 const gp_Pln& S)
{
  Perform(C, S);
}



void Extrema_ExtElCS::Perform(const gp_Lin& C,
			      const gp_Pln& S)
{
  myDone = Standard_True;
  myIsPar = Standard_False;

  if (C.Direction().IsNormal(S.Axis().Direction(), 
			     Precision::Angular())) {
    myValue = new TColStd_HArray1OfReal(1, 1);
    myValue->SetValue(1, S.Distance(C));
    myIsPar = Standard_True;
  }
  else {
    myNbExt = 0;
  }
  
}


Extrema_ExtElCS::Extrema_ExtElCS(const gp_Lin& C,
				 const gp_Cylinder& S)
{
  Perform(C, S);
}



void Extrema_ExtElCS::Perform(const gp_Lin& C,
			      const gp_Cylinder& S)
{
  myDone = Standard_False;
  myNbExt = 0;
  myIsPar = Standard_False;

  gp_Ax3 Pos = S.Position();
#ifdef DEB
  gp_Pnt O = Pos.Location();
#else
  Pos.Location();
#endif
  Standard_Real radius = S.Radius();
  Extrema_ExtElC Extrem(gp_Lin(Pos.Axis()), C, Precision::Angular());
  if (Extrem.IsParallel()) {
    myValue = new TColStd_HArray1OfReal(1, 1);
    myValue->SetValue(1, Abs(Extrem.Value(1)-radius));
    myDone = Standard_True;
    myIsPar = Standard_True;
  }
  else {
    Standard_Integer i;
    if (Extrem.IsDone()) {
      Extrema_POnCurv myPOnC1, myPOnC2;
      Extrem.Points(1, myPOnC1, myPOnC2);
      gp_Pnt PC = myPOnC2.Value();

      if ((gp_Lin(Pos.Axis())).Contains(PC, Precision::Confusion())) {
	gp_Dir D = C.Direction();
	gp_Vec Dp(-D.Dot(Pos.YDirection()), D.Dot(Pos.XDirection()), 0.0);
	Standard_Real U, V;
	gp_Pnt P1(PC.Translated(radius*Dp));
	gp_Pnt P2(PC.Translated(-radius*Dp));
	
	myNbExt = 2;
	myValue = new TColStd_HArray1OfReal(1, myNbExt);
	myPoint1 = new Extrema_HArray1OfPOnCurv(1, myNbExt);
	myPoint2 = new Extrema_HArray1OfPOnSurf(1, myNbExt);
	ElSLib::CylinderParameters(Pos, radius, P1, U, V);
	Extrema_POnSurf P1S(U, V, P1);
	ElSLib::CylinderParameters(Pos, radius, P2, U, V);
	Extrema_POnSurf P2S(U, V, P2);
	myValue->SetValue(1, PC.Distance(P1));
	myValue->SetValue(2, PC.Distance(P2));
	myPoint1->SetValue(1, myPOnC2);
	myPoint1->SetValue(2, myPOnC2);
	myPoint2->SetValue(1, P1S);
	myPoint2->SetValue(2, P2S);
      }
      else {
	Extrema_ExtPElS ExPS(myPOnC2.Value(), S, Precision::Confusion());
	if (ExPS.IsDone()) {
	  myNbExt = ExPS.NbExt();
	  myValue = new TColStd_HArray1OfReal(1, myNbExt);
	  myPoint1 = new Extrema_HArray1OfPOnCurv(1, myNbExt);
	  myPoint2 = new Extrema_HArray1OfPOnSurf(1, myNbExt);
	  for (i = 1; i <= myNbExt; i++) {
	    myPoint1->SetValue(i, myPOnC2);
	    myPoint2->SetValue(i, ExPS.Point(i));
	    myValue->SetValue(i,(myPOnC2.Value()).Distance(ExPS.Point(i).Value()));
	  }
	}
      }
      myDone = Standard_True;
    }
  }

}



Extrema_ExtElCS::Extrema_ExtElCS(const gp_Lin& C,
				 const gp_Cone& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Lin& C,
//			      const gp_Cone& S)
void Extrema_ExtElCS::Perform(const gp_Lin& ,
			      const gp_Cone& )
{
  Standard_NotImplemented::Raise();

}



Extrema_ExtElCS::Extrema_ExtElCS(const gp_Lin& C,
				 const gp_Sphere& S)
{
  Perform(C, S);
}



void Extrema_ExtElCS::Perform(const gp_Lin& C,
			      const gp_Sphere& S)
{
  myDone = Standard_False;
  myNbExt = 0;
  myIsPar = Standard_False;

  gp_Pnt O = S.Location();

  Extrema_ExtPElC Extrem(O, C, Precision::Angular(), RealFirst(), RealLast());

  Standard_Integer i;
  if (Extrem.IsDone()) {
    Extrema_POnCurv myPOnC1 =  Extrem.Point(1);
    Extrema_ExtPElS ExPS(myPOnC1.Value(), S, Precision::Confusion());
    if (ExPS.IsDone()) {
      myNbExt = ExPS.NbExt();
      myValue = new TColStd_HArray1OfReal(1, myNbExt);
      myPoint1 = new Extrema_HArray1OfPOnCurv(1, myNbExt);
      myPoint2 = new Extrema_HArray1OfPOnSurf(1, myNbExt);
      for (i = 1; i <= myNbExt; i++) {
	myPoint1->SetValue(i, myPOnC1);
	myPoint2->SetValue(i, ExPS.Point(i));
	myValue->SetValue(i,(myPOnC1.Value()).Distance(ExPS.Point(i).Value()));
	myDone = Standard_True;
      }
    }
  }
}


Extrema_ExtElCS::Extrema_ExtElCS(const gp_Lin& C,
				 const gp_Torus& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Lin& C,
//			      const gp_Torus& S)
void Extrema_ExtElCS::Perform(const gp_Lin& ,
			      const gp_Torus& )
{
  Standard_NotImplemented::Raise();

}


//        Circle-?

Extrema_ExtElCS::Extrema_ExtElCS(const gp_Circ& C,
				 const gp_Pln& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Circ& C,
//			      const gp_Pln& S)
void Extrema_ExtElCS::Perform(const gp_Circ& ,
			      const gp_Pln& )
{
  Standard_NotImplemented::Raise();

}



Extrema_ExtElCS::Extrema_ExtElCS(const gp_Circ& C,
				 const gp_Cylinder& S)
{
  Perform(C, S);
}



//  Modified by skv - Thu Jul  7 14:37:05 2005 OCC9134 Begin
// Implementation of the method.
void Extrema_ExtElCS::Perform(const gp_Circ& C,
			      const gp_Cylinder& S)
{
  myDone  = Standard_False;
  myIsPar = Standard_False;
  myNbExt = 0;

  // Get an axis line of the cylinder.
  gp_Lin anAxis(S.Axis());

  // Compute extrema between the circle and the line.
  Extrema_ExtElC anExtC(anAxis, C, 0.);

  if (anExtC.IsDone()) {
    if (anExtC.IsParallel()) {
      myIsPar =     Standard_True;
      myValue = new TColStd_HArray1OfReal(1, 1);
      myValue->SetValue(1, Abs(anExtC.Value(1) - S.Radius()));
    } else {
      Standard_Integer aNbExt   = anExtC.NbExt();
      gp_Pnt           aCenter  = C.Location();
      Standard_Integer i;
      Standard_Integer aCurI    = 1;
      Standard_Real    aTolConf = Precision::Confusion();
      Standard_Real    aCylRad  = S.Radius();

      // Compute the extremas.
      myNbExt  =     2*aNbExt;
      myValue  = new TColStd_HArray1OfReal(1, myNbExt);
      myPoint1 = new Extrema_HArray1OfPOnCurv(1, myNbExt);
      myPoint2 = new Extrema_HArray1OfPOnSurf(1, myNbExt);

      for (i = 1; i <= aNbExt; i++) {
	Extrema_POnCurv aPOnAxis;
	Extrema_POnCurv aPOnCirc;
	Standard_Real   aValue = anExtC.Value(i);

	anExtC.Points(i, aPOnAxis, aPOnCirc);

	if (aValue <= aTolConf || aCenter.IsEqual(aPOnAxis.Value(), aTolConf)) {
	  myNbExt -= 2;
	  continue;
	}

	gp_Dir           aDir(aPOnAxis.Value().XYZ().
			      Subtracted(aPOnCirc.Value().XYZ()));
	Standard_Real    aShift[2] = { aValue + aCylRad, aValue - aCylRad };
	Standard_Integer j;

	for (j = 0; j < 2; j++) {
	  gp_Vec aVec(aDir);
	  gp_Pnt aPntOnCyl;

	  aVec.Multiply(aShift[j]);
	  aPntOnCyl = aPOnCirc.Value().Translated(aVec);

	  Standard_Real aU;
	  Standard_Real aV;

	  ElSLib::Parameters(S, aPntOnCyl, aU, aV);

	  Extrema_POnSurf aPOnSurf(aU, aV, aPntOnCyl);

	  myPoint1->SetValue(aCurI, aPOnCirc);
	  myPoint2->SetValue(aCurI, aPOnSurf);
	  myValue->SetValue(aCurI++, Abs(aShift[j]));
	}
      }
    }

    myDone = Standard_True;
  }
}
//  Modified by skv - Thu Jul  7 14:37:05 2005 OCC9134 End



Extrema_ExtElCS::Extrema_ExtElCS(const gp_Circ& C,
				 const gp_Cone& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Circ& C,
//			 const gp_Cone& S)
void Extrema_ExtElCS::Perform(const gp_Circ& ,
			 const gp_Cone& )
{
  Standard_NotImplemented::Raise();

}



Extrema_ExtElCS::Extrema_ExtElCS(const gp_Circ& C,
				 const gp_Sphere& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Circ& C,
//			      const gp_Sphere& S)
void Extrema_ExtElCS::Perform(const gp_Circ& ,
			      const gp_Sphere& )
{
  Standard_NotImplemented::Raise();

}

Extrema_ExtElCS::Extrema_ExtElCS(const gp_Circ& C,
				 const gp_Torus& S)
{
  Perform(C, S);
}



//void Extrema_ExtElCS::Perform(const gp_Circ& C,
//			      const gp_Torus& S)
void Extrema_ExtElCS::Perform(const gp_Circ& ,
			      const gp_Torus& )
{
  Standard_NotImplemented::Raise();

}

Extrema_ExtElCS::Extrema_ExtElCS(const gp_Hypr& C,
				 const gp_Pln& S)
{
  Perform(C, S);
}



void Extrema_ExtElCS::Perform(const gp_Hypr& C,
			      const gp_Pln& S)
{
  myDone = Standard_True;
  myIsPar = Standard_False;

  gp_Ax2 Pos = C.Position();
  gp_Dir NHypr = Pos.Direction();
  gp_Dir NPln = S.Axis().Direction();

  if (NHypr.IsParallel(NPln, Precision::Angular())) {

    myValue = new TColStd_HArray1OfReal(1, 1);
    myValue->SetValue(1, S.Distance(C.Location()));
    myIsPar = Standard_True;

  }
  else {

    gp_Dir XDir = Pos.XDirection();
    gp_Dir YDir = Pos.YDirection();

    Standard_Real A = C.MinorRadius()*(NPln.Dot(YDir)); 
    Standard_Real B = C.MajorRadius()*(NPln.Dot(XDir)); 

    if(Abs(B) > Abs(A)) {
      Standard_Real T = -0.5 * Log((A+B)/(B-A));
      gp_Pnt Ph = ElCLib::HyperbolaValue(T, Pos, C.MajorRadius(), C.MinorRadius());
      Extrema_POnCurv PC(T, Ph);
      myPoint1 = new Extrema_HArray1OfPOnCurv(1,1);
      myPoint1->SetValue(1, PC);

      Standard_Real val = S.Distance(Ph);
      myValue = new TColStd_HArray1OfReal(1, 1);
      myValue->SetValue(1, val);

      Standard_Real U, V;
      ElSLib::PlaneParameters(S.Position(), Ph, U, V);
      gp_Pnt Pp = ElSLib::PlaneValue(U, V, S.Position());
      Extrema_POnSurf PS(U, V, Pp);
      myPoint2 = new Extrema_HArray1OfPOnSurf(1,1);
      myPoint2->SetValue(1, PS);

      myNbExt = 1;
    }
    else {
      myNbExt = 0;
    }

  }
  
}


Standard_Boolean Extrema_ExtElCS::IsDone() const
{
  return myDone;
}


Standard_Integer Extrema_ExtElCS::NbExt() const
{
  if (myIsPar) StdFail_InfiniteSolutions::Raise();
  return myNbExt;
}

Standard_Real Extrema_ExtElCS::Value(const Standard_Integer N) const
{
  if (myIsPar && N != 1) StdFail_InfiniteSolutions::Raise();
  return myValue->Value(N);
}


void Extrema_ExtElCS::Points(const Standard_Integer N,
			     Extrema_POnCurv& P1,
			     Extrema_POnSurf& P2) const
{
  if (myIsPar) StdFail_InfiniteSolutions::Raise();
  P1 = myPoint1->Value(N);
  P2 = myPoint2->Value(N);
}


Standard_Boolean Extrema_ExtElCS::IsParallel() const
{
  return myIsPar;
}
