// Created on: 1997-04-10
// Created by: Prestataire Mary FABIEN
// Copyright (c) 1997-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 <BRep_Tool.hxx>
#include <gp_Pnt.hxx>
#include <Standard_Type.hxx>
#include <TColStd_IndexedMapOfInteger.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Shape.hxx>
#include <TopOpeBRepDS.hxx>
#include <TopOpeBRepDS_Check.hxx>
#include <TopOpeBRepDS_CheckStatus.hxx>
#include <TopOpeBRepDS_CurvePointInterference.hxx>
#include <TopOpeBRepDS_DataMapOfCheckStatus.hxx>
#include <TopOpeBRepDS_EdgeVertexInterference.hxx>
#include <TopOpeBRepDS_HDataStructure.hxx>
#include <TopOpeBRepDS_Interference.hxx>
#include <TopOpeBRepDS_InterferenceTool.hxx>
#include <TopOpeBRepDS_Kind.hxx>
#include <TopOpeBRepDS_ListOfInterference.hxx>
#include <TopOpeBRepDS_PointExplorer.hxx>
#include <TopOpeBRepTool_ShapeTool.hxx>
#include <TopTools_ListOfShape.hxx>

IMPLEMENT_STANDARD_RTTIEXT(TopOpeBRepDS_Check, Standard_Transient)

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

TopOpeBRepDS_Check::TopOpeBRepDS_Check(const Handle(TopOpeBRepDS_HDataStructure)& HDS)
{
  myHDS = HDS;
  myMapSurfaceStatus.Clear();
  myMapCurveStatus.Clear();
  myMapPointStatus.Clear();
  myMapShapeStatus.Clear();
}

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

TopOpeBRepDS_Check::TopOpeBRepDS_Check()
{
  myMapSurfaceStatus.Clear();
  myMapCurveStatus.Clear();
  myMapPointStatus.Clear();
  myMapShapeStatus.Clear();
}

//=======================================================================
// function : ChkIntg
// purpose  : Check Integrity
//=======================================================================
Standard_Boolean CheckEdgeParameter(const Handle(TopOpeBRepDS_HDataStructure)& myHDS);

Standard_Boolean TopOpeBRepDS_Check::ChkIntg()
{
  const TopOpeBRepDS_DataStructure& DS = myHDS->DS();
  Standard_Boolean                  bI = Standard_False;
  // Check the integrity of the DS
  Standard_Integer i, nshape = DS.NbShapes();
  for (i = 1; i <= nshape; i++)
  {
    // Integrity of Interferences : Check support and geometry
    const TopOpeBRepDS_ListOfInterference& LI = DS.ShapeInterferences(i);
    bI                                        = ChkIntgInterf(LI);
  }
  Standard_Integer nsurface = DS.NbSurfaces();
  for (i = 1; i <= nsurface; i++)
  {
    // Integrity of Interferences : Check support and geometry
    const TopOpeBRepDS_ListOfInterference& LI = DS.SurfaceInterferences(i);
    bI                                        = bI && ChkIntgInterf(LI);
  }

  Standard_Integer ncurve = DS.NbCurves();
  for (i = 1; i <= ncurve; i++)
  {
    // Integrity of Interferences : Check support and geometry
    const TopOpeBRepDS_ListOfInterference& LI = DS.CurveInterferences(i);
    bI                                        = bI && ChkIntgInterf(LI);
  }

  Standard_Integer npoint = DS.NbPoints();
  for (i = 1; i <= npoint; i++)
  {
    // Integrity of Interferences : Check support and geometry
    const TopOpeBRepDS_ListOfInterference& LI = DS.PointInterferences(i);
    bI                                        = bI && ChkIntgInterf(LI);
  }

  //  CheckEdgeParameter();
  CheckEdgeParameter(myHDS);

  return bI;
}

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

Standard_Boolean TopOpeBRepDS_Check::ChkIntgInterf(const TopOpeBRepDS_ListOfInterference& LI)
{
  TopOpeBRepDS_ListIteratorOfListOfInterference it1;
  it1.Initialize(LI);
  Standard_Boolean IsOK = Standard_True;
  while (it1.More())
  {
    Handle(TopOpeBRepDS_Interference) I1 = it1.Value();
    IsOK                                 = IsOK && CheckDS(I1->Support(), I1->SupportType());
    IsOK                                 = IsOK && CheckDS(I1->Geometry(), I1->GeometryType());
    it1.Next();
  }
  return IsOK;
}

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

Standard_Boolean TopOpeBRepDS_Check::CheckDS(const Standard_Integer I, const TopOpeBRepDS_Kind K)
{
  // geometry
  switch (K)
  {
    case TopOpeBRepDS_SURFACE: {
      if (myHDS->NbSurfaces() < I)
      {
        if (myMapSurfaceStatus.IsBound(I))
          myMapSurfaceStatus.UnBind(I);
        myMapSurfaceStatus.Bind(I, TopOpeBRepDS_NOK);
        return Standard_False;
      }
      if (!myMapSurfaceStatus.IsBound(I))
        myMapSurfaceStatus.Bind(I, TopOpeBRepDS_OK);
      return Standard_True;
    }
    case TopOpeBRepDS_CURVE: {
      if (myHDS->NbCurves() < I)
      {
        if (myMapCurveStatus.IsBound(I))
          myMapCurveStatus.UnBind(I);
        myMapCurveStatus.Bind(I, TopOpeBRepDS_NOK);
        return Standard_False;
      }

      if (!myMapCurveStatus.IsBound(I))
        myMapCurveStatus.Bind(I, TopOpeBRepDS_OK);
      return Standard_True;
    }
    case TopOpeBRepDS_POINT: {
      if (myHDS->NbPoints() < I)
      {
        if (myMapPointStatus.IsBound(I))
          myMapPointStatus.UnBind(I);
        myMapPointStatus.Bind(I, TopOpeBRepDS_NOK);
        return Standard_False;
      }
      if (!myMapPointStatus.IsBound(I))
        myMapPointStatus.Bind(I, TopOpeBRepDS_OK);
      return Standard_True;
    }
    default:
      break;
  }

  // topology
  if (myHDS->NbShapes() < I)
  {
    if (myMapShapeStatus.IsBound(I))
      myMapShapeStatus.UnBind(I);
    myMapShapeStatus.Bind(I, TopOpeBRepDS_NOK);
    return Standard_False;
  }
  const TopoDS_Shape& S = myHDS->Shape(I);

  TopAbs_ShapeEnum se = TopAbs_COMPOUND;

  switch (K)
  {
    case TopOpeBRepDS_SOLID:
      se = TopAbs_SOLID;
      break;
    case TopOpeBRepDS_SHELL:
      se = TopAbs_SHELL;
      break;
    case TopOpeBRepDS_FACE:
      se = TopAbs_FACE;
      break;
    case TopOpeBRepDS_WIRE:
      se = TopAbs_WIRE;
      break;
    case TopOpeBRepDS_EDGE:
      se = TopAbs_EDGE;
      break;
    case TopOpeBRepDS_VERTEX:
      se = TopAbs_VERTEX;
      break;
    default:
      break;
  }
  if (S.ShapeType() != se)
  {
    if (myMapShapeStatus.IsBound(I))
      myMapShapeStatus.UnBind(I);
    myMapShapeStatus.Bind(I, TopOpeBRepDS_NOK);
    return Standard_False;
  }
  if (!myMapShapeStatus.IsBound(I))
    myMapShapeStatus.Bind(I, TopOpeBRepDS_OK);
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepDS_Check::ChkIntgSamDom()
{
  Standard_Boolean            b = Standard_True, bb = Standard_False;
  TopOpeBRepDS_DataStructure& BDS  = myHDS->ChangeDS();
  Standard_Integer            NbSh = myHDS->NbShapes(), i, Curr, Loc;
  for (i = 1; i <= NbSh; i++)
  {
    // Verifie que les Shapes de mySameDomaine existe bien dans la DS
    const TopoDS_Shape&         Sind = myHDS->Shape(i);
    const TopTools_ListOfShape& losi = BDS.ShapeSameDomain(Sind);
    if (!CheckShapes(losi))
    {
      b = Standard_False;
    }

    // Verification de SameDomaineRef
    Curr = BDS.SameDomainRef(i);
    Loc  = BDS.SameDomainRef(Curr);
    if (Curr && (Curr != Loc))
    {
      b = Standard_False;
    }

    if (Curr)
    {
      // Verification du type des differents Shapes SameDomain
      const TopoDS_Shape& Sref = myHDS->Shape(Curr);
      if (Sind.ShapeType() != Sref.ShapeType())
      {
        b = Standard_False;
      }

      // Verifier que ShapeSameDomain(Sref) contient bien Sind
      // sauf si Sind == Sref
      if (i != Curr)
      {
        const TopTools_ListOfShape&        losr = BDS.ShapeSameDomain(Sref);
        TopTools_ListIteratorOfListOfShape liolos;
        liolos.Initialize(losr);
        while (liolos.More())
        {
          const TopoDS_Shape& Sh = liolos.Value();
          Loc                    = myHDS->Shape(Sh);
          if (Loc == i)
          {
            bb = Standard_True;
            break;
          }
          liolos.Next();
        }
        if (!bb)
        {
          b = Standard_False;
        }
      }
    }
  }
  return b;
}

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

Standard_Boolean TopOpeBRepDS_Check::CheckShapes(const TopTools_ListOfShape& LS) const
{
  Standard_Integer                   index;
  TopTools_ListIteratorOfListOfShape it(LS);
  while (it.More())
  {
    const TopoDS_Shape& itS = it.Value();
    index                   = myHDS->Shape(itS);
    if (!index)
      return Standard_False;
    it.Next();
  }
  return Standard_True;
}

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

Standard_Boolean TopOpeBRepDS_Check::OneVertexOnPnt()
{
  Standard_Boolean b = Standard_True;
  //  Standard_Integer i, j, k;
  Standard_Integer i, j;
  Standard_Integer Curr1, Curr2, sdr1, sdr2;
  Standard_Integer NbVe = 0, NbPo = myHDS->NbPoints();
  //  Standard_Real tol, tol1, tol2, Dist;
  Standard_Real               tol1, tol2, Dist;
  TColStd_IndexedMapOfInteger vert;
  vert.Clear();
  for (i = 1; i <= myHDS->NbShapes(); i++)
  {
    const TopoDS_Shape& S = myHDS->Shape(i);
    if ((S.ShapeType() == TopAbs_VERTEX) && myHDS->HasShape(S))
      vert.Add(i);
  }
  NbVe = vert.Extent();
  for (i = 1; i <= NbVe; i++)
  {
    Curr1                  = vert.FindKey(i);
    const TopoDS_Shape& S1 = myHDS->Shape(Curr1);
    sdr1                   = myHDS->SameDomainReference(S1);
    for (j = i + 1; j <= NbVe; j++)
    {
      Curr2                  = vert.FindKey(j);
      const TopoDS_Shape& S2 = myHDS->Shape(Curr2);
      sdr2                   = myHDS->SameDomainReference(S2);
      tol1                   = TopOpeBRepTool_ShapeTool::Tolerance(S1);
      tol2                   = TopOpeBRepTool_ShapeTool::Tolerance(S2);
      const gp_Pnt& P1       = TopOpeBRepTool_ShapeTool::Pnt(S1);
      const gp_Pnt& P2       = TopOpeBRepTool_ShapeTool::Pnt(S2);
      Dist                   = P1.Distance(P2);
      if (Dist <= tol1 + tol2)
      {
        if (sdr1 != sdr2)
        {
          b = Standard_False;
        }
      }
      else if (sdr1 == sdr2)
      {
        b = Standard_False;
      }
    }
    TopOpeBRepDS_PointExplorer PE(myHDS->DS());
    for (; PE.More(); PE.Next())
    {
      const TopOpeBRepDS_Point& dsPnt = PE.Point();
      const gp_Pnt&             Pnt1  = dsPnt.Point();
      tol1                            = dsPnt.Tolerance();
      tol2                            = TopOpeBRepTool_ShapeTool::Tolerance(S1);
      const gp_Pnt& Pnt2              = TopOpeBRepTool_ShapeTool::Pnt(S1);
      Dist                            = Pnt1.Distance(Pnt2);
      if (Dist <= tol1 + tol2)
      {
        b = Standard_False;
      }
    }
  }
  for (i = 1; i <= NbPo; i++)
  {
    TopOpeBRepDS_PointExplorer PE(myHDS->DS());
    if (PE.IsPoint(i))
    {
      const TopOpeBRepDS_Point& dsPnt1 = myHDS->Point(i);
      for (j = i + 1; j < NbPo; j++)
      {
        const TopOpeBRepDS_Point& dsPnt2 = myHDS->Point(j);
        if (dsPnt1.IsEqual(dsPnt2))
        {
        }
      }
    }
  }

  return b;
}

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

/// Standard_Boolean TopOpeBRepDS_Check::CheckEdgeParameter() const
Standard_Boolean CheckEdgeParameter(const Handle(TopOpeBRepDS_HDataStructure)& myHDS)
{
  TopOpeBRepDS_ListIteratorOfListOfInterference it1;
  const TopOpeBRepDS_DataStructure&             DS = myHDS->DS();
  Standard_Integer                              i, nshape = DS.NbShapes();
  Standard_Boolean                              IsOK = Standard_True;
  for (i = 1; i <= nshape; i++)
  {
    // Integrity of Interferences : Check parameter of EdgeInterferences
    const TopOpeBRepDS_ListOfInterference& LI = DS.ShapeInterferences(i);
    it1.Initialize(LI);
    while (it1.More())
    {
      Handle(TopOpeBRepDS_Interference)           I1 = it1.Value();
      Handle(TopOpeBRepDS_EdgeVertexInterference) EVI =
        Handle(TopOpeBRepDS_EdgeVertexInterference)::DownCast(I1);
      if (!EVI.IsNull())
      {
        Standard_Integer Param = (Standard_Integer)EVI->Parameter();
        if (Param > 1.e50)
        {
          IsOK = Standard_False;
        }
      }
      it1.Next();
    }
  }

  Standard_Integer ncurve = DS.NbCurves();
  for (i = 1; i <= ncurve; i++)
  {
    // Integrity of Interferences : Check parameter of CurvesInterferences
    const TopOpeBRepDS_ListOfInterference& LI = DS.CurveInterferences(i);
    it1.Initialize(LI);
    while (it1.More())
    {
      const Handle(TopOpeBRepDS_Interference)&    I1 = it1.Value();
      Handle(TopOpeBRepDS_CurvePointInterference) CPI(
        Handle(TopOpeBRepDS_CurvePointInterference)::DownCast(I1));
      if (!CPI.IsNull())
      {
        Standard_Integer Param = (Standard_Integer)TopOpeBRepDS_InterferenceTool::Parameter(CPI);
        if (Param > 1.e50)
        {
          IsOK = Standard_False;
        }
      }
      it1.Next();
    }
  }
  return IsOK;
}

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

Standard_OStream& TopOpeBRepDS_Check::PrintIntg(Standard_OStream& OS)
{
  OS << std::endl << std::endl << "************************************************" << std::endl;
  OS << "state of the DS : (only the tested elements)" << std::endl << std::endl;

  // Display of the geometry
  PrintMap(myMapSurfaceStatus, "Surface", OS);
  PrintMap(myMapCurveStatus, "Curve", OS);
  PrintMap(myMapPointStatus, "Point", OS);

  // display of the topology
  TopOpeBRepDS_DataMapOfCheckStatus MapVertex, MapEdge, MapWire, MapFace, MapSolid;
  Standard_Integer                  i;
  // different Map keep their index of myMapShapeStatus
  for (TopOpeBRepDS_DataMapIteratorOfDataMapOfCheckStatus DMI(myMapShapeStatus); DMI.More();
       DMI.Next())
  {
    i                     = DMI.Key();
    const TopoDS_Shape& S = myHDS->Shape(i);
    switch (S.ShapeType())
    {
      case TopAbs_VERTEX:
        MapVertex.Bind(i, DMI.Value());
        break;
      case TopAbs_EDGE:
        MapEdge.Bind(i, DMI.Value());
        break;
      case TopAbs_WIRE:
        MapWire.Bind(i, DMI.Value());
        break;
      case TopAbs_FACE:
        MapFace.Bind(i, DMI.Value());
        break;
      case TopAbs_SOLID:
        MapSolid.Bind(i, DMI.Value());
        break;
      default:
        break;
    }
  }

  PrintMap(MapVertex, "Vertex", OS);
  PrintMap(MapEdge, "Edge", OS);
  PrintMap(MapWire, "Wire", OS);
  PrintMap(MapFace, "Face", OS);
  PrintMap(MapSolid, "Solid", OS);

  OS << std::endl << std::endl;
  return OS;
}

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

Standard_OStream& TopOpeBRepDS_Check::PrintMap(TopOpeBRepDS_DataMapOfCheckStatus& MapStat,
                                               const Standard_CString             eltstr,
                                               Standard_OStream&                  OS)
{
  TopOpeBRepDS_DataMapIteratorOfDataMapOfCheckStatus DMI(MapStat);
  DMI.Initialize(MapStat);
  if (DMI.More())
  {
    Standard_Boolean b = Standard_True;
    OS << " " << eltstr << "\t(/" << MapStat.Extent() << ")\tnumber ";

    PrintElts(MapStat, TopOpeBRepDS_OK, b, OS);
    if (!b)
      OS << " = OK" << std::endl;
    PrintElts(MapStat, TopOpeBRepDS_NOK, b, OS);
    if (!b)
      OS << " = NOK" << std::endl;
  }
  return OS;
}

//=======================================================================
// function : PrintElts
// purpose  : Print the elements in the state stat of MapStat
//=======================================================================
Standard_OStream& TopOpeBRepDS_Check::PrintElts(TopOpeBRepDS_DataMapOfCheckStatus& MapStat,
                                                const TopOpeBRepDS_CheckStatus     Stat,
                                                Standard_Boolean&                  b,
                                                Standard_OStream&                  OS)
{
  Standard_Boolean bb = !b;
  b                   = Standard_True;
  Standard_Integer         i;
  TopOpeBRepDS_CheckStatus s;
  for (TopOpeBRepDS_DataMapIteratorOfDataMapOfCheckStatus DMI(MapStat); DMI.More(); DMI.Next())
  {
    s = DMI.Value();
    i = DMI.Key();
    if (s == Stat)
    {
      if (b)
        b = !b;
      if (bb)
      {
        OS << "\t\tnumber ";
        bb = !bb;
      }
      OS << i << " ";
    }
  }
  return OS;
}

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

Standard_OStream& TopOpeBRepDS_Check::Print(const TopOpeBRepDS_CheckStatus stat,
                                            Standard_OStream&              OS)
{
  switch (stat)
  {
    case TopOpeBRepDS_OK:
      OS << "OK";
      break;
    case TopOpeBRepDS_NOK:
      OS << "NOK";
      break;
    default:
      break;
  }
  return OS;
}

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

Standard_OStream& TopOpeBRepDS_Check::PrintShape(const TopAbs_ShapeEnum SE, Standard_OStream& OS)
{
  switch (SE)
  {
    case TopAbs_SOLID:
      OS << "Solid ";
      break;
    case TopAbs_SHELL:
      OS << "Shell ";
      break;
    case TopAbs_FACE:
      OS << "Face  ";
      break;
    case TopAbs_WIRE:
      OS << "Wire  ";
      break;
    case TopAbs_EDGE:
      OS << "Edge  ";
      break;
    case TopAbs_VERTEX:
      OS << "Vertex";
      break;
    default:
      break;
  }
  return OS;
}

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

Standard_OStream& TopOpeBRepDS_Check::PrintShape(const Standard_Integer index, Standard_OStream& OS)
{
  if (myHDS->NbShapes() < index)
  {
    OS << "**PB**IN**TopOpeBRepDS_Check::PrintShape** ";
    return OS;
  }
  if (!myMapShapeStatus.IsBound(index))
  {
    OS << "NO CHECK HAS PROCESSING" << std::endl;
    return OS;
  }
  OS << " ";
  myHDS->Shape(index);

  return OS;
}

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

const Handle(TopOpeBRepDS_HDataStructure)& TopOpeBRepDS_Check::HDS() const
{
  return myHDS;
}

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

Handle(TopOpeBRepDS_HDataStructure)& TopOpeBRepDS_Check::ChangeHDS()
{
  return myHDS;
}
