# from OCC.Core.AIS import *
# from OCC.Core.Adaptor2d import *
# from OCC.Core.Adaptor3d import *
# from OCC.Core.Addons import *
# from OCC.Core.AdvApp2Var import *
# from OCC.Core.AdvApprox import *
# from OCC.Core.AppBlend import *
# from OCC.Core.AppCont import *
# from OCC.Core.AppDef import *
# from OCC.Core.AppParCurves import *
# from OCC.Core.AppStd import *
# from OCC.Core.AppStdL import *
# from OCC.Core.Approx import *
# from OCC.Core.ApproxInt import *
# from OCC.Core.Aspect import *
# from OCC.Core.BOPAlgo import *
# from OCC.Core.BOPCol import *
# from OCC.Core.BOPDS import *
# from OCC.Core.BOPInt import *
# from OCC.Core.BOPTools import *
from OCC.Core.BRep import *
from OCC.Core.BRepAdaptor import *
# from OCC.Core.BRepAlgo import *
from OCC.Core.BRepAlgoAPI import *
# from OCC.Core.BRepApprox import *
# from OCC.Core.BRepBlend import *
from OCC.Core.BRepBndLib import *
from OCC.Core.BRepBuilderAPI import *
# from OCC.Core.BRepCheck import *
# from OCC.Core.BRepClass import *
from OCC.Core.BRepClass3d import *
# from OCC.Core.BRepExtrema import *
# from OCC.Core.BRepFeat import *
# from OCC.Core.BRepFill import *
# from OCC.Core.BRepFilletAPI import *
from OCC.Core.BRepGProp import *
# from OCC.Core.BRepIntCurveSurface import *
# from OCC.Core.BRepLProp import *
# from OCC.Core.BRepLib import *
# from OCC.Core.BRepMAT2d import *
from OCC.Core.BRepMesh import *
# from OCC.Core.BRepOffset import *
from OCC.Core.BRepOffsetAPI import *
# from OCC.Core.BRepPrim import *
from OCC.Core.BRepPrimAPI import *
# from OCC.Core.BRepProj import *
from OCC.Core.BRepSweep import *
from OCC.Core.BRepTools import *
# from OCC.Core.BRepTopAdaptor import *
# from OCC.Core.BSplCLib import *
# from OCC.Core.BSplSLib import *
# from OCC.Core.BiTgte import *
# from OCC.Core.Bisector import *
# from OCC.Core.Blend import *
# from OCC.Core.BlendFunc import *
from OCC.Core.Bnd import *
# from OCC.Core.BndLib import *
# from OCC.Core.CDF import *
# from OCC.Core.CDM import *
# from OCC.Core.CPnts import *
# from OCC.Core.CSLib import *
# from OCC.Core.ChFi2d import *
# from OCC.Core.ChFi3d import *
# from OCC.Core.ChFiDS import *
# from OCC.Core.ChFiKPart import *
# from OCC.Core.Contap import *
# from OCC.Core.Convert import *
# from OCC.Core.Dico import *
# from OCC.Core.Draft import *
# from OCC.Core.DsgPrs import *
# from OCC.Core.Dynamic import *
# from OCC.Core.ElCLib import *
# from OCC.Core.ElSLib import *
# from OCC.Core.Expr import *
# from OCC.Core.ExprIntrp import *
# from OCC.Core.Extrema import *
# from OCC.Core.FEmTool import *
# from OCC.Core.FSD import *
# from OCC.Core.FairCurve import *
# from OCC.Core.FilletSurf import *
from OCC.Core.GC import *
# from OCC.Core.GCE2d import *
from OCC.Core.GCPnts import *
# from OCC.Core.GEOMAlgo import *
from OCC.Core.GProp import *
# from OCC.Core.GccAna import *
# from OCC.Core.GccEnt import *
# from OCC.Core.GccGeo import *
# from OCC.Core.GccInt import *
# from OCC.Core.GccIter import *
# from OCC.Core.Geom import *
# from OCC.Core.Geom2d import *
# from OCC.Core.Geom2dAPI import *
# from OCC.Core.Geom2dAdaptor import *
# from OCC.Core.Geom2dConvert import *
# from OCC.Core.Geom2dGcc import *
# from OCC.Core.Geom2dHatch import *
# from OCC.Core.Geom2dInt import *
# from OCC.Core.Geom2dLProp import *
from OCC.Core.GeomAPI import *
# from OCC.Core.GeomAbs import *
# from OCC.Core.GeomAdaptor import *
# from OCC.Core.GeomConvert import *
# from OCC.Core.GeomFill import *
# from OCC.Core.GeomInt import *
# from OCC.Core.GeomLProp import *
# from OCC.Core.GeomLib import *
# from OCC.Core.GeomPlate import *
from OCC.Core.GeomProjLib import *
# from OCC.Core.GeomToStep import *
# from OCC.Core.GeomTools import *
# from OCC.Core.GraphDS import *
# from OCC.Core.GraphTools import *
# from OCC.Core.Graphic3d import *
# from OCC.Core.HLRAlgo import *
# from OCC.Core.HLRAppli import *
# from OCC.Core.HLRBRep import *
# from OCC.Core.HLRTopoBRep import *
# from OCC.Core.Hatch import *
# from OCC.Core.HatchGen import *
# from OCC.Core.Hermit import *
from OCC.Core.IFSelect import *
# from OCC.Core.IGESCAFControl import *
from OCC.Core.IGESControl import *
# from OCC.Core.Image import *
# from OCC.Core.IncludeLibrary import *
# from OCC.Core.IntAna import *
# from OCC.Core.IntAna2d import *
# from OCC.Core.IntCurve import *
# from OCC.Core.IntCurveSurface import *
# from OCC.Core.IntCurvesFace import *
# from OCC.Core.IntImp import *
# from OCC.Core.IntImpParGen import *
# from OCC.Core.IntPatch import *
# from OCC.Core.IntPoly import *
# from OCC.Core.IntPolyh import *
# from OCC.Core.IntRes2d import *
# from OCC.Core.IntStart import *
# from OCC.Core.IntSurf import *
# from OCC.Core.IntTools import *
# from OCC.Core.IntWalk import *
# from OCC.Core.Interface import *
# from OCC.Core.InterfaceGraphic import *
# from OCC.Core.Intf import *
# from OCC.Core.Intrv import *
# from OCC.Core.LProp import *
# from OCC.Core.LProp3d import *
# from OCC.Core.Law import *
# from OCC.Core.LocOpe import *
# from OCC.Core.LocalAnalysis import *
# from OCC.Core.MAT import *
# from OCC.Core.MAT2d import *
# from OCC.Core.MMgt import *
# from OCC.Core.Materials import *
# from OCC.Core.MeshVS import *
# from OCC.Core.Message import *
# from OCC.Core.NCollection import *
# from OCC.Core.NIS import *
# from OCC.Core.NLPlate import *
# from OCC.Core.OSD import *
# from OCC.Core.PCDM import *
# from OCC.Core.PLib import *
# from OCC.Core.Plate import *
# from OCC.Core.Plugin import *
from OCC.Core.Poly import *
from OCC.Core.Precision import *
# from OCC.Core.Primitives import *
# from OCC.Core.ProjLib import *
# from OCC.Core.Prs3d import *
# from OCC.Core.PrsMgr import *
# from OCC.Core.Quantity import *
# from OCC.Core.RWStepAP203 import *
# from OCC.Core.RWStepAP214 import *
# from OCC.Core.RWStepBasic import *
# from OCC.Core.RWStepDimTol import *
# from OCC.Core.RWStepElement import *
# from OCC.Core.RWStepFEA import *
# from OCC.Core.RWStepGeom import *
# from OCC.Core.RWStepRepr import *
# from OCC.Core.RWStepShape import *
# from OCC.Core.RWStepVisual import *
# from OCC.Core.RWStl import *
# from OCC.Core.Resource import *
# from OCC.Core.STEPCAFControl import *
# from OCC.Core.STEPConstruct import *
from OCC.Core.STEPControl import *
# from OCC.Core.STEPEdit import *
# from OCC.Core.STEPSelections import *
# from OCC.Core.Select3D import *
# from OCC.Core.SelectBasics import *
# from OCC.Core.SelectMgr import *
# from OCC.Core.ShapeAlgo import *
# from OCC.Core.ShapeAnalysis import *
# from OCC.Core.ShapeBuild import *
# from OCC.Core.ShapeConstruct import *
# from OCC.Core.ShapeCustom import *
# from OCC.Core.ShapeExtend import *
# from OCC.Core.ShapeFix import *
# from OCC.Core.ShapeProcess import *
# from OCC.Core.ShapeProcessAPI import *
# from OCC.Core.ShapeUpgrade import *
# from OCC.Core.SortTools import *
# from OCC.Core.Standard import *
# from OCC.Core.StdFail import *
# from OCC.Core.StdPrs import *
# from OCC.Core.StdSelect import *
# from OCC.Core.StepAP203 import *
# from OCC.Core.StepAP209 import *
# from OCC.Core.StepAP214 import *
# from OCC.Core.StepBasic import *
# from OCC.Core.StepDimTol import *
# from OCC.Core.StepElement import *
# from OCC.Core.StepFEA import *
# from OCC.Core.StepGeom import *
# from OCC.Core.StepRepr import *
# from OCC.Core.StepShape import *
# from OCC.Core.StepToGeom import *
# from OCC.Core.StepToTopoDS import *
# from OCC.Core.StepVisual import *
from OCC.Core.StlAPI import *
from OCC.Core.StlMesh import *
# from OCC.Core.StlTransfer import *
# from OCC.Core.Storage import *
# from OCC.Core.Sweep import *
# from OCC.Core.TColGeom import *
# from OCC.Core.TColGeom2d import *
# from OCC.Core.TColQuantity import *
from OCC.Core.TColStd import *
from OCC.Core.TColgp import *
# from OCC.Core.TCollection import *
# from OCC.Core.TDF import *
# from OCC.Core.TDataStd import *
# from OCC.Core.TDataXtd import *
# from OCC.Core.TDocStd import *
# from OCC.Core.TFunction import *
# from OCC.Core.TNaming import *
# from OCC.Core.TPrsStd import *
# from OCC.Core.TShort import *
from OCC.Core.TopAbs import *
# from OCC.Core.TopBas import *
# from OCC.Core.TopClass import *
# from OCC.Core.TopCnx import *
from OCC.Core.TopExp import *
from OCC.Core.TopLoc import *
# from OCC.Core.TopOpeBRep import *
# from OCC.Core.TopOpeBRepBuild import *
# from OCC.Core.TopOpeBRepDS import *
# from OCC.Core.TopOpeBRepTool import *
from OCC.Core.TopTools import *
# from OCC.Core.TopTrans import *
from OCC.Core.TopoDS import *
# from OCC.Core.TopoDSToStep import *
# from OCC.Core.Transfer import *
# from OCC.Core.TransferBRep import *
# from OCC.Core.UTL import *
# from OCC.Core.Units import *
# from OCC.Core.UnitsAPI import *
# from OCC.Core.V3d import *
# from OCC.Core.Visual3d import *
# from OCC.Core.Visualization import *
# from OCC.Core.Voxel import *
# from OCC.Core.XBRepMesh import *
# from OCC.Core.XCAFApp import *
# from OCC.Core.XCAFDoc import *
# from OCC.Core.XCAFPrs import *
# from OCC.Core.XSControl import *
# from OCC.Core.gce import *
from OCC.Core.gp import *
# from OCC.Core.math import *

import tempfile
import pathlib
from PySide2.QtCore import *
from PySide2.QtWidgets import *
import vtk
import vmi
import numpy as np

import warnings

from typing import List


def ignore_warnings(*args):
    pass


warnings.simplefilter("ignore")
warnings.simplefilter = ignore_warnings

tr = QObject()
tr = tr.tr

ccType = [TopAbs_COMPOUND,
          TopAbs_COMPSOLID,
          TopAbs_SOLID,
          TopAbs_SHELL,
          TopAbs_FACE,
          TopAbs_WIRE,
          TopAbs_EDGE,
          TopAbs_VERTEX,
          TopAbs_SHAPE]

ccTypeCast = {TopAbs_COMPOUND: topods.Compound,
              TopAbs_COMPSOLID: topods.CompSolid,
              TopAbs_SOLID: topods.Solid,
              TopAbs_SHELL: topods.Shell,
              TopAbs_FACE: topods.Face,
              TopAbs_WIRE: topods.Wire,
              TopAbs_EDGE: topods.Edge,
              TopAbs_VERTEX: topods.Vertex}


def ccExplore_Recursive(sh: TopoDS_Shape, target_type: int = ccType[-1]):
    shs, exp = [], TopExp_Explorer(sh, target_type)
    while exp.More():
        shs.append(exp.Current())
        exp.Next()
    return shs


def ccExplore_Once(sh: TopoDS_Shape):
    shs, it = [], TopoDS_Iterator(sh)
    while it.More():
        shs.append(it.Value())
        it.Next()
    return shs


def ccOpenFile_IGES(file: str = None):
    if file is None:
        file = vmi.askOpenFile(tr('导入 (Import) IGES'), '*.igs')
        if file is None:
            return
    r = IGESControl_Reader()
    if r.ReadFile(file):
        r.TransferRoots()
        sh: TopoDS_Shape = r.OneShape()
        sh_sub = ccExplore_Once(sh)
        if sh.ShapeType() is TopAbs_COMPOUND and len(sh_sub) == 1:
            sh = sh_sub[0]
        return sh


def ccOpenFile_STEP(file: str = None):
    if file is None:
        file = vmi.askOpenFile(tr('导入 (Import) STEP'), '*.stp')
        if file is None:
            return
    r = STEPControl_Reader()
    if IFSelect_RetDone == r.ReadFile(file):
        r.TransferRoots()
        sh: TopoDS_Shape = r.OneShape()
        sh_sub = ccExplore_Once(sh)
        if sh.ShapeType() is TopAbs_COMPOUND and len(sh_sub) == 1:
            sh = sh_sub[0]
        return sh


def ccSaveFile_IGES(sh: TopoDS_Shape, file=None):
    if file is None:
        file = vmi.askSaveFile(tr('导出 (Export) IGES'), '*.igs')
        if file is None:
            return
    w = IGESControl_Writer()
    w.AddShape(sh)
    return w.Write(file)


def ccSaveFile_STEP(sh: TopoDS_Shape, file=None):
    if file is None:
        file = vmi.askSaveFile(tr('导出 (Export) STEP'), '*.stp')
        if file is None:
            return
    w = STEPControl_Writer()
    w.Transfer(sh, STEPControl_AsIs)
    status = w.Write(file)
    if status == IFSelect_RetDone:
        return True


def ccSaveFile_STL(sh: TopoDS_Shape, file=None):
    if file is None:
        file = vmi.askSaveFile(tr('导出 (Export) STL'), '*.stl')
        if file is None:
            return
    return stlapi.Write(sh, file, False)


def ccPd_Sh(sh: TopoDS_Shape):
    if sh.IsNull():
        return vtk.vtkPolyData()

    pts = vtk.vtkPoints()
    polydata = vtk.vtkPolyData()
    polydata.SetPoints(pts)
    polydata.Allocate()

    # 1. 离散化
    breptools.Clean(sh)

    discrete = BRepMesh_IncrementalMesh(sh, 1e-3, True, 0.5, True)
    discrete.Perform()

    # 2. 转换Vertex
    exp = TopExp_Explorer(sh, TopAbs_VERTEX)
    while exp.More():
        vertex = topods.Vertex(exp.Current())
        exp.Next()

        pnt = BRep_Tool.Pnt(vertex)
        id = polydata.GetPoints().InsertNextPoint(pnt.X(), pnt.Y(), pnt.Z())
        polydata.InsertNextCell(vtk.VTK_VERTEX, 1, [id])

    # 3. 转换Edge
    exp = TopExp_Explorer(sh, TopAbs_EDGE)
    while exp.More():
        edge = topods.Edge(exp.Current())
        exp.Next()

        if edge.IsNull() or BRep_Tool.Degenerated(edge):
            continue

        p_on_tri = Handle_Poly_PolygonOnTriangulation()
        triangulation = Handle_Poly_Triangulation()
        loc = TopLoc_Location()
        BRep_Tool.PolygonOnTriangulation(edge, p_on_tri, triangulation, loc, 1)
        p_3d = Handle_Poly_Polygon3D()

        if p_on_tri.IsNull():
            p_3d = BRep_Tool.Polygon3D(None, edge, loc)

        if p_3d.IsNull() and p_on_tri.IsNull():
            continue

        trsf = loc.Transformation()

        if not p_3d.IsNull():
            n = p_3d.NbNodes()
            pnts = p_3d.Nodes()
            ids = list(range(1, n + 1))
        else:
            n = p_on_tri.NbNodes()
            pnts = triangulation.Nodes()
            ids = p_on_tri.Nodes()
            ids = [ids.Value(i) for i in range(ids.Lower(), ids.Upper() + 1)]

        if n > 1:
            idList = vtk.vtkIdList()
            for i in ids:
                pnt = pnts.Value(i)
                pnt.Transform(trsf)
                id = polydata.GetPoints().InsertNextPoint(pnt.X(), pnt.Y(), pnt.Z())
                idList.InsertNextId(id)
            polydata.InsertNextCell(vtk.VTK_POLY_LINE, idList)

    # 4. 转换Face并合并
    exp = TopExp_Explorer(sh, TopAbs_FACE)
    if exp.More():
        with tempfile.TemporaryDirectory() as p:
            p = pathlib.Path(p) / '.stl'
            stlapi.Write(sh, str(p), False)

            r = vtk.vtkSTLReader()
            r.SetFileName(str(p))
            r.Update()

            a = vtk.vtkAppendPolyData()
            a.AddInputData(r.GetOutput())
            a.AddInputData(polydata)
            a.Update()

            c = vtk.vtkCleanPolyData()
            c.SetInputData(a.GetOutput())
            c.Update()

            polydata.DeepCopy(a.GetOutput())

    return polydata


def ccSh_Pd(pd: vtk.vtkPolyData):
    builder, compound = BRep_Builder(), TopoDS_Compound()
    builder.MakeCompound(compound)

    triangles = vtk.vtkTriangleFilter()
    triangles.SetInputData(pd)
    triangles.Update()
    triangles = triangles.GetOutput()

    for i in range(triangles.GetNumberOfCells()):
        cell: vtk.vtkCell = triangles.GetCell(i)
        if cell.GetCellType() == vtk.VTK_TRIANGLE:
            ids: vtk.vtkIdList = cell.GetPointIds()
            pts = [triangles.GetPoint(ids.GetId(0)),
                   triangles.GetPoint(ids.GetId(1)),
                   triangles.GetPoint(ids.GetId(2))]
            face = ccFace(ccWire(ccSegments(pts, True)))
            builder.Add(compound, face)

    sewing = BRepBuilderAPI_Sewing()
    sewing.Load(compound)
    sewing.Perform()

    sh = sewing.SewedShape()

    if sh.ShapeType() is TopAbs_SHELL and sh.Closed():
        return ccSolid(sh)
    else:
        return sh


def ccPnt(arg):
    return gp_Pnt(arg[0], arg[1], arg[2])


def ccVec(arg):
    return gp_Vec(arg[0], arg[1], arg[2])


def ccDir(arg):
    return gp_Dir(arg[0], arg[1], arg[2])


def ccAx2(origin, z, x=None):
    args = [ccPnt(origin), ccDir(z)]
    if x is not None:
        args.append(ccDir(x))
    return gp_Ax2(*args)


def ccSegment(pt0, pt1):
    pt0 = pt0 if isinstance(pt0, gp_Pnt) else ccPnt(pt0)
    pt1 = pt1 if isinstance(pt1, gp_Pnt) else ccPnt(pt1)
    return GC_MakeSegment(pt0, pt1).Value()


def ccSegments(pts, closed=False):
    pnts_ = [pnt for pnt in pts]
    if closed:
        pnts_.append(pts[0])
    return [ccSegment(pnts_[i], pnts_[i + 1]) for i in range(len(pnts_) - 1)]


def ccBSpline(pts, tgs=None, tgflags=None, closed=False, scale=True, tol=precision.Confusion()):
    if len(pts) < 2:
        return None

    if pts is not None and not isinstance(pts, TColgp_HArray1OfPnt):
        _ = TColgp_HArray1OfPnt(1, len(pts))
        for i in range(len(pts)):
            if isinstance(pts[i], gp_Pnt):
                _.SetValue(i + 1, pts[i])
            else:
                _.SetValue(i + 1, ccPnt(pts[i]))
        pts = _

    if tgs is not None and not isinstance(tgs, TColgp_Array1OfVec):
        _ = TColgp_Array1OfVec(1, len(tgs))
        for i in range(len(tgs)):
            if isinstance(pts[i], gp_Vec):
                _.SetValue(i + 1, tgs[i])
            else:
                _.SetValue(i + 1, ccVec(tgs[i]))
        tgs = _

    if tgs is not None and tgflags is None:
        tgflags = [True for _ in tgs]

    if tgflags is not None and not isinstance(tgflags, TColStd_HArray1OfBoolean):
        _ = TColStd_HArray1OfBoolean(1, len(tgflags))
        for i in range(len(tgflags)):
            _.SetValue(i + 1, tgflags[i])
        tgflags = _

    lower, upper = pts.Lower(), pts.Upper() + (1 if closed else 0)
    knots = TColStd_HArray1OfReal(lower, upper)
    knots.SetValue(lower, 0)

    for i in range(lower, upper):
        if closed and i == upper - 1:
            var = pts.Value(i).Distance(pts.Value(lower)) ** 0.5
        else:
            var = pts.Value(i).Distance(pts.Value(i + 1)) ** 0.5
        knots.SetValue(i + 1, knots.Value(i) + var)

    curve = GeomAPI_Interpolate(pts, knots, closed, tol)
    if tgs is not None:
        curve.Load(tgs, tgflags, scale)
    curve.Perform()
    return curve.Curve() if curve.IsDone() else None


def ccBSpline_Kochanek(pts, closed=False, tol=precision.Confusion()):
    if len(pts) < 2:
        return None

    points = vtk.vtkPoints()
    for pt in pts:
        points.InsertNextPoint(pt)

    ks = [vtk.vtkKochanekSpline(), vtk.vtkKochanekSpline(), vtk.vtkKochanekSpline()]

    ps = vtk.vtkParametricSpline()
    ps.SetXSpline(ks[0])
    ps.SetYSpline(ks[1])
    ps.SetZSpline(ks[2])
    ps.SetPoints(points)
    ps.SetClosed(1 if closed else 0)
    ps.SetParameterizeByLength(1)
    ps.SetLeftConstraint(1)
    ps.SetRightConstraint(1)

    n = 100 * len(pts)

    pf = vtk.vtkParametricFunctionSource()
    pf.SetParametricFunction(ps)
    pf.SetUResolution(n)
    pf.SetVResolution(n)
    pf.SetWResolution(n)
    pf.Update()

    pts_, points = [], pf.GetOutput().GetPoints()

    for i in range(points.GetNumberOfPoints() - (1 if closed else 0)):
        pts_.append(points.GetPoint(i))
    pts_[0] = pts[0].copy()
    if not closed:
        pts_[-1] = pts[-1].copy()

    curve = ccBSpline(pts_, closed=closed, tol=tol)

    obb = vmi.pdOBB_Pts(points)
    if 0 < obb['size'][2] < tol:
        curve = ccCurveOnPlane(curve, obb['center'], obb['axis'][2])
    return curve


def ccCurveOnPlane(curve, origin, normal, vt=None):
    if vt is None:
        vt = normal
    return geomprojlib.ProjectOnPlane(curve, ccPlane(origin, normal), ccDir(vt), True)


def ccCircle_Ax2(ax2, radius):
    mk = GC_MakeCircle(ax2, radius)
    return mk.Value() if mk.IsDone() else None


def ccCircle_CS(center, radius, cs: vmi.CS4x4):
    ax2 = ccAx2(center, cs.axis(2), cs.axis(0))
    return ccCircle_Ax2(ax2, radius)


def ccPolygonRegular_CS(center, radius, n, cs: vmi.CS4x4):
    pts, center, cs = [], np.array(center[:3]), cs.copy()
    cs.translate(radius * cs.axis(0))

    for i in range(n):
        cs.rotate(360 / n, cs.axis(2))
        pt = center + radius * cs.axis(0)
        pts.append(pt)

    return ccSegments(pts, True)


def ccPlane(origin, normal):
    mk = GC_MakePlane(ccPnt(origin), ccDir(normal))
    return mk.Value() if mk.IsDone() else None


def ccVertex(pt):
    pt = pt if isinstance(pt, gp_Pnt) else ccPnt(pt)
    mk = BRepBuilderAPI_MakeVertex(pt)
    mk.Build()
    return mk.Vertex() if mk.IsDone() else None


def ccEdge(curve):
    mk = BRepBuilderAPI_MakeEdge(curve)
    mk.Build()
    return mk.Edge() if mk.IsDone() else None


def ccEdges(curves):
    return [ccEdge(curve) for curve in curves]


def ccWire(edges):
    edges_ = edges
    if not hasattr(edges_, '__iter__'):
        edges_ = [edges_]
    edges_ = [edge if isinstance(edge, TopoDS_Edge) else ccEdge(edge) for edge in edges_]
    mk = BRepBuilderAPI_MakeWire()
    for edge in edges_:
        mk.Add(edge)
    mk.Build()
    return mk.Wire() if mk.IsDone() else None


def ccWires(edges):
    return [ccWire(edge) for edge in edges]


def ccFace(wire):
    mk = BRepBuilderAPI_MakeFace(wire)
    mk.Build()
    return mk.Face() if mk.IsDone() else None


def ccFaceOnPlane(wire, origin, normal):
    mk = BRepBuilderAPI_MakeFace(gp_Pln(ccPnt(origin), ccDir(normal)), wire)
    mk.Build()
    return mk.Face() if mk.IsDone() else None


def ccLoft(sections: List[TopoDS_Wire], ruled=False, redirect=False):
    mk = BRepOffsetAPI_ThruSections(False, ruled)
    mk.CheckCompatibility(redirect)
    for section in sections:
        if isinstance(section, TopoDS_Edge):
            section = ccWire(section)
        if isinstance(section, TopoDS_Wire):
            mk.AddWire(section)
        if isinstance(section, TopoDS_Vertex):
            mk.AddVertex(section)
    mk.Build()
    return mk.Shape() if mk.IsDone() else None


def ccSew(shs: List[TopoDS_Shape]):
    mk = BRepBuilderAPI_Sewing()
    for sh in shs:
        mk.Add(sh)
    mk.Perform()
    return mk.SewedShape()


def ccSolid(shells):
    if isinstance(shells, TopoDS_Shell):
        shells = [shells]
    mk = BRepBuilderAPI_MakeSolid()
    for shell in shells:
        if shell.ShapeType() is TopAbs_SHELL:
            mk.Add(topods.Shell(shell))
    if mk.IsDone():
        s = mk.Solid()

        if ccInside_Pt(s):
            s.Reverse()
        return s


def ccBoolean_Union(sh: List[TopoDS_Shape]):
    if len(sh) == 0:
        return None
    elif len(sh) == 1:
        return sh[0]
    elif len(sh) > 1:
        sh = [sh[:1], sh[1:]]
        shape_list = [TopTools_ListOfShape(), TopTools_ListOfShape()]

        for j in range(2):
            for i in sh[j]:
                shape_list[j].Append(i)

        mk = BRepAlgoAPI_Fuse()
        mk.SetArguments(shape_list[0])
        mk.SetTools(shape_list[1])
        mk.SetRunParallel(True)
        mk.Build()

        if not mk.IsDone() or mk.ErrorStatus() or mk.WarningStatus():
            raise Exception('ccBoolean_Union', mk.ErrorStatus(), mk.WarningStatus())
        return mk.Shape()


def ccBoolean_Difference(sh: List[TopoDS_Shape]):
    if len(sh) == 0:
        return None
    elif len(sh) == 1:
        return sh[0]
    elif len(sh) > 1:
        sh = [sh[:1], sh[1:]]
        shape_list = [TopTools_ListOfShape(), TopTools_ListOfShape()]

        for j in range(2):
            for i in sh[j]:
                shape_list[j].Append(i)

        mk = BRepAlgoAPI_Cut()
        mk.SetArguments(shape_list[0])
        mk.SetTools(shape_list[1])
        mk.SetRunParallel(True)
        mk.Build()

        if not mk.IsDone() or mk.ErrorStatus() or mk.WarningStatus():
            raise Exception('ccBoolean_Difference', mk.ErrorStatus(), mk.WarningStatus())
        return mk.Shape()


def ccBoolean_Intersection(sh: List[TopoDS_Shape]):
    if len(sh) == 0:
        return None
    elif len(sh) == 1:
        return sh[0]
    elif len(sh) > 1:
        sh = [sh[:1], sh[1:]]
        shape_list = [TopTools_ListOfShape(), TopTools_ListOfShape()]

        for j in range(2):
            for i in sh[j]:
                shape_list[j].Append(i)

        mk = BRepAlgoAPI_Common()
        mk.SetArguments(shape_list[0])
        mk.SetTools(shape_list[1])
        mk.SetRunParallel(True)
        mk.Build()

        if not mk.IsDone() or mk.ErrorStatus() or mk.WarningStatus():
            raise Exception('ccBoolean_Intersection', mk.ErrorStatus(), mk.WarningStatus())
        return mk.Shape()


def ccSection(sh: List[TopoDS_Shape]):
    if len(sh) == 0:
        return None
    elif len(sh) == 1:
        return sh[0]
    elif len(sh) > 1:
        sh = [sh[:1], sh[1:]]
        shape_list = [TopTools_ListOfShape(), TopTools_ListOfShape()]

        for j in range(2):
            for i in sh[j]:
                shape_list[j].Append(i)

        mk = BRepAlgoAPI_Section()
        mk.SetArguments(shape_list[0])
        mk.SetTools(shape_list[1])
        mk.SetRunParallel(True)
        mk.Build()

        if not mk.IsDone() or mk.ErrorStatus() or mk.WarningStatus():
            raise Exception('ccSection', mk.ErrorStatus(), mk.WarningStatus())
        return mk.Shape()


def ccCylinder(radius, height, origin, z, x=None):
    mk = BRepPrimAPI_MakeCylinder(ccAx2(origin, z, x), radius, height)
    mk.Build()
    return mk.Shape() if mk.IsDone() else None


def ccPrism(sh, vt, l):
    vt = np.array(vt)
    vec = l * vt / np.linalg.norm(vt)
    vec = gp_Vec(vec[0], vec[1], vec[2])
    mk = BRepPrimAPI_MakePrism(sh, vec)
    mk.Build()
    return mk.Shape() if mk.IsDone() else None


def ccOffset(sh, offset):
    mk = BRepOffsetAPI_MakeOffsetShape(sh, offset, precision.Confusion())
    mk.Build()
    return mk.Shape() if mk.IsDone() else None


def ccRayCast(sh, pt, vt):
    """把pt沿vt方向投射到sh上"""
    pt, vt = np.array(pt), np.array(vt)

    def dmax(s0, s1):
        b = [Bnd_Box(), Bnd_Box()]
        brepbndlib.Add(s0, b[0])
        brepbndlib.Add(s1, b[1])

        min = [[b[0].Get()[i] for i in range(3)],
               [b[1].Get()[i] for i in range(3)]]
        max = [[b[0].Get()[i] for i in range(3, 6)],
               [b[1].Get()[i] for i in range(3, 6)]]
        xyz = [[gp_XYZ(min[0][0], min[0][1], min[0][2]),
                gp_XYZ(max[0][0], max[0][1], max[0][2])],
               [gp_XYZ(min[1][0], min[1][1], min[1][2]),
                gp_XYZ(max[1][0], max[1][1], max[1][2])]]
        delta = [xyz[0][1].Subtracted(xyz[0][0]),
                 xyz[1][1].Subtracted(xyz[1][0])]

        def dmin(s0, s1):
            b = [Bnd_Box(), Bnd_Box()]
            brepbndlib.Add(s0, b[0])
            brepbndlib.Add(s1, b[1])
            return b[0].Distance(b[1])

        return delta[0].Modulus() + delta[1].Modulus() + dmin(s0, s1)

    d = dmax(ccVertex(pt), sh)
    vtmax = d * vt / np.linalg.norm(vt)

    sweep = BRepSweep_Prism(ccVertex(pt), vtmax)
    section = ccSection([sh, sweep.Shape()])

    origin = ccPnt(pt)

    pts = {}
    for s in ccExplore_Recursive(section, TopAbs_VERTEX):
        pnt = BRep_Tool.Pnt(s)
        pts[pnt.Distance(origin)] = [pnt.X(), pnt.Y(), pnt.Z()]
    return [pts[i] for i in sorted(pts)]


def ccUniformPts_Edge(edge, n, u=None):
    if n < 2:
        return
    curve = BRepAdaptor_Curve(edge)
    mk = GCPnts_QuasiUniformAbscissa(curve, n)
    if mk.IsDone():
        pts = [curve.Value(mk.Parameter(i)) for i in range(1, n + 1)]
        return [[pt.X(), pt.Y(), pt.Z()] for pt in pts]


def ccEdge_FirstLastVertex(edge):
    pts = [TopoDS_Vertex(), TopoDS_Vertex()]
    topexp.Vertices(edge, pts[0], pts[1])
    return pts


def ccEdge_FirstLastPoint(edge):
    pts = ccEdge_FirstLastVertex(edge)
    pts = [BRep_Tool.Pnt(pt) for pt in pts]
    pts = [[pt.X(), pt.Y(), pt.Z()] for pt in pts]
    return pts


def ccLength(sh):
    p = GProp_GProps()
    brepgprop.LinearProperties(sh, p)
    return p.Mass()


def ccArea(sh):
    p = GProp_GProps()
    brepgprop.SurfaceProperties(sh, p)
    return p.Mass()


def ccInside_Pt(sh, pt=None):
    c = BRepClass3d_SolidClassifier(sh)
    if pt is None:
        c.PerformInfinitePoint(precision.Confusion())
    else:
        c.Perform(ccPnt(pt), precision.Confusion())

    if c.State() == TopAbs_IN or c.State() == TopAbs_ON:
        return True
    return False
