"""
BuildPart

name: build_part.py
by:   Gumyr
date: July 12th 2022

desc:
    This python module is a library used to build 3D parts.

TODO:
- add TwistExtrude, ProjectText

license:

    Copyright 2022 Gumyr

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

"""

from __future__ import annotations

from build123d.build_common import Builder, logger
from build123d.build_enums import Mode
from build123d.geometry import Location, Plane
from build123d.topology import Edge, Face, Joint, Part, Solid, Wire


class BuildPart(Builder):
    """BuildPart

    The BuildPart class is another subclass of Builder for building parts
    (objects with the property of volume) from sketches or 3D objects.
    It has an _obj property that returns the current part being built, and
    several pending lists for storing faces, edges, and planes that will be
    integrated into the final part later. The class overrides the _add_to_pending
    method of Builder.

    Args:
        workplanes (Plane, optional): initial plane to work on. Defaults to Plane.XY.
        mode (Mode, optional): combination mode. Defaults to Mode.ADD.
    """

    _tag = "BuildPart"  # Alternate for __class__.__name__
    _obj_name = "part"  # Name of primary instance variable
    _shape = Solid  # Type of shapes being constructed
    _sub_class = Part  # Class of part/_obj

    def __init__(
        self,
        *workplanes: Face | Plane | Location,
        mode: Mode = Mode.ADD,
    ):
        self.joints: dict[str, Joint] = {}
        self._part: Part | None = None  # Use a private attribute
        self.pending_faces: list[Face] = []
        self.pending_face_planes: list[Plane] = []
        self.pending_planes: list[Plane] = []
        self.pending_edges: list[Edge] = []
        super().__init__(*workplanes, mode=mode)

    @property
    def part(self) -> Part | None:
        """Get the current part"""
        return self._part

    @part.setter
    def part(self, value: Part) -> None:
        """Set the current part"""
        self._part = value

    _obj = part  # Alias _obj to part

    @property
    def pending_edges_as_wire(self) -> Wire:
        """Return a wire representation of the pending edges"""
        return Wire.combine(self.pending_edges)[0]

    @property
    def location(self) -> Location | None:
        """Builder's location"""
        return self.part.location if self.part is not None else Location()

    def _add_to_pending(self, *objects: Edge | Face, face_plane: Plane | None = None):
        """Add objects to BuildPart pending lists

        Args:
            objects (Union[Edge, Face]): sequence of objects to add
        """
        new_faces = [o for o in objects if isinstance(o, Face)]
        for face in new_faces:
            logger.debug(
                "Adding Face to pending_faces at %s on pending_face_plane %s",
                face.location,
                face_plane,
            )
            self.pending_faces.append(face)
            if face_plane is not None:
                self.pending_face_planes.append(face_plane)

        new_edges = [o for o in objects if isinstance(o, Edge)]
        for edge in new_edges:
            logger.debug(
                "Adding Edge to pending_edges at %s",
                edge.location,
            )
            self.pending_edges.append(edge)

    def _exit_extras(self):
        """Transfer joints on exit"""
        if self.joints:
            self.part.joints = self.joints
            for joint in self.part.joints.values():
                joint.parent = self.part
