# SPDX-License-Identifier: LGPL-2.1-or-later

# /***************************************************************************
# *   Copyright (c) 2016 Victor Titov (DeepSOIC) <vv.titov@gmail.com>       *
# *                                                                         *
# *   This file is part of the FreeCAD CAx development system.              *
# *                                                                         *
# *   This library is free software; you can redistribute it and/or         *
# *   modify it under the terms of the GNU Library General Public           *
# *   License as published by the Free Software Foundation; either          *
# *   version 2 of the License, or (at your option) any later version.      *
# *                                                                         *
# *   This library  is distributed in the hope that it will be useful,      *
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
# *   GNU Library General Public License for more details.                  *
# *                                                                         *
# *   You should have received a copy of the GNU Library General Public     *
# *   License along with this library; see the file COPYING.LIB. If not,    *
# *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
# *   Suite 330, Boston, MA  02111-1307, USA                                *
# *                                                                         *
# ***************************************************************************/

__title__ = "BOPTools.SplitAPI module"
__author__ = "DeepSOIC"
__url__ = "https://www.freecad.org"
__doc__ = "Split functions that operate on list_of_shapes."

import Part
from . import ShapeMerge
from .GeneralFuseResult import GeneralFuseResult
from . import Utils
import FreeCAD


def booleanFragments(list_of_shapes, mode, tolerance=0.0):
    """booleanFragments(list_of_shapes, mode, tolerance = 0.0): functional part of
    BooleanFragments feature. It's just result of generalFuse plus a bit of
    post-processing.

    mode is a string. It can be "Standard", "Split" or "CompSolid".
    "Standard" - return generalFuse as is.
    "Split" - wires and shells will be split at intersections.
    "CompSolid" - solids will be extracted from result of generelFuse, and compsolids will
    be made from them; all other stuff is discarded."""

    pieces, map = list_of_shapes[0].generalFuse(list_of_shapes[1:], tolerance)
    if mode == "Standard":
        return pieces
    elif mode == "CompSolid":
        solids = pieces.Solids
        if len(solids) < 1:
            raise ValueError("No solids in the result. Can't make CompSolid.")
        elif len(solids) == 1:
            FreeCAD.Console.PrintWarning(
                "Part_BooleanFragments: only one solid in the result, generating trivial compsolid."
            )
        return ShapeMerge.mergeSolids(solids, bool_compsolid=True)
    elif mode == "Split":
        gr = GeneralFuseResult(list_of_shapes, (pieces, map))
        gr.splitAggregates()
        return Part.makeCompound(gr.pieces)
    else:
        raise ValueError("Unknown mode: {mode}".format(mode=mode))


def slice(base_shape, tool_shapes, mode, tolerance=0.0):
    """slice(base_shape, tool_shapes, mode, tolerance = 0.0): functional part of
    Slice feature. Splits base_shape into pieces based on intersections with tool_shapes.

    mode is a string. It can be "Standard", "Split" or "CompSolid".
    "Standard" - return like generalFuse: edges, faces and solids are split, but wires,
    shells, compsolids get extra segments but remain in one piece.
    "Split" - wires and shells will be split at intersections, too.
    "CompSolid" - slice a solid and glue it back together to make a compsolid"""

    shapes = [base_shape] + [
        Part.makeCompound([tool_shape]) for tool_shape in tool_shapes
    ]  # hack: putting tools into compounds will prevent contamination of result with pieces of tools
    if len(shapes) < 2:
        raise ValueError("No slicing objects supplied!")
    pieces, map = shapes[0].generalFuse(shapes[1:], tolerance)
    gr = GeneralFuseResult(shapes, (pieces, map))
    result = None
    if mode == "Standard":
        result = gr.piecesFromSource(shapes[0])
    elif mode == "CompSolid":
        solids = Part.makeCompound(gr.piecesFromSource(shapes[0])).Solids
        if len(solids) < 1:
            raise ValueError("No solids in the result. Can't make compsolid.")
        elif len(solids) == 1:
            FreeCAD.Console.PrintWarning(
                "Part_Slice: only one solid in the result, generating trivial compsolid."
            )
        result = ShapeMerge.mergeSolids(solids, bool_compsolid=True).childShapes()
    elif mode == "Split":
        gr.splitAggregates(gr.piecesFromSource(shapes[0]))
        result = gr.piecesFromSource(shapes[0])
    if result != None:
        return result[0] if len(result) == 1 else Part.makeCompound(result)
    else:
        return Part.Shape()


def xor(list_of_shapes, tolerance=0.0):
    """xor(list_of_shapes, tolerance = 0.0): boolean XOR operation."""
    list_of_shapes = Utils.upgradeToAggregateIfNeeded(list_of_shapes)
    pieces, map = list_of_shapes[0].generalFuse(list_of_shapes[1:], tolerance)
    gr = GeneralFuseResult(list_of_shapes, (pieces, map))
    gr.explodeCompounds()
    gr.splitAggregates()
    pieces_to_keep = []
    for piece in gr.pieces:
        if len(gr.sourcesOfPiece(piece)) % 2 == 1:
            pieces_to_keep.append(piece)
    return Part.makeCompound(pieces_to_keep)
