// Copyright (c) 1995-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.

void Blend_CSWalking::InternalPerform(Blend_CSFunction& Func,
//				    Blend_CSFuncInv& FuncInv,
				    math_Vector& sol,
				    const Standard_Real Bound)
{

  Standard_Real stepw = pasmax;
  Standard_Real parprec = param;

  Blend_Status State;
  TopAbs_State situ;
  Standard_Real w,U,V;
  Standard_Integer nbarc;
  Standard_Integer  Index = 0;
  Standard_Boolean Isvtx = Standard_False;
  Standard_Integer Nbvar = Func.NbVariables();
  Standard_Boolean Arrive,recad,echecrecad;
  gp_Pnt2d p2d;
//  math_Vector tolerance(1,3),infbound(1,3),supbound(1,3),parinit(1,3);
//  math_Vector solrst(1,3);
  math_Vector tolerance(1,Nbvar),infbound(1,Nbvar),supbound(1,Nbvar),
              parinit(1,Nbvar);
  math_Vector solrst(1,Nbvar);
  TheVertex Vtx;
  TheExtremity Exts,Extc;

  //IntSurf_Transition Tline,Tarc;

  Func.GetTolerance(tolerance,tolpoint3d);
  Func.GetBounds(infbound,supbound);

  math_FunctionSetRoot rsnld(Func,tolerance,30);
  parinit = sol;

  param = parprec + sens*stepw;
  Arrive = (sens *(param - Bound) > 0.) ;

//  if (Arrive) {
//    line->Clear();
//  }

  while (!Arrive) {

    Func.Set(param);
    rsnld.Perform(Func,parinit,infbound,supbound);

    if (!rsnld.IsDone()) {
      State = Blend_StepTooLarge;
    }
    else {
      rsnld.Root(sol);

//      situ1 = TheTopolTool::Classify(surf1,gp_Pnt2d(sol(1),sol(2)),
//				     Max(tolerance(1),tolerance(2)));
//      situ2 = TheTopolTool::Classify(surf2,gp_Pnt2d(sol(3),sol(4)),
//				     Max(tolerance(3),tolerance(4)));
/*
      situ = domain->Classify(gp_Pnt2d(sol(1),sol(2)),
			      Min(tolerance(1),tolerance(2)));
*/
      situ = domain->Classify(Func.Pnt2d(),
			      Min(tolerance(1),tolerance(2)));

      w = Bound;
      recad = Standard_False;
      echecrecad = Standard_False;

      if (situ == TopAbs_OUT || situ == TopAbs_ON) {
	// pb inverse sur surf
//	recad = Recadre(FuncInv,sol,solrst,Index,Isvtx,Vtx);
	Isvtx = Standard_False; // en attendant Recadre
	if (recad) {
	  w = solrst(2);
	}
	else {
	  echecrecad = Standard_True;
	}
      }
      if (!echecrecad) {
	if (recad) {

	  // sol sur surf
	  State = Blend_OnRst1;
	  param = w;
	  domain->Init();
	  nbarc = 1;
	  while (nbarc < Index) {
	    nbarc++;
	    domain->Next();
	  }
	  p2d = TheArcTool::Value(domain->Value(),solrst(1));
	  sol(1) = p2d.X();
	  sol(2) = p2d.Y();
	  sol(3) = solrst(3);
	  Func.Set(param);
	}
	else {
	  State = Blend_OK;
	}
	State = TestArret(Func,sol,Standard_True,State);
      }
      else {
	// Echec recadrage. On sort avec PointsConfondus
	State = Blend_SamePoints;
      }
    }

    switch (State) {
    case Blend_OK :
      {
#ifdef OCCT_DEBUG
	if (Blend_GettraceDRAWSECT()){
	  Drawsect(surf,curv,param,Func);
	}
#endif
	  
	// Mettre a jour la ligne.
	if (sens>0.) {
	  line->Append(previousP);
	}
	else {
	  line->Prepend(previousP);
	}
	parinit = sol;
	parprec = param;

	if (param == Bound) {
	  Arrive = Standard_True;
/*
	  Exts.SetValue(previousP.PointOnS(),sol(1),sol(2),tolesp);
	  Extc.SetValue(previousP.PointOnC(),sol(3),tolesp);
*/
	  previousP.ParametersOnS(U,V);
	  Exts.SetValue(previousP.PointOnS(),U,V, 
			previousP.Parameter(),tolpoint3d);
	  Extc.SetValue(previousP.PointOnC(),previousP.ParameterOnC(),
			previousP.Parameter(),tolpoint3d);
	  // Indiquer que fin sur Bound.
	}
	else {
	  param = param + sens*stepw;
	  if (sens*(param - Bound) > - tolgui) {
	    param = Bound;
	  }
	}
	
      }
      break;
      
    case Blend_StepTooLarge :
      {
	stepw = stepw/2.;
	if (Abs(stepw) < tolgui) {
/*
	  Exts.SetValue(previousP.PointOnS(),sol(1),sol(2),tolesp);
	  Extc.SetValue(previousP.PointOnC(),sol(3),sol(4),tolesp);
*/
	  previousP.ParametersOnS(U,V);
	  Exts.SetValue(previousP.PointOnS(),U,V,
			previousP.Parameter(),tolpoint3d);
	  Extc.SetValue(previousP.PointOnC(),previousP.ParameterOnC(),
			previousP.Parameter(),tolpoint3d);
	  Arrive = Standard_True;
	  if (line->NbPoints()>=2) {
	    // Indiquer qu on s arrete en cours de cheminement
	  }
//	  else {
//	    line->Clear();
//	  }
	}
	else {
	  param = parprec + sens*stepw;  // on ne risque pas de depasser Bound.
	}
      }
      break;
      
    case Blend_StepTooSmall :
      {
#ifdef OCCT_DEBUG
	if (Blend_GettraceDRAWSECT()){
	  Drawsect(surf,curv,param,Func);
	}
#endif
	// Mettre a jour la ligne.
	if (sens>0.) {
	  line->Append(previousP);
	}
	else {
	  line->Prepend(previousP);
	}
	parinit = sol;
	parprec = param;

	stepw = Min(1.5*stepw,pasmax);
	if (param == Bound) {
	  Arrive = Standard_True;
/*
	  Exts.SetValue(previousP.PointOnS(),sol(1),sol(2),tolesp);
	  Extc.SetValue(previousP.PointOnC(),sol(3),tolesp);
*/
	  previousP.ParametersOnS(U,V);
	  Exts.SetValue(previousP.PointOnS(),U,V,
			previousP.Parameter(),tolpoint3d);
	  Extc.SetValue(previousP.PointOnC(),previousP.ParameterOnC(),
			previousP.Parameter(),tolpoint3d);
	  // Indiquer que fin sur Bound.
	}
	else {
	  param = param + sens*stepw;
	  if (sens*(param - Bound) > - tolgui) {
	    param = Bound;
	  }
	}
      }
      break;
      
    case Blend_OnRst1  :
      {
#ifdef OCCT_DEBUG
	if (Blend_GettraceDRAWSECT()){
	  Drawsect(surf,curv,param,Func);
	}
#endif
	if (sens>0.) {
	  line->Append(previousP);
	}
	else {
	  line->Prepend(previousP);
	}
	MakeExtremity(Exts,Index,solrst(1),Isvtx,Vtx);
//	Extc.SetValue(previousP.PointOnC(),sol(3),tolesp);
	Extc.SetValue(previousP.PointOnC(),previousP.ParameterOnC(),
		      previousP.Parameter(),tolpoint3d);
	Arrive = Standard_True;
      }
      break;

    case Blend_SamePoints :
      {
	// On arrete
	std::cout << " Points confondus dans le cheminement" << std::endl;
/*
	Exts.SetValue(previousP.PointOnS(),sol(1),sol(2),tolesp);
	Extc.SetValue(previousP.PointOnC(),sol(3),tolesp);
*/
	previousP.ParametersOnS(U,V);
	Exts.SetValue(previousP.PointOnS(),U,V,
		      previousP.Parameter(),tolpoint3d);
	Extc.SetValue(previousP.PointOnC(),previousP.ParameterOnC(),
		      previousP.Parameter(),tolpoint3d);
	Arrive = Standard_True;
      }
      break;

    default:
      break;
    }
    if (Arrive) {
      if (sens > 0.) {
	line->SetEndPoints(Exts,Extc);
      }
      else {
	line->SetStartPoints(Exts,Extc);

      }
    }

  }

}



