# Source code for tabletree.src.api.view

    # sphinx bug workaround for python 3.11. see grm:86663
    from __future__ import annotations

    from collections import defaultdict
    from collections.abc import Sequence
    from typing import Literal, Self

    import frtobjs
    import geode as gd

    from ..lib.i18n import I18nResourcesWriter, I18nWord
    from ..lib.view_command import (
        INVALID_PROPERTY_KEY,
        AppendHighlightArrowBuilder,
        AppendHighlightCircleBuilder,
        AppendHighlightElementsBuilder,
        AppendHighlightPointsBuilder,
        AppendHighlightPolygonBuilder,
        AppendHighlightPolylinesBuilder,
        AppendHighlightRefElementsBuilder,
        AppendHighlightTextBuilder,
        ClearHighlightBuilder,
        DisplayOnlyAroundHighlightedBuilder,
        EndSectionBuilder,
        SetViewDirectionBuilder,
        StartSectionBuilder,
        ViewCommandBuilder,
        ZoomToHighlightedBuilder,
    )
    from ..lib.view_operation import ViewOperationBuilder
    from .view_util import (
        Arrow,
        Circle,
        Highlight,
        HighlightColor,
        Point,
        Polygon,
        Polyline,
        Text,
    )

    # maintain rst file for document when changing this
    PointCloud = Sequence[gd.PointOnFace | gd.PointOnEdge] | Sequence[Point]

    # maintain rst file for document when changing this
    GeomElement = (
        frtobjs.Body
        | frtobjs.Face
        | frtobjs.Edge
        | gd.FacetedEdge
        | Polyline
        | gd.FacetedFace
        | Polygon
        | gd.PointOnEdge
        | gd.PointOnFace
        | Point
        | PointCloud
        | Text
        | Arrow
        | Circle
    )

    AppendHighlightBuilder = (
        AppendHighlightElementsBuilder
        | AppendHighlightPolylinesBuilder
        | AppendHighlightPolygonBuilder
        | AppendHighlightArrowBuilder
        | AppendHighlightTextBuilder
        | AppendHighlightCircleBuilder
        | AppendHighlightPointsBuilder
    )

    ViewEventType = Literal["on_single_clicked", "on_double_clicked"]

    class ViewKeySuffix:
        colors_resource = "ColorsRes"
        colors = "Colors"
        zoomed = "Zoom"

        elements_executable = "ElemsExecutable"

        polylines_executable = "PolylinesExecutable"
        polylines_lengths = "PolylinesLns"
        polylines_lengths_resource = "PolylinesLnsRes"

        polygon_executable = "PolygonExecutable"
        polygon_points = "PolygonPts"
        polygon_points_resource = "PolygonPtsRes"
        polygon_vertex_indices = "PolygonIdxs"
        polygon_vertex_indices_resource = "PolygonIdxsRes"

        arrow_executable = "ArrExecutable"
        arrow_start = "ArrStart"
        arrow_end = "ArrEnd"
        arrow_double_headed = "ArrDblHead"
        arrow_head_reversed = "ArrReverse"
        arrow_head_length = "ArrHeadLen"

        text_executable = "TxtExecutable"
        text_content = "TxtContent"
        text_position = "TxtPos"
        text_letter_width = "TxtWidth"
        text_letter_height = "TxtHeight"

        circle_executale = "CircleExecutable"
        circle_center = "CircleCenter"
        circle_radius = "CircleR"
        circle_normal = "CircleNormal"

        point_cloud_executable = "PocExecutable"
        point_cloud_points = "PocPts"
        point_cloud_points_resource = "PocPtsRes"
        point_cloud_texts = "PocTxt"

    class View:
        _op_bdr: ViewOperationBuilder
        __view_cmd_bdr: list[ViewCommandBuilder]
        __highlight_cmd_bdr: list[AppendHighlightBuilder]
        __post_cmd_bdr: dict[Literal["zoom", "neighbor"], bool]
        _zoomed_keys: list[str]
        _neighbor_keys: list[str]

        def __init__(self, view_id: str, name: str | I18nWord):
            """Create a 3D view for the items.

            Args:
                view_id: The unique id of the view.
                name: The name of the view.
            """
            self._op_bdr = ViewOperationBuilder(view_id)
            if isinstance(name, str):
                self._op_bdr.set_name(name)
            else:
                self._op_bdr.set_name_i18n(name)
                I18nResourcesWriter.get_instance().add_word(name)
            self._op_bdr.add_pre_command(EndSectionBuilder()).add_pre_command(
                ClearHighlightBuilder()
            )
            self.__view_cmd_bdr = []
            self.__highlight_cmd_bdr = []
            self.__post_cmd_bdr = defaultdict[Literal["zoom", "neighbor"], bool](
                lambda: False
            )
            self._zoomed_keys = []
            self._neighbor_keys = []
            self._get_data_called = False

        def available_on(self, event_types: Sequence[ViewEventType]) -> Self:
            """Set types of events that can trigger the view.

            Args:
                event_types:
                    The types of events that can trigger the view.
                    If not set, the view is triggered only when
                    it is invoked from the context menu of items.
            """
            if "on_single_clicked" in event_types:
                self._op_bdr.set_assignment("select", True)
            if "on_double_clicked" in event_types:
                self._op_bdr.set_assignment("doubleClick", True)
            return self

        def highlight(  # noqa: C901
            self,
            highlights: list[Highlight],
        ) -> Self:
            """Highlight geometry elements in the view.

            Args:
                highlights:
                    A list of Highlight objects.
            """
            for highlight in highlights:
                hlt_key = highlight.key
                hlt_color = highlight.color

                bdr_perm_elem = AppendHighlightElementsBuilder()
                bdr_polylines = AppendHighlightPolylinesBuilder()
                bdr_polygons = AppendHighlightPolygonBuilder()
                bdr_arr = AppendHighlightArrowBuilder()
                bdr_txt = AppendHighlightTextBuilder()
                bdr_circle = AppendHighlightCircleBuilder()
                bdr_poc = AppendHighlightPointsBuilder()

                builders = list[AppendHighlightBuilder](
                    (
                        bdr_perm_elem,
                        bdr_polylines,
                        bdr_polygons,
                        bdr_arr,
                        bdr_txt,
                        bdr_circle,
                        bdr_poc,
                    )
                )

                bdr_perm_elem.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.elements_executable}", False
                ).set_elem_id_property(hlt_key, []).set_contour_if_available(False)

                bdr_polylines.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.polylines_executable}", False
                ).set_polylines_property(
                    f"{hlt_key}{ViewKeySuffix.polylines_lengths}", []
                ).set_polylines_resource_property(
                    f"{hlt_key}{ViewKeySuffix.polylines_lengths_resource}"
                )

                bdr_polygons.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.polygon_executable}", False
                ).set_point_list_property(
                    f"{hlt_key}{ViewKeySuffix.polygon_points}", []
                ).set_point_list_resource_property(
                    f"{hlt_key}{ViewKeySuffix.polygon_points_resource}"
                ).set_point_index_list_property(
                    f"{hlt_key}{ViewKeySuffix.polygon_vertex_indices}", []
                ).set_point_index_list_resource_property(
                    f"{hlt_key}{ViewKeySuffix.polygon_vertex_indices_resource}"
                )

                bdr_arr.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_executable}", False
                ).set_start_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_start}", [0.0, 0.0, 0.0]
                ).set_end_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_end}", [0.0, 0.0, 0.0]
                ).set_double_headed_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_double_headed}", False
                ).set_reverse_head_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_head_reversed}", False
                ).set_head_length_property(
                    f"{hlt_key}{ViewKeySuffix.arrow_head_length}", -1.0
                )

                bdr_txt.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.text_executable}", False
                ).set_text_property(
                    f"{hlt_key}{ViewKeySuffix.text_content}", ""
                ).set_position_property(
                    f"{hlt_key}{ViewKeySuffix.text_position}", [0.0, 0.0, 0.0]
                ).set_width_property(
                    f"{hlt_key}{ViewKeySuffix.text_letter_width}", 10.0
                ).set_height_property(
                    f"{hlt_key}{ViewKeySuffix.text_letter_height}", 15.0
                )

                bdr_circle.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.circle_executale}", False
                ).set_center_property(
                    f"{hlt_key}{ViewKeySuffix.circle_center}", [0.0, 0.0, 0.0]
                ).set_radius_property(
                    f"{hlt_key}{ViewKeySuffix.circle_radius}", 0
                ).set_normal_property(
                    f"{hlt_key}{ViewKeySuffix.circle_normal}", [1.0, 0.0, 0.0]
                )

                bdr_poc.set_executable_property(
                    f"{hlt_key}{ViewKeySuffix.point_cloud_executable}", False
                ).set_point_list_property(
                    f"{hlt_key}{ViewKeySuffix.point_cloud_points}", []
                ).set_point_list_resource_property(
                    f"{hlt_key}{ViewKeySuffix.point_cloud_points_resource}"
                ).set_text_list_property(
                    f"{hlt_key}{ViewKeySuffix.point_cloud_texts}", []
                )

                for builder in builders:
                    # Set only one color
                    if isinstance(
                        builder,
                        (
                            AppendHighlightElementsBuilder
                            | AppendHighlightArrowBuilder
                            | AppendHighlightTextBuilder
                            | AppendHighlightCircleBuilder
                        ),
                    ):
                        if isinstance(hlt_color, HighlightColor):
                            if isinstance(hlt_color.config_accessor, list):
                                builder.set_color_rgb_property(
                                    INVALID_PROPERTY_KEY, hlt_color.rgb
                                )
                            else:
                                builder.set_color_rgb_accessor(
                                    hlt_color.index,
                                    hlt_color.rgb,
                                )

                            if highlight.priority is not None:
                                builder.set_priority(highlight.priority)

                        elif isinstance(hlt_color, str):
                            builder.set_color_rgb_accessor(
                                HighlightColor._HighlightColorPresetDict[
                                    hlt_color
                                ],
                                HighlightColor.fallback_color,
                            ).set_priority(
                                HighlightColor._HighlightColorPresetToPriority[
                                    hlt_color
                                ]
                            )
                        else:
                            raise TypeError(
                                f"{builder.__class__}: "
                                "highlight_elems contains unknown color "
                                f"of type {type(hlt_color)}."
                            )

                    # Set one / multiple color
                    elif isinstance(
                        builder,
                        (
                            AppendHighlightPolylinesBuilder
                            | AppendHighlightPolygonBuilder
                            | AppendHighlightPointsBuilder
                        ),
                    ):
                        if isinstance(hlt_color, HighlightColor):
                            builder.set_color_rgb_list_resource_property(
                                f"{hlt_key}{ViewKeySuffix.colors_resource}"
                            )
                            if isinstance(hlt_color.config_accessor, list):
                                builder.set_color_rgb_list_property(
                                    f"{hlt_key}{ViewKeySuffix.colors}",
                                    [hlt_color.config_accessor],
                                )
                            else:
                                builder.set_color_rgb_list_accessor(
                                    hlt_color.index,
                                    hlt_color.rgb,
                                )

                            if highlight.priority is not None:
                                builder.set_priority(highlight.priority)

                        elif isinstance(hlt_color, str):
                            if (
                                hlt_color
                                in HighlightColor._HighlightColorPresetDict
                            ):
                                builder.set_color_rgb_list_accessor(
                                    HighlightColor._HighlightColorPresetDict[
                                        hlt_color
                                    ],
                                    HighlightColor.fallback_color,
                                ).set_priority(
                                    HighlightColor._HighlightColorPresetToPriority[
                                        hlt_color
                                    ]
                                )

                    builder.set_zoomable_property(
                        f"{hlt_key}_{self._op_bdr.id}{ViewKeySuffix.zoomed}",
                        False,
                    )
                    self.__highlight_cmd_bdr.append(builder)
            return self

        def zoom_into(self, keys: Sequence[str]) -> Self:
            """Zoom into geometries in the view.

            Args:
                keys:
                    The keys for geometries to be zoomed into.
            """
            self._zoomed_keys.extend(keys)
            self.__post_cmd_bdr["zoom"] = True
            return self

        def create_section_at(
            self,
            key_position: str,
            key_normal: str,
            key_rotation_axis: str | None = None,
        ) -> Self:
            """Create a section in the view.

            Args:
                key_position:
                    The property key for the position of the section.
                key_normal:
                    The property key for the unit vector
                    normal to the section plane.
                key_rotation_axis:
                    The property key for the unit vector used as
                    the manipulator rotation axis.
            """
            builder = (
                StartSectionBuilder()
                .set_position_property(key_position, [0.0, 0.0, 0.0])
                .set_normal_property(key_normal, [1.0, 0.0, 0.0])
            )
            if key_rotation_axis is not None:
                builder.set_rotation_axis(key_rotation_axis, [1.0, 0.0, 0.0])
            self.__view_cmd_bdr.append(builder)
            return self

        def show_only_neighbors_of(self, keys: Sequence[str]) -> Self:
            """Set the geometries to be displayed in the neighbor view.

            Only the elements themselves and neighbors of them
            are shown in the view.

            Args:
                keys:
                    The keys for the geometries to be displayed
                    in the neighbor view.
            """
            self._neighbor_keys.extend(keys)
            self.__post_cmd_bdr["neighbor"] = True
            return self

        def view_from(
            self, key_position: str, key_direction: str, key_upward: str
        ) -> Self:
            """Set the view direction and position.

            Args:
                key_position:
                    The property key for the view position.
                key_direction:
                    The property key for the view direction.
                key_upward:
                    The property key for the unit vector
                    which defines the upward orientation of the view.
            """
            self.__view_cmd_bdr.append(
                SetViewDirectionBuilder()
                .set_view_pos_property(key_position, [0.0, 0.0, 0.0])
                .set_view_dir_property(key_direction, [0.0, 0.0, -1.0])
                .set_view_up_property(key_upward, [0.0, 1.0, 0.0])
            )
            return self

        def _get_data(self) -> ViewOperationBuilder:  # noqa: C901
            """Obtain the data.

            Call this function only once for each instance.
            """
            if self._get_data_called:
                raise RuntimeError(
                    "_get_data is called twice for the view. "
                    "Please use different view instance for each table."
                )
            self._get_data_called = True

            view_op_bdr = self._op_bdr
            for key in self._zoomed_keys:
                self.__view_cmd_bdr.append(
                    AppendHighlightRefElementsBuilder()
                    .set_elem_id_list_property(key, [])
                    .set_zoomable(True)
                )

            zoomed_key_set = set(self._zoomed_keys)
            for key in self._neighbor_keys:
                if key not in zoomed_key_set:
                    self.__view_cmd_bdr.append(
                        AppendHighlightRefElementsBuilder()
                        .set_elem_id_list_property(key, [])
                        .set_zoomable(False)
                    )
            # AppendHighlight*** commands has to be added
            # *after* the AppendHighlightRefElements's.
            self.__view_cmd_bdr.extend(self.__highlight_cmd_bdr)

            late_command_bdr: list[StartSectionBuilder] = []
            for bdr in self.__view_cmd_bdr:
                if isinstance(bdr, StartSectionBuilder):
                    late_command_bdr.append(bdr)
                else:
                    view_op_bdr.add_main_command(bdr)

            for late_bdr in late_command_bdr:
                view_op_bdr.add_main_command(late_bdr)

            for post_cmd, enabled in self.__post_cmd_bdr.items():
                if post_cmd == "zoom" and enabled:
                    view_op_bdr.add_post_command(ZoomToHighlightedBuilder())
                elif post_cmd == "neighbor" and enabled:
                    view_op_bdr.add_post_command(
                        DisplayOnlyAroundHighlightedBuilder()
                    )

            return view_op_bdr