// Created on: 1992-10-21
// Created by: Remi GILET
// Copyright (c) 1992-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 <GccAna_Circ2dTanOnRad.hxx>
#include <GccEnt_BadQualifier.hxx>
#include <GccEnt_QualifiedCirc.hxx>
#include <GccEnt_QualifiedLin.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_Point.hxx>
#include <Geom2dGcc_Circ2dTanOnRad.hxx>
#include <Geom2dGcc_Circ2dTanOnRadGeo.hxx>
#include <Geom2dGcc_QCurve.hxx>
#include <Geom2dGcc_QualifiedCurve.hxx>
#include <gp_Circ2d.hxx>
#include <gp_Pnt2d.hxx>
#include <Standard_NegativeValue.hxx>
#include <Standard_OutOfRange.hxx>
#include <StdFail_NotDone.hxx>

Geom2dGcc_Circ2dTanOnRad::Geom2dGcc_Circ2dTanOnRad(const Geom2dGcc_QualifiedCurve& Qualified1,
                                                   const Geom2dAdaptor_Curve&      OnCurve,
                                                   const double                    Radius,
                                                   const double                    Tolerance)
    : cirsol(1, 8),
      qualifier1(1, 8),
      TheSame1(1, 8),
      pnttg1sol(1, 8),
      par1sol(1, 8),
      pararg1(1, 8),
      pntcen3(1, 8),
      parcen3(1, 8)
{
  if (Radius < 0.)
  {
    throw Standard_NegativeValue();
  }
  else
  {
    Geom2dAdaptor_Curve              C1    = Qualified1.Qualified();
    GeomAbs_CurveType                Type1 = C1.GetType();
    GeomAbs_CurveType                Type2 = OnCurve.GetType();
    const occ::handle<Geom2d_Curve>& CC1   = C1.Curve();
    const occ::handle<Geom2d_Curve>& Con   = OnCurve.Curve();

    //=============================================================================
    //                            Appel a GccAna.                                 +
    //=============================================================================

    NbrSol = 0;
    if ((Type1 == GeomAbs_Line || Type1 == GeomAbs_Circle)
        && (Type2 == GeomAbs_Line || Type2 == GeomAbs_Circle))
    {
      if (Type1 == GeomAbs_Circle)
      {
        occ::handle<Geom2d_Circle> CCC1 = occ::down_cast<Geom2d_Circle>(CC1);
        gp_Circ2d                  c1(CCC1->Circ2d());
        GccEnt_QualifiedCirc       Qc1 = GccEnt_QualifiedCirc(c1, Qualified1.Qualifier());
        if (Type2 == GeomAbs_Circle)
        {
          occ::handle<Geom2d_Circle> CCon = occ::down_cast<Geom2d_Circle>(Con);
          gp_Circ2d                  con(CCon->Circ2d());
          GccAna_Circ2dTanOnRad      Circ(Qc1, con, Radius, Tolerance);
          WellDone = Circ.IsDone();
          NbrSol   = Circ.NbSolutions();
          Results(Circ);
        }
        else
        {
          occ::handle<Geom2d_Line> LLon = occ::down_cast<Geom2d_Line>(Con);
          gp_Lin2d                 lon(LLon->Lin2d());
          GccAna_Circ2dTanOnRad    Circ(Qc1, lon, Radius, Tolerance);
          WellDone = Circ.IsDone();
          NbrSol   = Circ.NbSolutions();
          Results(Circ);
        }
      }
      else
      {
        occ::handle<Geom2d_Line> LL1 = occ::down_cast<Geom2d_Line>(CC1);
        gp_Lin2d                 l1(LL1->Lin2d());
        GccEnt_QualifiedLin      Ql1 = GccEnt_QualifiedLin(l1, Qualified1.Qualifier());
        if (Type2 == GeomAbs_Circle)
        {
          occ::handle<Geom2d_Circle> CCon = occ::down_cast<Geom2d_Circle>(Con);
          gp_Circ2d                  con(CCon->Circ2d());
          GccAna_Circ2dTanOnRad      Circ(Ql1, con, Radius, Tolerance);
          WellDone = Circ.IsDone();
          NbrSol   = Circ.NbSolutions();
          Results(Circ);
        }
        else
        {
          occ::handle<Geom2d_Line> LLon = occ::down_cast<Geom2d_Line>(Con);
          gp_Lin2d                 lon(LLon->Lin2d());
          GccAna_Circ2dTanOnRad    Circ(Ql1, lon, Radius, Tolerance);
          WellDone = Circ.IsDone();
          NbrSol   = Circ.NbSolutions();
          Results(Circ);
        }
      }
    }

    //=============================================================================
    //                            Appel a GccGeo.                                 +
    //=============================================================================

    else
    {
      if (Type1 == GeomAbs_Circle)
      {
        occ::handle<Geom2d_Circle>  CCC1 = occ::down_cast<Geom2d_Circle>(CC1);
        gp_Circ2d                   c1(CCC1->Circ2d());
        GccEnt_QualifiedCirc        Qc1 = GccEnt_QualifiedCirc(c1, Qualified1.Qualifier());
        Geom2dGcc_Circ2dTanOnRadGeo CircGeo(Qc1, OnCurve, Radius, Tolerance);
        WellDone = CircGeo.IsDone();
        NbrSol   = CircGeo.NbSolutions();
        Results(CircGeo);
      }
      else if (Type1 == GeomAbs_Line)
      {
        occ::handle<Geom2d_Line>    LL1 = occ::down_cast<Geom2d_Line>(CC1);
        gp_Lin2d                    l1(LL1->Lin2d());
        GccEnt_QualifiedLin         Ql1 = GccEnt_QualifiedLin(l1, Qualified1.Qualifier());
        Geom2dGcc_Circ2dTanOnRadGeo CircGeo(Ql1, OnCurve, Radius, Tolerance);
        WellDone = CircGeo.IsDone();
        NbrSol   = CircGeo.NbSolutions();
        Results(CircGeo);
      }
      else
      {
        Geom2dGcc_QCurve            Qc1(C1, Qualified1.Qualifier());
        Geom2dGcc_Circ2dTanOnRadGeo CircGeo(Qc1, OnCurve, Radius, Tolerance);
        WellDone = CircGeo.IsDone();
        NbrSol   = CircGeo.NbSolutions();
        Results(CircGeo);
      }
    }
  }
}

Geom2dGcc_Circ2dTanOnRad::Geom2dGcc_Circ2dTanOnRad(const occ::handle<Geom2d_Point>& Point1,
                                                   const Geom2dAdaptor_Curve&       OnCurve,
                                                   const double                     Radius,
                                                   const double                     Tolerance)
    : cirsol(1, 8),
      qualifier1(1, 8),
      TheSame1(1, 8),
      pnttg1sol(1, 8),
      par1sol(1, 8),
      pararg1(1, 8),
      pntcen3(1, 8),
      parcen3(1, 8)
{
  if (Radius < 0.)
  {
    throw Standard_NegativeValue();
  }
  else
  {
    gp_Pnt2d                         point1(Point1->Pnt2d());
    GeomAbs_CurveType                Type2 = OnCurve.GetType();
    const occ::handle<Geom2d_Curve>& Con   = OnCurve.Curve();

    //=============================================================================
    //                            Appel a GccAna.                                 +
    //=============================================================================

    NbrSol = 0;
    if (Type2 == GeomAbs_Line || Type2 == GeomAbs_Circle)
    {
      if (Type2 == GeomAbs_Circle)
      {
        occ::handle<Geom2d_Circle> CCon = occ::down_cast<Geom2d_Circle>(Con);
        gp_Circ2d                  con(CCon->Circ2d());
        GccAna_Circ2dTanOnRad      Circ(point1, con, Radius, Tolerance);
        WellDone = Circ.IsDone();
        NbrSol   = Circ.NbSolutions();
        Results(Circ);
      }
      else
      {
        occ::handle<Geom2d_Line> LLon = occ::down_cast<Geom2d_Line>(Con);
        gp_Lin2d                 lon(LLon->Lin2d());
        GccAna_Circ2dTanOnRad    Circ(point1, lon, Radius, Tolerance);
        WellDone = Circ.IsDone();
        NbrSol   = Circ.NbSolutions();
        Results(Circ);
      }
    }

    //=============================================================================
    //                            Appel a GccGeo.                                 +
    //=============================================================================

    else
    {
      Geom2dGcc_Circ2dTanOnRadGeo CircGeo(point1, OnCurve, Radius, Tolerance);
      WellDone = CircGeo.IsDone();
      NbrSol   = CircGeo.NbSolutions();
      Results(CircGeo);
    }
  }
}

void Geom2dGcc_Circ2dTanOnRad::Results(const GccAna_Circ2dTanOnRad& Circ)
{
  for (int j = 1; j <= NbrSol; j++)
  {
    cirsol(j) = Circ.ThisSolution(j);
    if (Circ.IsTheSame1(j))
    {
      TheSame1(j) = 1;
    }
    else
    {
      TheSame1(j) = 0;
    }
    Circ.Tangency1(j, par1sol(j), pararg1(j), pnttg1sol(j));
    Circ.CenterOn3(j, parcen3(j), pntcen3(j));
    Circ.WhichQualifier(j, qualifier1(j));
  }
}

void Geom2dGcc_Circ2dTanOnRad::Results(const Geom2dGcc_Circ2dTanOnRadGeo& Circ)
{
  for (int j = 1; j <= NbrSol; j++)
  {
    cirsol(j) = Circ.ThisSolution(j);
    if (Circ.IsTheSame1(j))
    {
      TheSame1(j) = 1;
    }
    else
    {
      TheSame1(j) = 0;
    }
    Circ.Tangency1(j, par1sol(j), pararg1(j), pnttg1sol(j));
    Circ.CenterOn3(j, parcen3(j), pntcen3(j));
    Circ.WhichQualifier(j, qualifier1(j));
  }
}

bool Geom2dGcc_Circ2dTanOnRad::IsDone() const
{
  return WellDone;
}

int Geom2dGcc_Circ2dTanOnRad::NbSolutions() const
{
  return NbrSol;
}

gp_Circ2d Geom2dGcc_Circ2dTanOnRad::ThisSolution(const int Index) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  if (Index <= 0 || Index > NbrSol)
  {
    throw Standard_OutOfRange();
  }
  return cirsol(Index);
}

void Geom2dGcc_Circ2dTanOnRad::WhichQualifier(const int Index, GccEnt_Position& Qualif1) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else if (Index <= 0 || Index > NbrSol)
  {
    throw Standard_OutOfRange();
  }
  else
  {
    Qualif1 = qualifier1(Index);
  }
}

void Geom2dGcc_Circ2dTanOnRad::Tangency1(const int Index,
                                         double&   ParSol,
                                         double&   ParArg,
                                         gp_Pnt2d& PntSol) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else if (Index <= 0 || Index > NbrSol)
  {
    throw Standard_OutOfRange();
  }
  else
  {
    if (TheSame1(Index) == 0)
    {
      ParSol = par1sol(Index);
      ParArg = pararg1(Index);
      PntSol = pnttg1sol(Index);
    }
    else
    {
      throw StdFail_NotDone();
    }
  }
}

void Geom2dGcc_Circ2dTanOnRad::CenterOn3(const int Index, double& ParArg, gp_Pnt2d& PntSol) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  else if (Index <= 0 || Index > NbrSol)
  {
    throw Standard_OutOfRange();
  }
  else
  {
    ParArg = parcen3(Index);
    PntSol = pntcen3(Index);
  }
}

bool Geom2dGcc_Circ2dTanOnRad::IsTheSame1(const int Index) const
{
  if (!WellDone)
  {
    throw StdFail_NotDone();
  }
  if (Index <= 0 || Index > NbrSol)
  {
    throw Standard_OutOfRange();
  }
  if (TheSame1(Index) == 0)
  {
    return false;
  }
  return true;
}
