// Created on: 1991-12-20
// Created by: Remi GILET
// Copyright (c) 1991-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.

//========================================================================
// CREATION D UNE LIGNE TANGENTE A UNE COURBE ET PARALLELE A UNE DROITE. +
//========================================================================

#include <GccEnt_BadQualifier.hxx>
#include <Geom2dGcc_CurveTool.hxx>
#include <Geom2dGcc_FunctionTanObl.hxx>
#include <Geom2dGcc_IsParallel.hxx>
#include <Geom2dGcc_Lin2dTanOblIter.hxx>
#include <Geom2dGcc_QCurve.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt2d.hxx>
#include <gp_Vec2d.hxx>
#include <gp_XY.hxx>
#include <IntAna2d_AnaIntersection.hxx>
#include <IntAna2d_IntPoint.hxx>
#include <math_FunctionRoot.hxx>
#include <StdFail_NotDone.hxx>

Geom2dGcc_Lin2dTanOblIter::Geom2dGcc_Lin2dTanOblIter(const Geom2dGcc_QCurve& Qualified1,
                                                     const gp_Lin2d&         TheLin,
                                                     const Standard_Real     Param1,
                                                     const Standard_Real     TolAng,
                                                     const Standard_Real     Angle)
    : par2sol(0.0),
      pararg2(0.0)
{

  par1sol  = 0.;
  pararg1  = 0.;
  WellDone = Standard_False;
  if (!(Qualified1.IsEnclosed() || Qualified1.IsEnclosing() || Qualified1.IsOutside()
        || Qualified1.IsUnqualified()))
  {
    throw GccEnt_BadQualifier();
    return;
  }
  Paral2                  = Standard_False;
  Geom2dAdaptor_Curve Cu1 = Qualified1.Qualified();
  Standard_Real       U1  = Geom2dGcc_CurveTool::FirstParameter(Cu1);
  Standard_Real       U2  = Geom2dGcc_CurveTool::LastParameter(Cu1);
  gp_Dir2d            Dir(TheLin.Direction());
  Standard_Real       A = Dir.X();
  Standard_Real       B = Dir.Y();
  gp_Dir2d            TheDirection(Dir);
  if (Abs(Angle) > Abs(TolAng))
  {
    if (Abs(Abs(Angle) - M_PI) <= Abs(TolAng))
    {
      Paral2       = Standard_True;
      TheDirection = Dir.Reversed();
    }
    else if (Abs(Angle - M_PI / 2) <= Abs(TolAng))
    {
      TheDirection = gp_Dir2d(-B, A);
    }
    else if (Abs(Angle + M_PI / 2) <= Abs(TolAng))
    {
      TheDirection = gp_Dir2d(B, -A);
    }
    else
    {
      TheDirection = gp_Dir2d(A * Cos(Angle) - B * Sin(Angle), A * Sin(Angle) + B * Cos(Angle));
    }
  }
  else
  {
    Paral2 = Standard_True;
  }
  Geom2dGcc_FunctionTanObl func(Cu1, TheDirection);
  math_FunctionRoot sol(func, Param1, Geom2dGcc_CurveTool::EpsX(Cu1, Abs(TolAng)), U1, U2, 100);
  if (sol.IsDone())
  {
    Standard_Real Usol = sol.Root();
    gp_Pnt2d      Origine;
    gp_Vec2d      Vect1, Vect2;
    Geom2dGcc_CurveTool::D2(Cu1, Usol, Origine, Vect1, Vect2);
    Standard_Real sign1 = Vect1.XY().Dot(TheDirection.XY());
    Standard_Real sign2 = Vect2.XY().Crossed(TheDirection.XY());
    if (Qualified1.IsUnqualified() || (Qualified1.IsEnclosing() && sign2 <= 0.)
        || (Qualified1.IsOutside() && sign1 <= 0. && sign2 >= 0.)
        || (Qualified1.IsEnclosed() && sign1 >= 0. && sign2 >= 0.))
    {
      WellDone   = Standard_True;
      linsol     = gp_Lin2d(Origine, TheDirection);
      pnttg1sol  = Origine;
      qualifier1 = Qualified1.Qualifier();
      pararg1    = Usol;
      par1sol    = 0.;
      if (!Paral2)
      {
        IntAna2d_AnaIntersection Intp(linsol, TheLin);
        if (Intp.IsDone() && !Intp.IsEmpty())
        {
          if (Intp.NbPoints() == 1)
          {
            pntint2sol = Intp.Point(1).Value();
            par2sol    = gp_Vec2d(linsol.Direction()).Dot(gp_Vec2d(linsol.Location(), pntint2sol));
            pararg2    = gp_Vec2d(TheLin.Direction()).Dot(gp_Vec2d(TheLin.Location(), pntint2sol));
          }
        }
      }
    }
  }
}

Standard_Boolean Geom2dGcc_Lin2dTanOblIter::IsDone() const
{
  return WellDone;
}

gp_Lin2d Geom2dGcc_Lin2dTanOblIter::ThisSolution() const
{
  if (!WellDone)
    throw StdFail_NotDone();

  return linsol;
}

void Geom2dGcc_Lin2dTanOblIter::WhichQualifier(GccEnt_Position& Qualif1) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else
  {
    Qualif1 = qualifier1;
  }
}

Standard_Boolean Geom2dGcc_Lin2dTanOblIter::IsParallel2() const
{
  return Paral2;
}

void Geom2dGcc_Lin2dTanOblIter::Tangency1(Standard_Real& ParSol,
                                          Standard_Real& ParArg,
                                          gp_Pnt2d&      PntSol) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else
  {
    ParSol = par1sol;
    ParArg = pararg1;
    PntSol = gp_Pnt2d(pnttg1sol);
  }
}

void Geom2dGcc_Lin2dTanOblIter::Intersection2(Standard_Real& ParSol,
                                              Standard_Real& ParArg,
                                              gp_Pnt2d&      PntSol) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else if (Paral2)
  {
    throw Geom2dGcc_IsParallel();
  }
  else
  {
    PntSol = pntint2sol;
    ParSol = par2sol;
    ParArg = pararg2;
  }
}
