// 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.

#include <NCollection_IncAllocator.hxx>

void IntWalk_IWalking::ComputeCloseLine(const TColStd_SequenceOfReal& Umult,
					const TColStd_SequenceOfReal& Vmult,
					const ThePOPIterator& Pnts1,
					const ThePOLIterator& Pnts2,
					TheIWFunction& Func,
                                        Standard_Boolean& Rajout )
// *********** Processing of closed line **********************
//
// for any interior non-processed point 
//       calculate the step of advancement=step depending on the arrow and max step
//       calculate a point of approach (this point is on the tangent to the section
// of distance = no interior point)
//  conditions 
//            (all calculated points do not form a closed loop)  
//                              or                    
//            (all points do not form an open line going from 
//            one border of the domain to the other or from a point tangent
//            to the border or from 2 tangent points : single cases)
//  
//     frame the point of approach on borders if necessary
//     calculate the point
//     if point not found divide the step
//     test of stop    
//     calculate step depending on the arrow and the max step (stop possible)
//
// ******************************************************************** 
{
  Standard_Integer I = 0, N = 0,SaveN = 0;
  Standard_Real aBornInf[2] = {}, aBornSup[2] = {}, aUVap[2] = {};
  math_Vector BornInf(aBornInf,1,2), BornSup(aBornSup,1,2);
  math_Vector Uvap(aUVap,1,2);// parameters of current approach
  Standard_Real PasC = 0.0;  // step of advancement on the tangent
  Standard_Real PasCu = 0.0; // step of advancement current by U
  Standard_Real PasCv = 0.0; // step of advancement current by V
  Standard_Real PasSav = 0.0; // save first step of advancement
  Standard_Boolean Arrive = false;// show if line ends
  Standard_Boolean Cadre = false; // show if on border of the  domains
  Standard_Boolean ArretAjout = false; // show if on the added point
  IntSurf_PntOn2S Psol;
  Handle(IntWalk_TheIWLine)  CurrentLine; //line under construction
  ThePointOfPath PathPnt;
  ThePointOfLoop LoopPnt;

  Standard_Boolean Tgtbeg = false, Tgtend = false;

  Standard_Integer StepSign = 0;
  
  IntWalk_StatusDeflection aStatus = IntWalk_OK, StatusPrecedent;
  Standard_Integer NbDivision = 0;   // number of divisions of step
  // during calculation of  1 section

  Standard_Integer Ipass = 0;
  //index in the iterator of points on edge of point of passage  


  BornInf(1) = Um;
  BornSup(1) = UM;
  BornInf(2) = Vm;
  BornSup(2) = VM;
  
  math_FunctionSetRoot Rsnld(Func,tolerance);
  Standard_Integer nbLoop = Pnts2.Length();

  // Check borders for degeneracy:
  math_Matrix D(1,1,1,2);
  const Standard_Integer aNbSamplePnt = 10;
  Standard_Boolean isLeftDegeneratedBorder[2] = {Standard_True, Standard_True};
  Standard_Boolean isRightDegeneratedBorder[2] = {Standard_True, Standard_True};
  math_Vector aStep(1,2);
  aStep = (BornSup - BornInf) / (aNbSamplePnt - 1);
  for(Standard_Integer aBorderIdx = 1; aBorderIdx <= 2; aBorderIdx++)
  {
    Standard_Integer aChangeIdx = aBorderIdx == 2? 1 : 2;
    math_Vector UV(1,2);

    // Left border.
    UV(aBorderIdx) = BornInf(aBorderIdx);
    for(Standard_Integer aParamIdx = 0; aParamIdx < aNbSamplePnt; aParamIdx++)
    {
      Standard_Real aParam = BornInf(aChangeIdx) + aParamIdx * aStep(aChangeIdx);
      UV(aChangeIdx) = aParam;
      Func.Derivatives(UV, D);
      if ( Abs(D(1, aChangeIdx) ) > Precision::Confusion())
      {
        isLeftDegeneratedBorder[aBorderIdx - 1] = Standard_False;
        break;
      }
    }

    // Right border.
    UV(aBorderIdx) = BornSup(aBorderIdx);
    for(Standard_Integer aParamIdx = 0; aParamIdx < aNbSamplePnt; aParamIdx++)
    {
      Standard_Real aParam = BornInf(aChangeIdx) + aParamIdx * aStep(aChangeIdx);
      UV(aChangeIdx) = aParam;
      Func.Derivatives(UV, D);
      if ( Abs(D(1, aChangeIdx) ) > Precision::Confusion())
      {
        isRightDegeneratedBorder[aBorderIdx - 1] = Standard_False;
        break;
      }
    }
  }

  TheIWFunction aFuncForDuplicate = Func;

  for (I = 1;I<=nbLoop;I++) {
    if (wd2[I].etat > 12)
    { // start point of closed line
      LoopPnt = Pnts2.Value(I);
      previousPoint.SetValue(ThePointOfLoopTool::Value3d(LoopPnt), reversed,
                             wd2[I].ustart, wd2[I].vstart);

      if (IsPointOnLine(previousPoint, BornInf, BornSup, Rsnld, aFuncForDuplicate))
      {
        wd2[I].etat = -wd2[I].etat; //mark point as processed
        continue;
      }

      previousd3d = ThePointOfLoopTool::Direction3d(LoopPnt);
      previousd2d = ThePointOfLoopTool::Direction2d(LoopPnt);

      CurrentLine = new IntWalk_TheIWLine (new NCollection_IncAllocator());
      CurrentLine->AddPoint(previousPoint);
      CurrentLine->SetTangentVector(previousd3d,1);
      Tgtbeg = Standard_False;
      Tgtend = Standard_False;
      Uvap(1) = wd2[I].ustart;
      Uvap(2) = wd2[I].vstart;

      StepSign = 1;

      // first step of advancement

      Standard_Real d2dx = Abs(previousd2d.X()); 
      Standard_Real d2dy = Abs(previousd2d.Y()); 
      if (d2dx < tolerance(1)) {
        PasC = pas * (VM-Vm)/d2dy;
      }
      else if (d2dy < tolerance(2)) {
        PasC = pas * (UM-Um)/d2dx;
      }
      else {
        PasC = pas * Min((UM-Um)/d2dx,(VM-Vm)/d2dy);
      }

      PasSav = PasC;

      Arrive = Standard_False;
      ArretAjout = Standard_False;
      NbDivision = 0;
      StatusPrecedent = IntWalk_OK;
      Standard_Integer aNbIter = 10;
      while (!Arrive) {  // as no test of stop is passed
        Cadre=Cadrage(BornInf,BornSup,Uvap,PasC, StepSign);  // border?
#ifdef CHRONO
        Chronrsnld.Start();
#endif

        Rsnld.Perform(Func,Uvap,BornInf,BornSup);

#ifdef CHRONO
        Chronrsnld.Stop();
#endif
        Standard_Boolean isOnDegeneratedBorder = Standard_False;
        
        if (Cadre) { // update of limits.
          BornInf(1) = Um;BornSup(1) = UM;BornInf(2) = Vm;BornSup(2) = VM;
        }
        if (Rsnld.IsDone()) {
          if (Abs(Func.Root()) > Func.Tolerance()) { // no solution for the tolerance
            PasC = PasC/2.;
            PasCu = Abs(PasC*previousd2d.X());
            PasCv = Abs(PasC*previousd2d.Y());

            if (PasCu <= tolerance(1) && PasCv <= tolerance(2)) {
              if (CurrentLine->NbPoints()==1)
              {
                RemoveTwoEndPoints(I);
                break; //cancel the line
              }
              if (wd2[I].etat >12) { //the line should become open
                wd2[I].etat = 12; //declare it open
                ArretAjout = Standard_False;
                OpenLine(0,Psol,Pnts1,Func,CurrentLine);
                StepSign = -1;
                StatusPrecedent = IntWalk_OK;
                Arrive = Standard_False;
                PasC = PasSav;
                Rajout = Standard_True;
                seqAlone.Append(-lines.Length()-1);
                seqAjout.Append(-lines.Length()-1);
              }
              else { // line s is open                 
                Arrive =Standard_True;
                CurrentLine->AddStatusLast(Standard_False);
                Rajout = Standard_True;
                seqAlone.Append(lines.Length()+1);
                seqAjout.Append(lines.Length()+1);
                Tgtend = Standard_True;
              } 
              /*              
              Arrive = Standard_True;
              CurrentLine->AddStatusFirstLast(Standard_False,
                Standard_False,Standard_False);
              Rajout = Standard_True;
              seqAlone.Append(lines.Length()+1);
              seqAjout.Append(lines.Length()+1);
              Tgtend = Standard_True;
              */
            }
          }
          else { // there is a solution
            Rsnld.Root(Uvap);

            // Avoid uninitialized memory access.
            if (CurrentLine->NbPoints() > 2)
            {
              for(Standard_Integer aCoordIdx = 1; aCoordIdx <= 2; aCoordIdx++)
              {
                // Check degenerated cases and fix if possible.
                if ( ( isLeftDegeneratedBorder[aCoordIdx - 1]
                && Abs (Uvap(aCoordIdx) - BornInf(aCoordIdx)) < Precision::PConfusion())||
                  (isRightDegeneratedBorder[aCoordIdx - 1]
                && Abs (Uvap(aCoordIdx) - BornSup(aCoordIdx)) < Precision::PConfusion()) )
                {
                  Standard_Real uvprev[2], uv[2];
                  if (!reversed)
                  {
                    CurrentLine->Value(CurrentLine->NbPoints() - 1).ParametersOnS2(uvprev[0], uvprev[1]);
                    CurrentLine->Value(CurrentLine->NbPoints()).ParametersOnS2(uv[0], uv[1]);
                  }
                  else
                  {
                    CurrentLine->Value(CurrentLine->NbPoints() - 1).ParametersOnS1(uvprev[0], uvprev[1]);
                    CurrentLine->Value(CurrentLine->NbPoints()).ParametersOnS1(uv[0], uv[1]);
                  }

                  Standard_Real aScaleCoeff = 0.0;

                  // Avoid finite cycle which lead to stop computing iline.
                  if (aStatus != IntWalk_PasTropGrand)
                  {
                    // Make linear extrapolation.
                    if ( Abs(uv[aCoordIdx - 1] - uvprev[aCoordIdx - 1]) > gp::Resolution())
                      aScaleCoeff = Abs ((Uvap(aCoordIdx) - uv[aCoordIdx - 1])
                                      /  (uv[aCoordIdx - 1] - uvprev[aCoordIdx - 1]) );
                    Standard_Integer aFixIdx =  aCoordIdx == 1? 2 : 1; // Fixing index;
                    Uvap(aFixIdx) = uv[aFixIdx - 1] + (uv[aFixIdx - 1] - uvprev[aFixIdx - 1]) * aScaleCoeff;
                    isOnDegeneratedBorder = Standard_True;
                  }
                }
              }
            }

            Arrive = TestArretPassage(Umult,Vmult,Uvap,I,Ipass);
            if (Arrive) {//reset proper parameter to test the arrow.
              Psol = CurrentLine->Value(1);
              if (!reversed) {
                Psol.ParametersOnS2(Uvap(1),Uvap(2));
              }
              else {
                Psol.ParametersOnS1(Uvap(1),Uvap(2));
              }
              Cadre=Standard_False; 
              //in case if there is a frame and arrival at the same time
            }
            else { // modif jag 940615
              if (Rajout) {    // test on added points
                ArretAjout =TestArretAjout(Func,Uvap,N,Psol);
                SaveN = N;
                if (ArretAjout) {
                  if (N >0) {
                    Tgtend = lines.Value(N)->IsTangentAtEnd();
                    N = -N;
                  }
                  else {
                    Tgtend = lines.Value(-N)->IsTangentAtBegining();
                  }
                  Arrive = (wd2[I].etat == 12);
                }
              }

              if (!ArretAjout&& Cadre) {  // test on already marked points
                if (CurrentLine->NbPoints() == 1)
                {
                  RemoveTwoEndPoints(I);
                  break; // cancel the line
                }
                TestArretCadre(Umult,Vmult,CurrentLine,Func,Uvap,N);
                SaveN = N;
                //		if (N==0) {
                if (N <= 0) { // jag 941017
                  MakeWalkingPoint(2,Uvap(1),Uvap(2),Func,Psol);
                  Tgtend = Func.IsTangent(); // jag 940616
                  if (isOnDegeneratedBorder)
                    Tgtend = Standard_True;
                  N = -N;
                }
                Arrive = (wd2[I].etat == 12); // the line is open
              }
            }
            aStatus = TestDeflection(Func, Arrive,Uvap,StatusPrecedent,
                                                NbDivision,PasC,StepSign);

            if (isOnDegeneratedBorder && Tgtend)
              aStatus = IntWalk_ArretSurPoint;
            
            StatusPrecedent = aStatus;
            if (aStatus == IntWalk_PasTropGrand) {// division of the step
              Arrive = Standard_False;
              ArretAjout = Standard_False;
              Tgtend = Standard_False; // jag 940616
              if (!reversed) {
                previousPoint.ParametersOnS2(Uvap(1),Uvap(2));
              }
              else {
                previousPoint.ParametersOnS1(Uvap(1),Uvap(2));
              }
            }
            else if (ArretAjout || Cadre) {

              if (Arrive) { // line s is open
                CurrentLine->AddStatusLast(Standard_False);
                //if (aStatus != IntWalk_ArretSurPointPrecedent)
                CurrentLine->AddPoint(Psol);

                //Remove <SaveN> from <seqAlone> and, if it is first found point,
                //from <seqAjout> too
                if (IsValidEndPoint(I, SaveN))
                {
                  for (Standard_Integer iseq = 1; iseq <= seqAlone.Length(); iseq++)
                    if (seqAlone(iseq) == SaveN)
                    {
                      seqAlone.Remove(iseq);
                      break;
                    }
                  if (CurrentLine->NbPoints() <= 3)
                    for (Standard_Integer iseq = 1; iseq <= seqAjout.Length(); iseq++)
                      if (seqAjout(iseq) == SaveN)
                      {
                        seqAjout.Remove(iseq);
                        break;
                      }
                }
                else
                {
                  if (seqAlone.Last() == -lines.Length()-1)
                  {
                    seqAlone.Remove(seqAlone.Length());
                    seqAjout.Remove(seqAjout.Length());
                  }
                  RemoveTwoEndPoints(I);
                  Arrive = Standard_False;
                  break; //cancel the line
                }

                if (Cadre && N==0) {
                  Rajout = Standard_True;
                  //seqAlone.Append(lines.Length()+1);
                  seqAjout.Append(lines.Length()+1);
                }

              }
              else { // open
                wd2[I].etat = 12; // declare it open
                Tgtbeg = Tgtend;
                Tgtend = Standard_False;
                ArretAjout = Standard_False;
                StepSign = -1;
                StatusPrecedent = IntWalk_OK;
                PasC = PasSav;
                //Check if <Psol> has been really updated
                if (Arrive || Rajout || (!ArretAjout && Cadre && SaveN <= 0))
                {
                  if (aStatus == IntWalk_ArretSurPointPrecedent) {
                    CurrentLine->AddPoint(Psol);
                    OpenLine(0,Psol,Pnts1,Func,CurrentLine);
                  }
                  else {
                    OpenLine(-lines.Length()-1,Psol,Pnts1,Func,CurrentLine);
                  }
                }
                //Remove <SaveN> from <seqAlone> and, if it is first found point,
                //from <seqAjout> too
                if (IsValidEndPoint(I, SaveN))
                {
                  for (Standard_Integer iseq = 1; iseq <= seqAlone.Length(); iseq++)
                    if (seqAlone(iseq) == SaveN)
                    {
                      seqAlone.Remove(iseq);
                      break;
                    }
                  if (CurrentLine->NbPoints() <= 2)
                    for (Standard_Integer iseq = 1; iseq <= seqAjout.Length(); iseq++)
                      if (seqAjout(iseq) == SaveN)
                      {
                        seqAjout.Remove(iseq);
                        break;
                      }
                }
                else
                {
                  RemoveTwoEndPoints(I);
                  break; //cancel the line
                }
                
                if (Cadre && N==0) {
                  Rajout = Standard_True;
                  seqAjout.Append(-lines.Length()-1);
                }
              }
            }
            else if (aStatus == IntWalk_ArretSurPointPrecedent) {
              if (CurrentLine->NbPoints() == 1) { //cancel the line
                Arrive = Standard_False;
                RemoveTwoEndPoints(I);
                break;
              }
              if (wd2[I].etat >12) { //the line should become open
                wd2[I].etat = 12; //declare it open
                ArretAjout = Standard_False;
                OpenLine(0,Psol,Pnts1,Func,CurrentLine);
                StepSign = -1;
                StatusPrecedent = IntWalk_OK;
                Arrive = Standard_False;
                PasC = PasSav;
                Rajout = Standard_True;
                seqAlone.Append(-lines.Length()-1);
                seqAjout.Append(-lines.Length()-1);
              }
              else { // line s is open                 
                Arrive =Standard_True;
                CurrentLine->AddStatusLast(Standard_False);
                Rajout = Standard_True;
                seqAlone.Append(lines.Length()+1);
                seqAjout.Append(lines.Length()+1);
              } 
            }
            else if (Arrive)  {
              if (wd2[I].etat > 12) {  //line closed good case
                CurrentLine->AddStatusFirstLast(Standard_True,
                  Standard_False,Standard_False);
                CurrentLine->AddPoint(CurrentLine->Value(1));
              }
              else if ((N >0) && (Pnts1.Length() >= N))
              {
                //point of stop given at input 
                PathPnt = Pnts1.Value(N);
                CurrentLine->AddStatusLast(Standard_True,N,PathPnt);
                AddPointInCurrentLine(N,PathPnt,CurrentLine);
              }
            }
            else if (aStatus == IntWalk_ArretSurPoint) {
              if (wd2[I].etat >12) { //line should become open
                wd2[I].etat = 12; //declare it open
                Tgtbeg = Standard_True;
                Tgtend = Standard_False;
                N= -lines.Length()-1;
                Psol.SetValue(Func.Point(),reversed,Uvap(1),Uvap(2));
                OpenLine(N,Psol,Pnts1,Func,CurrentLine);
                StepSign = -1;
                Rajout = Standard_True;
                seqAlone.Append(N);
                seqAjout.Append(N);
                StatusPrecedent = IntWalk_OK;
                Arrive = Standard_False;
                PasC = PasSav;	
              }
              else { 
                Arrive = Standard_True;                   
                if (Ipass!=0) { //point of passage, point of stop
                  PathPnt = Pnts1.Value(Ipass);
                  CurrentLine->AddStatusLast(Standard_True,Ipass,PathPnt);
                  AddPointInCurrentLine(Ipass,PathPnt,CurrentLine);
                }
                else {
                  CurrentLine->AddStatusLast(Standard_False);
                  IntSurf_PntOn2S newP;
                  newP.SetValue(Func.Point(),reversed,Uvap(1),Uvap(2));
                  CurrentLine->AddPoint(newP);
                  Rajout = Standard_True;
                  seqAlone.Append(lines.Length()+1);
                  seqAjout.Append(lines.Length()+1);
                }
              }
            }
            else if (aStatus == IntWalk_OK) {
              if (Ipass!=0) CurrentLine->AddIndexPassing(Ipass);
              previousPoint.SetValue(Func.Point(),reversed,Uvap(1),Uvap(2));
              previousd3d = Func.Direction3d();
              previousd2d = Func.Direction2d();
              CurrentLine->AddPoint(previousPoint);
            }
            else if (aStatus == IntWalk_PointConfondu)
            {
              aNbIter --;
            }
          }
        }
        else { //no numerical solution NotDone
          PasC = PasC/2.;
          PasCu = Abs(PasC*previousd2d.X());
          PasCv = Abs(PasC*previousd2d.Y());

          if (PasCu <= tolerance(1) && PasCv <= tolerance(2)) {
            if (CurrentLine->NbPoints() == 1)
            {
              RemoveTwoEndPoints(I);
              break; // cancel the line
            }
            if (wd2[I].etat >12) { //the line should become open
              wd2[I].etat = 12; //declare it open
              ArretAjout = Standard_False;
              OpenLine(0,Psol,Pnts1,Func,CurrentLine);
              StepSign = -1;
              StatusPrecedent = IntWalk_OK;
              Arrive = Standard_False;
              PasC = PasSav;
              Rajout = Standard_True;
              seqAlone.Append(-lines.Length()-1);
              seqAjout.Append(-lines.Length()-1);
            }
            else { // line s is open                 
              Arrive =Standard_True;
              CurrentLine->AddStatusLast(Standard_False);
              Tgtend = Standard_True;
              Rajout = Standard_True;
              seqAlone.Append(lines.Length()+1);
              seqAjout.Append(lines.Length()+1);
            } 
            /*
            Arrive = Standard_True;
            CurrentLine->AddStatusFirstLast(Standard_False,Standard_False,
              Standard_False);
            Tgtend = Standard_True;
            Rajout = Standard_True;
            seqAlone.Append(lines.Length()+1);
            seqAjout.Append(lines.Length()+1);
            */
          }  
        }

        if(aNbIter < 0)
          break;
      }// end of started line 
      if (Arrive) {
        CurrentLine->SetTangencyAtBegining(Tgtbeg);
        CurrentLine->SetTangencyAtEnd(Tgtend);

        lines.Append(CurrentLine);
        wd2[I].etat=-wd2[I].etat; //mark point as processed
      }
    } //end of processing of start point
  } //end of all start points
}
