import multiprocessing

from ifcopenshell import SchemaError, file, ifcopenshell_wrapper, open, sqlite
from ifcopenshell.geom import iterator, settings

from specklepy.logging.exceptions import SpeckleException


def _create_iterator_settings() -> settings:
    ifc_settings = settings()
    # triangles for now, speckle does support n-gons, but may be less performant
    ifc_settings.set("triangulation-type", ifcopenshell_wrapper.TRIANGLE_MESH)
    # no need to weld verts
    ifc_settings.set("weld-vertices", False)
    #
    ifc_settings.set("use-world-coords", False)
    ifc_settings.set("permissive-shape-reuse", True)

    # Tiny performance improvement,
    ifc_settings.set("no-wire-intersection-check", True)
    # Rendermaterials inherit the material names instead of type + unique id
    ifc_settings.set("use-material-names", True)

    # IfcOpenshell defaults to 0.001mm here, which leads to very dense meshes.
    # lowering the mesh quality a bit here leads to meshes
    # that are still much higher quality than webifc

    # We still need to experiment with the affect on memory usage
    # It may be desirable to lower this further, and increase the angular deflection
    # to compensate. This would allow large meshes to be lower quality,
    # while keeping small meshes relatively similar.
    ifc_settings.set("mesher-linear-deflection", 0.2)

    return ifc_settings


def open_ifc(file_path: str) -> file:
    try:
        ifc_file = open(file_path)
    except SchemaError:
        raise
    except FileNotFoundError:
        raise
    except Exception as ex:
        raise SpeckleException("File could not be opened as an IFC file") from ex

    if isinstance(ifc_file, file):
        return ifc_file
    else:
        raise SpeckleException(f"file at {file_path} is not a compatible ifc file type")


def create_geometry_iterator(ifc_file: file | sqlite) -> iterator:
    return iterator(_create_iterator_settings(), ifc_file, multiprocessing.cpu_count())
