// 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 <GeomAdaptor_SurfaceOfLinearExtrusion.hxx>
#include <GeomAdaptor_SurfaceOfRevolution.hxx>
#include <BRep_Builder.hxx>
#include <BRep_GCurve.hxx>
#include <BRep_TEdge.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom_ConicalSurface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <Geom_OffsetSurface.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_Surface.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <Geom_SurfaceOfRevolution.hxx>
#include <Geom_SweptSurface.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <gp_Pln.hxx>
#include <gp_Pnt.hxx>
#include <Message_Msg.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <ShapeCustom_SweptToElementary.hxx>
#include <Standard_Type.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Vertex.hxx>

IMPLEMENT_STANDARD_RTTIEXT(ShapeCustom_SweptToElementary, ShapeCustom_Modification)

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

ShapeCustom_SweptToElementary::ShapeCustom_SweptToElementary() {}

//=======================================================================
// function : IsToConvert
// purpose  : auxiliary (Analyze surface: is it to be converted?)
//=======================================================================
static Standard_Boolean IsToConvert(const Handle(Geom_Surface)& S, Handle(Geom_SweptSurface)& SS)
{
  Handle(Geom_Surface) Stmp;

  if (S->IsKind(STANDARD_TYPE(Geom_SweptSurface)))
  {
    SS = Handle(Geom_SweptSurface)::DownCast(S);
    return Standard_True;
  }
  if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
  {
    Handle(Geom_RectangularTrimmedSurface) RTS =
      Handle(Geom_RectangularTrimmedSurface)::DownCast(S);
    Stmp = RTS->BasisSurface();
  }
  else if (S->IsKind(STANDARD_TYPE(Geom_OffsetSurface)))
  {
    Handle(Geom_OffsetSurface) OS = Handle(Geom_OffsetSurface)::DownCast(S);
    Stmp                          = OS->BasisSurface();
  }
  if (Stmp.IsNull())
    return Standard_False;
  if (S->IsKind(STANDARD_TYPE(Geom_SweptSurface)))
  {
    SS = Handle(Geom_SweptSurface)::DownCast(Stmp);
    return Standard_True;
  }
  return Standard_False;
}

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

Standard_Boolean ShapeCustom_SweptToElementary::NewSurface(const TopoDS_Face&    F,
                                                           Handle(Geom_Surface)& S,
                                                           TopLoc_Location&      L,
                                                           Standard_Real&        Tol,
                                                           Standard_Boolean&     RevWires,
                                                           Standard_Boolean&     RevFace)
{
  S = BRep_Tool::Surface(F, L);
  Handle(Geom_SweptSurface) SS;
  if (!IsToConvert(S, SS))
    return Standard_False;

  // case SurfaceOfRevolution
  if (SS->IsKind(STANDARD_TYPE(Geom_SurfaceOfRevolution)))
  {
    Handle(Geom_SurfaceOfRevolution) SR  = Handle(Geom_SurfaceOfRevolution)::DownCast(SS);
    Handle(Geom_Curve)               bc  = SR->BasisCurve();
    gp_Ax1                           ax1 = SR->Axis();
    Handle(GeomAdaptor_Curve)        HC  = new GeomAdaptor_Curve();
    HC->Load(bc, bc->FirstParameter(), bc->LastParameter());
    GeomAdaptor_SurfaceOfRevolution AS(HC, ax1);
    switch (AS.GetType())
    {
      // skl 18.12.2003 - plane not used, problems in PRO14665.igs
      // case GeomAbs_Plane : {
      //  Handle(Geom_Plane) Pl = new Geom_Plane(AS.Plane());
      //  S = Pl;
      //} break;
      case GeomAbs_Cylinder: {
        Handle(Geom_CylindricalSurface) Cy = new Geom_CylindricalSurface(AS.Cylinder());
        S                                  = Cy;
      }
      break;
      case GeomAbs_Sphere: {
        Handle(Geom_SphericalSurface) Sp = new Geom_SphericalSurface(AS.Sphere());
        S                                = Sp;
      }
      break;
      case GeomAbs_Cone: {
        Handle(Geom_ConicalSurface) Co = new Geom_ConicalSurface(AS.Cone());
        S                              = Co;
      }
      break;
      case GeomAbs_Torus: {
        Handle(Geom_ToroidalSurface) To = new Geom_ToroidalSurface(AS.Torus());
        S                               = To;
      }
      break;
      default:
        return Standard_False;
        break;
    }
  }
  // case SurfaceOfLinearExtrusion
  else if (SS->IsKind(STANDARD_TYPE(Geom_SurfaceOfLinearExtrusion)))
  {
    Handle(Geom_SurfaceOfLinearExtrusion) SLE = Handle(Geom_SurfaceOfLinearExtrusion)::DownCast(SS);
    Handle(Geom_Curve)                    bc  = SLE->BasisCurve();
    gp_Dir                                dir = SLE->Direction();
    Handle(GeomAdaptor_Curve)             HC  = new GeomAdaptor_Curve();
    HC->Load(bc, bc->FirstParameter(), bc->LastParameter());
    GeomAdaptor_SurfaceOfLinearExtrusion AS(HC, dir);
    switch (AS.GetType())
    {
      // skl 18.12.2003 - plane not used, problems in ims013.igs
      // case GeomAbs_Plane : {
      //  Handle(Geom_Plane) Pl = new Geom_Plane(AS.Plane());
      //  S = Pl;
      //} break;
      case GeomAbs_Cylinder: {
        Handle(Geom_CylindricalSurface) Cy = new Geom_CylindricalSurface(AS.Cylinder());
        S                                  = Cy;
      }
      break;
      default:
        return Standard_False;
        break;
    }
  }

  SendMsg(F, Message_Msg("SweptToElementary.NewSurface.MSG0"));

  Tol      = BRep_Tool::Tolerance(F);
  RevWires = Standard_False;
  RevFace  = Standard_False;
  return Standard_True;
}

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

Standard_Boolean ShapeCustom_SweptToElementary::NewCurve(const TopoDS_Edge&  E,
                                                         Handle(Geom_Curve)& C,
                                                         TopLoc_Location&    L,
                                                         Standard_Real&      Tol)
{
  //: p5 abv 26 Feb 99: force copying of edge if any its pcurve will be replaced
  Handle(BRep_TEdge)& TE = *((Handle(BRep_TEdge)*)&E.TShape());

  // iterate on pcurves
  BRep_ListIteratorOfListOfCurveRepresentation itcr(TE->Curves());
  for (; itcr.More(); itcr.Next())
  {
    Handle(BRep_GCurve) GC = Handle(BRep_GCurve)::DownCast(itcr.Value());
    if (GC.IsNull() || !GC->IsCurveOnSurface())
      continue;
    Handle(Geom_Surface)      S = GC->Surface();
    Handle(Geom_SweptSurface) SS;
    if (!IsToConvert(S, SS))
      continue;
    Standard_Real f, l;
    C = BRep_Tool::Curve(E, L, f, l);
    if (!C.IsNull())
      C = Handle(Geom_Curve)::DownCast(C->Copy());
    Tol = BRep_Tool::Tolerance(E);
    return Standard_True;
  }
  return Standard_False;
}

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

Standard_Boolean ShapeCustom_SweptToElementary::NewPoint(const TopoDS_Vertex& /*V*/,
                                                         gp_Pnt& /*P*/,
                                                         Standard_Real& /*Tol*/)
{
  // 3d points are never modified
  return Standard_False;
}

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

Standard_Boolean ShapeCustom_SweptToElementary::NewCurve2d(const TopoDS_Edge&    E,
                                                           const TopoDS_Face&    F,
                                                           const TopoDS_Edge&    NewE,
                                                           const TopoDS_Face&    NewF,
                                                           Handle(Geom2d_Curve)& C,
                                                           Standard_Real&        Tol)
{
  TopLoc_Location           L;
  Handle(Geom_Surface)      S = BRep_Tool::Surface(F, L);
  Handle(Geom_SweptSurface) SS;

  // just copy pcurve if either its surface is changing or edge was copied
  if (!IsToConvert(S, SS) && E.IsSame(NewE))
    return Standard_False;

  Standard_Real f, l;
  C = BRep_Tool::CurveOnSurface(E, F, f, l);
  if (!C.IsNull())
  {
    C                       = Handle(Geom2d_Curve)::DownCast(C->Copy());
    Handle(Geom_Surface) NS = BRep_Tool::Surface(NewF, L);
    if (!NS.IsNull() && NS->IsKind(STANDARD_TYPE(Geom_ToroidalSurface)))
    {
      if (SS->IsKind(STANDARD_TYPE(Geom_SurfaceOfRevolution)))
      {
        Handle(Geom_SurfaceOfRevolution) SR = Handle(Geom_SurfaceOfRevolution)::DownCast(SS);
        Standard_Real                    U1, U2, V1, V2;
        SR->Bounds(U1, U2, V1, V2);
        gp_Pnt P0;
        SR->D0(U1, V1, P0);
        Handle(ShapeAnalysis_Surface) sas = new ShapeAnalysis_Surface(NS);
        gp_Pnt2d                      p2d = sas->ValueOfUV(P0, Precision::Confusion());
        gp_Vec2d                      shift(p2d.X() - U1, p2d.Y() - V1);
        C->Translate(shift);
      }
    }
    if (!NS.IsNull() && NS->IsKind(STANDARD_TYPE(Geom_SphericalSurface)))
    {
      if (SS->IsKind(STANDARD_TYPE(Geom_SurfaceOfRevolution)))
      {
        Handle(Geom_SurfaceOfRevolution) SR = Handle(Geom_SurfaceOfRevolution)::DownCast(SS);
        gp_Pnt                           PR, PS;
        Handle(Geom_SphericalSurface)    SPH = Handle(Geom_SphericalSurface)::DownCast(NS);
        Standard_Real                    US1, US2, VS1, VS2;
        SPH->Bounds(US1, US2, VS1, VS2);
        SPH->D0(US1, VS1, PS);
        Standard_Real UR1, UR2, VR1, VR2;
        SR->Bounds(UR1, UR2, VR1, VR2);
        SR->D0(UR1, VR1, PR);
        gp_Pnt        P0 = SPH->Location();
        gp_Vec        VS(P0, PS);
        gp_Vec        VR(P0, PR);
        Standard_Real angle = VS.Angle(VR);
        gp_Vec2d      shift(0, VS1 - VR1 + angle);
        C->Translate(shift);
      }
    }
  }

  Tol = BRep_Tool::Tolerance(E);
  return Standard_True;
}

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

Standard_Boolean ShapeCustom_SweptToElementary::NewParameter(const TopoDS_Vertex& /*V*/,
                                                             const TopoDS_Edge& /*E*/,
                                                             Standard_Real& /*P*/,
                                                             Standard_Real& /*Tol*/)
{
  return Standard_False;
}

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

GeomAbs_Shape ShapeCustom_SweptToElementary::Continuity(const TopoDS_Edge& E,
                                                        const TopoDS_Face& F1,
                                                        const TopoDS_Face& F2,
                                                        const TopoDS_Edge& /*NewE*/,
                                                        const TopoDS_Face& /*NewF1*/,
                                                        const TopoDS_Face& /*NewF2*/)
{
  return BRep_Tool::Continuity(E, F1, F2);
}
