// File:	StepToGeom_MakeTrimmedCurve.cxx
// Created:	Fri Nov  4 10:30:12 1994
// Author:	Frederic MAUPAS
//		<fma@bibox>
//:n9 abv 16.02.99: PRO10107: check parameters on curve with PConfusion()!
//:o6 abv 18.02.99: r0301_db #63077: application of parameter Factor moved
//:p0 abv 19.02.99: management of 'done' flag improved
//:p3 abv 23.02.99: bm4_id_punch_d.stp #1313: shift of parameters on ellipse with R1 < R2
//    abv 09.02.99: S4136: eliminate using BRepAPI::Precision()

#include <StepToGeom_MakeTrimmedCurve.ixx>
#include <StdFail_NotDone.hxx>
#include <gp_Pnt.hxx>
#include <Geom_CartesianPoint.hxx>
#include <Geom_Curve.hxx>
#include <StepGeom_CartesianPoint.hxx>
#include <StepGeom_Line.hxx>
#include <StepGeom_Vector.hxx>
#include <StepGeom_Circle.hxx>
#include <StepGeom_Ellipse.hxx>
#include <StepGeom_Parabola.hxx>
#include <StepGeom_Hyperbola.hxx>
#include <StepGeom_TrimmingSelect.hxx>
#include <StepGeom_HArray1OfTrimmingSelect.hxx>
#include <StepGeom_TrimmedCurve.hxx>
#include <StepToGeom_MakeTrimmedCurve.hxx>
#include <StepToGeom_MakeCartesianPoint.hxx>
#include <StepToGeom_MakeCurve.hxx>
//#include <GeomAPI_ProjectPointOnCurve.hxx>
//#include <BRepAPI.hxx>
#include <ElCLib.hxx>

#include <UnitsMethods.hxx>
#include <Precision.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <StepGeom_Axis2Placement3d.hxx>

// ----------------------------------------------------------------
// ExtractParameter
// ----------------------------------------------------------------
//:o6 abv 18 Feb 99: parameter Factor added
//:p3 abv 23 Feb 99: parameter Shift added

static Standard_Boolean  ExtractParameter
(const Handle(Geom_Curve) &  aGeomCurve,
 const Handle(StepGeom_HArray1OfTrimmingSelect) & TS,
 const Standard_Integer nbSel,
 const Standard_Integer MasterRep,
 const Standard_Real Factor,
 const Standard_Real Shift,
 Standard_Real & aParam)     
{
  Handle(StepGeom_CartesianPoint) aPoint;
  Handle(Geom_CartesianPoint) theGeomPnt;
  Standard_Integer i;
//:S4136  Standard_Real precBrep = BRepAPI::Precision();
  for ( i = 1 ; i <= nbSel ; i++) {
    StepGeom_TrimmingSelect theSel = TS->Value(i);
    if (MasterRep == 2 && theSel.CaseMember() > 0) {
      aParam = Shift + Factor * theSel.ParameterValue();
      return Standard_True;
    }
    else if (MasterRep == 1 && theSel.CaseNumber() > 0) {
      aPoint = theSel.CartesianPoint();
      StepToGeom_MakeCartesianPoint MkPoint(aPoint);
      theGeomPnt = MkPoint.Value();
      gp_Pnt thegpPnt = theGeomPnt->Pnt();
      
      //:S4136: use advanced algorithm
      ShapeAnalysis_Curve sac;
      gp_Pnt p;
      sac.Project ( aGeomCurve, thegpPnt, Precision::Confusion(), p, aParam );
/* //:S4136
      //Trim == natural boundary ?
      if(aGeomCurve->IsKind(STANDARD_TYPE(Geom_BoundedCurve))) {
	Standard_Real frstPar = aGeomCurve->FirstParameter();
	Standard_Real lstPar = aGeomCurve->LastParameter();
	gp_Pnt frstPnt = aGeomCurve->Value(frstPar);
	gp_Pnt lstPnt = aGeomCurve->Value(lstPar);
	if(frstPnt.IsEqual(thegpPnt,precBrep)) {
	  aParam = frstPar;
	  return Standard_True;
	}
	if(lstPnt.IsEqual(thegpPnt,precBrep)) {
	  aParam = lstPar;
	  return Standard_True;
	}
      }
      // Project Point On Curve
      GeomAPI_ProjectPointOnCurve PPOC(thegpPnt, aGeomCurve);
      if (PPOC.NbPoints() == 0) {
	return Standard_False;
      }
      aParam = PPOC.LowerDistanceParameter();
*/
      return Standard_True;
    }
  }
// if the MasterRepresentation is unspecified:
// if a ParameterValue exists, it is prefered

  for ( i = 1 ; i <= nbSel ; i++) {
    StepGeom_TrimmingSelect theSel = TS->Value(i);
    if (theSel.CaseMember() > 0) {
      aParam = Shift + Factor * theSel.ParameterValue();
      
      return Standard_True;
    }
  }
// if no ParameterValue exists, it is created from the CartesianPointValue

  for ( i = 1 ; i <= nbSel ; i++) {
    StepGeom_TrimmingSelect theSel = TS->Value(i);
    if (theSel.CaseNumber() > 0) {
      aPoint = theSel.CartesianPoint();
      StepToGeom_MakeCartesianPoint MkPoint(aPoint);
      theGeomPnt = MkPoint.Value();
      gp_Pnt thegpPnt = theGeomPnt->Pnt();
      // Project Point On Curve
      ShapeAnalysis_Curve sac;
      gp_Pnt p;
      sac.Project ( aGeomCurve, thegpPnt, Precision::Confusion(), p, aParam );
/*
      GeomAPI_ProjectPointOnCurve PPOC(thegpPnt, aGeomCurve);
      if (PPOC.NbPoints() == 0) {
	return Standard_False;
      }
      aParam = PPOC.LowerDistanceParameter();
*/
      return Standard_True;
    }
  }
  return Standard_False;  // I suppose
}


//=============================================================================
// Creation d' une Trimmed Curve de Geom a partir d' une Trimmed Curve de Step
//=============================================================================

StepToGeom_MakeTrimmedCurve::StepToGeom_MakeTrimmedCurve 
(const Handle(StepGeom_TrimmedCurve)& C)
{
  done = Standard_False;
  Standard_Boolean FoundParam1 = Standard_False;
  Standard_Boolean FoundParam2 = Standard_False;
  Standard_Real trim1 = 0.;
  Standard_Real trim2 = 0.;
  Handle(StepGeom_CartesianPoint) TrimCP1, TrimCP2;

  Handle(StepGeom_Curve) theSTEPCurve = C->BasisCurve();
  StepToGeom_MakeCurve MkCurve(theSTEPCurve);
  if ( ! MkCurve.IsDone() ) return;
  Handle(Geom_Curve) theCurve = MkCurve.Value();

  Handle(StepGeom_HArray1OfTrimmingSelect) theTrimSel1 = C->Trim1();
  Handle(StepGeom_HArray1OfTrimmingSelect) theTrimSel2 = C->Trim2();
  Standard_Integer nbSel1 = C->NbTrim1();
  Standard_Integer nbSel2 = C->NbTrim2();
  
  Standard_Integer MasterRep;

  if (C->MasterRepresentation() == StepGeom_tpCartesian) {
    MasterRep = 1;
  }
  else if (C->MasterRepresentation() == StepGeom_tpParameter) {
    MasterRep = 2;
  }
  else {
    MasterRep = 0;
  }
  //gka 18.02.04 analysis for case when MasterRep = .Unspecified 
  //and parameters are specified as CARTESIAN_POINT
  Standard_Boolean isPoint = Standard_False;
  if(!MasterRep || (MasterRep ==2 && nbSel1 >1 && nbSel2 > 1)) {
    Standard_Integer ii =1;
    Standard_Boolean isPoint1 = Standard_False;
    Standard_Boolean isPoint2 = Standard_False;
    for( ; ii <= nbSel1 && !isPoint1; ii++)
      isPoint1 = !(theTrimSel1->Value(ii).CartesianPoint().IsNull());
    for(ii =1 ; ii <= nbSel1 && !isPoint2; ii++)
      isPoint2 = !(theTrimSel1->Value(ii).CartesianPoint().IsNull());
    isPoint = isPoint1 && isPoint2;
  }
  
  //:o6 abv 18 Feb 99: computation of factor moved 
  Standard_Real fact = 1., shift = 0.;
  if (theSTEPCurve->IsKind(STANDARD_TYPE(StepGeom_Line))) {
    Handle(StepGeom_Line) theLine = 
      Handle(StepGeom_Line)::DownCast(theSTEPCurve);
    Handle(StepGeom_Vector) vec = theLine->Dir();
    fact = vec->Magnitude() * UnitsMethods::LengthFactor();
  }
  else if (theSTEPCurve->IsKind(STANDARD_TYPE(StepGeom_Circle)) ||
	   theSTEPCurve->IsKind(STANDARD_TYPE(StepGeom_Ellipse))) {
//    if (trim1 > 2.1*PI || trim2 > 2.1*PI) fact = PI / 180.;
    fact = UnitsMethods::PlaneAngleFactor();
    //:p3 abv 23 Feb 99: shift on pi/2 on ellipse with R1 < R2
    Handle(StepGeom_Ellipse) ellipse = Handle(StepGeom_Ellipse)::DownCast(theSTEPCurve);
    if ( ! ellipse.IsNull() && 
	 ellipse->SemiAxis1() - ellipse->SemiAxis2() < 0. ) 
      shift = PI/2.;

    // skl 04.02.2002 for OCC133: we can not make TrimmedCurve if
    // there is no X-direction in StepGeom_Axis2Placement3d
    Handle(StepGeom_Conic) conic = Handle(StepGeom_Conic)::DownCast(theSTEPCurve);
    // CKY 6-FEB-2004 for Airbus-MedialAxis : 
    // this restriction does not apply for trimming by POINTS 
    if(!conic.IsNull() && MasterRep != 1 ) {
      StepGeom_Axis2Placement a2p = conic->Position();
      if(a2p.CaseNum(a2p.Value())==2) {
        if( !a2p.Axis2Placement3d()->HasRefDirection() ) {
          ////gka 18.02.04 analysis for case when MasterRep = .Unspecified 
          //and parameters are specified as CARTESIAN_POINT
          if(isPoint /*&& !MasterRep*/)
            MasterRep =1;
          else {
            if ( C->SenseAgreement() ) 
              theTrimmedCurve = new Geom_TrimmedCurve(theCurve, 0., 2*PI,
                                                      C->SenseAgreement());
            else 
              theTrimmedCurve = new Geom_TrimmedCurve(theCurve, 2*PI, 0.,
                                                      C->SenseAgreement());
            done = Standard_True;
            return;
          }
        }
      }
    }
  }

  FoundParam1 = ExtractParameter(theCurve, theTrimSel1, nbSel1, MasterRep, fact, shift, trim1);
  FoundParam2 = ExtractParameter(theCurve, theTrimSel2, nbSel2, MasterRep, fact, shift, trim2);

  if (FoundParam1 && FoundParam2) {
    Standard_Real cf = theCurve->FirstParameter();
    Standard_Real cl = theCurve->LastParameter();
    //: abv 09.04.99: S4136: bm2_ug_t4-B.stp #70610: protect against OutOfRange
    if ( ! theCurve->IsPeriodic() ) {
      if ( trim1 < cf ) trim1 = cf;
      else if ( trim1 > cl ) trim1 = cl;
      if ( trim2 < cf ) trim2 = cf;
      else if ( trim2 > cl ) trim2 = cl;
    }
    if (Abs(trim1 - trim2) < Precision::PConfusion()) {
      if (theCurve->IsPeriodic()) {
	ElCLib::AdjustPeriodic(cf,cl,Precision::PConfusion(),trim1,trim2);
      }
      else if (theCurve->IsClosed()) {
	if (Abs(trim1 - cf) < Precision::PConfusion()) {
	  trim2 = trim2 + cl;
	}
	else {
	  trim1 = trim1 - cl;
	}
      }
      else {
#ifdef DEBUG
	cout << "Trimming Failed" << endl;
#endif
	done = Standard_False;
	return;
      }
    }
//  CKY 16-DEC-1997 : USA60035 le texte de Part42 parle de degres
//    mais des systemes ecrivent en radians. Exploiter UnitsMethods
//:o6    trim1 = trim1 * fact;
//:o6    trim2 = trim2 * fact;
    if ( C->SenseAgreement() ) 
      theTrimmedCurve = new Geom_TrimmedCurve(theCurve, trim1, trim2,
					      C->SenseAgreement());
    else //:abv 29.09.00 PRO20362: reverse parameters in case of reversed curve
      theTrimmedCurve = new Geom_TrimmedCurve(theCurve, trim2, trim1,
					      C->SenseAgreement());
    done = Standard_True;
  }
  else {
    done = Standard_False;
  }
}

//=============================================================================
// renvoi des valeurs
//=============================================================================

const Handle(Geom_TrimmedCurve) &
      StepToGeom_MakeTrimmedCurve::Value() const
{
  StdFail_NotDone_Raise_if(!done == Standard_True,"");
  return theTrimmedCurve;
}
