// Created on: 1996-02-15
// Created by: Jacques GOUSSARD
// Copyright (c) 1996-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 <LocOpe_FindEdgesInFace.hxx>

#include <BRep_Tool.hxx>
#include <BRepAdaptor_Surface.hxx>
#include <BRepTopAdaptor_TopolTool.hxx>
#include <ElCLib.hxx>
#include <ElSLib.hxx>
#include <Geom_Circle.hxx>
#include <Geom_Curve.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_Line.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_Surface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <Precision.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Shape.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_Map.hxx>

//=================================================================================================

void LocOpe_FindEdgesInFace::Set(const TopoDS_Shape& Sh, const TopoDS_Face& F)
{
  myShape = Sh;
  myFace  = F;
  myList.Clear();

  NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> M;
  TopExp_Explorer                                        exp, expf;
  occ::handle<Geom_Curve>                                C;
  occ::handle<Geom_Surface>                              S;
  TopLoc_Location                                        Loc;
  double                                                 f, l;
  occ::handle<Standard_Type>                             Tc, Ts;
  bool                                                   ToAdd;
  gp_Pln                                                 pl;
  gp_Cylinder                                            cy;

  constexpr double Tol    = Precision::Confusion();
  constexpr double TolAng = Precision::Angular();

  S  = BRep_Tool::Surface(F);
  Ts = S->DynamicType();
  if (Ts == STANDARD_TYPE(Geom_RectangularTrimmedSurface))
  {
    S  = occ::down_cast<Geom_RectangularTrimmedSurface>(S)->BasisSurface();
    Ts = S->DynamicType();
  }

  if (Ts != STANDARD_TYPE(Geom_Plane) && Ts != STANDARD_TYPE(Geom_CylindricalSurface))
  {
    return; // pour le moment
  }

  occ::handle<BRepAdaptor_Surface> HS = new BRepAdaptor_Surface(myFace);
  BRepTopAdaptor_TopolTool         TPT(HS);

  for (exp.Init(myShape, TopAbs_EDGE); exp.More(); exp.Next())
  {
    ToAdd                  = false;
    const TopoDS_Edge& edg = TopoDS::Edge(exp.Current());
    if (M.Contains(edg))
    {
      continue;
    }

    for (expf.Init(myFace, TopAbs_EDGE); expf.More(); expf.Next())
    {
      if (expf.Current().IsSame(edg))
      {
        break;
      }
    }
    if (expf.More())
    { // partage d`edge
      M.Add(edg);
      myList.Append(edg);
      continue;
    }

    C  = BRep_Tool::Curve(edg, Loc, f, l);
    C  = occ::down_cast<Geom_Curve>(C->Transformed(Loc.Transformation()));
    Tc = C->DynamicType();
    if (Tc == STANDARD_TYPE(Geom_TrimmedCurve))
    {
      C  = occ::down_cast<Geom_TrimmedCurve>(C)->BasisCurve();
      Tc = C->DynamicType();
    }
    if (Tc != STANDARD_TYPE(Geom_Line) && Tc != STANDARD_TYPE(Geom_Circle))
    {
      continue; // pour le moment
    }
    if (Ts == STANDARD_TYPE(Geom_Plane))
    {
      pl = occ::down_cast<Geom_Plane>(S)->Pln();
    }
    else
    {
      cy = occ::down_cast<Geom_CylindricalSurface>(S)->Cylinder();
    }

    if (Tc == STANDARD_TYPE(Geom_Line))
    {
      gp_Lin li = occ::down_cast<Geom_Line>(C)->Lin();
      if (Ts == STANDARD_TYPE(Geom_Plane))
      {
        if (pl.Contains(li, Tol, TolAng))
        {
          ToAdd = true;
        }
      }
      else
      { // Ts == STANDARD_TYPE(Geom_CylindricalSurface)
        if (cy.Axis().IsParallel(li.Position(), TolAng)
            && std::abs(li.Distance(cy.Location()) - cy.Radius()) < Tol)
        {
          ToAdd = true;
        }
      }
    }
    else
    { // Tt == STANDARD_TYPE(Geom_Circle)
      gp_Circ ci = occ::down_cast<Geom_Circle>(C)->Circ();
      if (Ts == STANDARD_TYPE(Geom_Plane))
      {
        if (pl.Position().IsCoplanar(ci.Position(), Tol, TolAng))
        {
          ToAdd = true;
        }
      }
      else
      {
        if (std::abs(cy.Radius() - ci.Radius()) < Tol
            && cy.Axis().IsCoaxial(ci.Axis(), TolAng, Tol))
        {
          ToAdd = true;
        }
      }
    }

    if (ToAdd)
    {
      // On classifie 3 points.
      gp_Pnt p[3];
      double U, V;
      p[0] = C->Value(f);
      p[1] = C->Value(l);
      p[2] = C->Value((f + l) / 2.);
      //      for (int i=0; i<3; i++) {
      int i;
      for (i = 0; i < 3; i++)
      {
        if (Ts == STANDARD_TYPE(Geom_Plane))
        {
          ElSLib::Parameters(pl, p[i], U, V);
        }
        else
        {
          ElSLib::Parameters(cy, p[i], U, V);
        }
        if (TPT.Classify(gp_Pnt2d(U, V), Precision::Confusion()) == TopAbs_OUT)
        {
          break;
        }
      }
      if (i >= 3)
      {
        M.Add(edg);
        myList.Append(edg);
      }
    }
  }
}
