# -*- coding: utf-8 -*-
# gui/components/model_view/contour/contour_visualization_component.py

# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_grid_contour_viz.py
# Визуализация контура

from __future__ import annotations

import math
from typing import TYPE_CHECKING

from error_logger import log_exception

if TYPE_CHECKING:  # pragma: no cover
    from gui.components.model_view_widget import ModelViewWidget

try:
    import pyvista as pv
except ImportError:  # pragma: no cover
    pv = None

class ContourVisualizationComponent:
    def set_contour_auxiliary_visibility(self: "ModelViewWidget", visible: bool) -> None:
        """Показать/скрыть вспомогательные акторы контура (candidate + dim helpers)."""
        show = bool(visible)
        self._contour_aux_hidden = not show
        if show:
            # Восстановить размерные линии при следующем валидном узле/hover.
            if (
                getattr(self, "_dim_enabled", False)
                and getattr(self, "_dim_current_node", None) is not None
                and hasattr(self, "_redraw_dim_lines")
            ):
                try:
                    self._redraw_dim_lines()
                except Exception as e:
                    log_exception(__name__, "set_contour_auxiliary_visibility", e)
            return
        # Скрыть candidate и дельта-подписи.
        self._hide_contour_candidate_visuals()
        # Скрыть размерные линии и подсветку узла, но не выключать сам режим.
        if hasattr(self, "_clear_dim_actors"):
            try:
                self._clear_dim_actors()
            except Exception as e:
                log_exception(__name__, "set_contour_auxiliary_visibility", e)
        if hasattr(self, "_clear_dim_highlight"):
            try:
                self._clear_dim_highlight()
            except Exception as e:
                log_exception(__name__, "set_contour_auxiliary_visibility", e)

    def _clear_contour_actors(self: "ModelViewWidget") -> None:
        if not self._plotter:
            return
        for actor_name in (
            "selection_contour_points",
            "selection_contour_point_labels",
            "selection_contour_lines",
            "selection_contour_candidate",
            "selection_contour_delta_line",
            "selection_contour_delta_label",
            "selection_contour_segment_labels",
        ):
            try:
                self._plotter.remove_actor(actor_name)
            except Exception as e:
                log_exception(__name__, "_clear_contour_actors", e)
        self._contour_candidate_actor = None
        self._contour_candidate_last_node = None

    def _update_contour_visualization(self: "ModelViewWidget") -> None:
        if not self._plotter:
            return
        self._clear_contour_actors()
        if not self._contour_points:
            try:
                self._plotter.update()
            except Exception as e:
                log_exception(__name__, "_update_contour_visualization", e)
            return
        floor_z = self._get_grid_plane_z(getattr(self, "_grid_origin", None))
        # Overlay: рисовать точки контура на уровне верха родительской зоны.
        if (
            self._contour_zone_overlay_enabled
            and self._contour_zone_mode == "overlay"
            and self._grid_plane_z_override is not None
        ):
            floor_z = float(self._grid_plane_z_override)
        z = floor_z + 2.0
        try:
            pts = [[px, py, z] for px, py in self._contour_points]
            poly_pts = pv.PolyData(pts)
            self._plotter.add_mesh(
                poly_pts,
                color=(0.1, 0.5, 1.0),
                point_size=11,
                render_points_as_spheres=True,
                name="selection_contour_points",
            )
            point_labels = [str(i + 1) for i in range(len(pts))]
            label_points = [[p[0], p[1], p[2] + 3.0] for p in pts]
            self._plotter.add_point_labels(
                label_points,
                point_labels,
                font_size=16,
                text_color="#EAF4FF",
                shape=None,
                show_points=False,
                always_visible=True,
                pickable=False,
                reset_camera=False,
                name="selection_contour_point_labels",
            )
            if len(pts) >= 2:
                line_points: list[list[float]] = []
                line_cells: list[int] = []
                seg_labels: list[str] = []
                seg_mids: list[list[float]] = []
                for i in range(len(pts) - 1):
                    i1 = len(line_points)
                    line_points.append(pts[i])
                    i2 = len(line_points)
                    line_points.append(pts[i + 1])
                    line_cells.extend([2, i1, i2])
                    dx = float(pts[i + 1][0]) - float(pts[i][0])
                    dy = float(pts[i + 1][1]) - float(pts[i][1])
                    dist = math.hypot(dx, dy)
                    if dist > 1e-3:
                        seg_mids.append(
                            [
                                0.5 * (float(pts[i][0]) + float(pts[i + 1][0])),
                                0.5 * (float(pts[i][1]) + float(pts[i + 1][1])),
                                float(z) + 2.8,
                            ]
                        )
                        seg_labels.append(f"{dist:.0f} мм")
                if len(pts) >= 4:
                    i1 = len(line_points)
                    line_points.append(pts[-1])
                    i2 = len(line_points)
                    line_points.append(pts[0])
                    line_cells.extend([2, i1, i2])
                contour_lines = pv.PolyData(line_points)
                contour_lines.lines = line_cells
                self._plotter.add_mesh(
                    contour_lines,
                    color=(0.1, 0.5, 1.0),
                    line_width=2,
                    name="selection_contour_lines",
                )
                if seg_mids and seg_labels:
                    self._plotter.add_point_labels(
                        seg_mids,
                        seg_labels,
                        font_size=20,
                        text_color="#00A3FF",
                        shape=None,
                        show_points=False,
                        always_visible=True,
                        pickable=False,
                        reset_camera=False,
                        name="selection_contour_segment_labels",
                    )
            self._plotter.update()
        except Exception as e:
            log_exception(__name__, "_update_contour_visualization", e)

    def update_contour_candidate_node(self: "ModelViewWidget", x: float, y: float) -> None:
        """Подсветить потенциальный узел новой точки контура."""
        if not self._plotter:
            return
        if bool(getattr(self, "_contour_aux_hidden", False)):
            return
        if not self.is_scenario_active("contour_edit"):
            self._hide_contour_candidate_visuals()
            return
        node = self._nearest_grid_node(float(x), float(y))
        if node is None:
            self._hide_contour_candidate_visuals()
            return
        plane_z = self._get_contour_plane_z()
        node_key = (float(node[0]), float(node[1]), float(plane_z))
        if getattr(self, "_contour_candidate_last_node", None) == node_key:
            return
        self._contour_candidate_last_node = node_key
        try:
            pt = pv.PolyData([[float(node[0]), float(node[1]), float(plane_z) + 2.2]])
            candidate_actor = getattr(self, "_contour_candidate_actor", None)
            if candidate_actor is None:
                candidate_actor = self._plotter.add_mesh(
                    pt,
                    color=(1.0, 0.95, 0.2),
                    point_size=16,
                    render_points_as_spheres=True,
                    name="selection_contour_candidate",
                )
                self._contour_candidate_actor = candidate_actor
            else:
                try:
                    mapper = candidate_actor.GetMapper()
                    mapper.SetInputData(pt)
                    mapper.Update()
                    candidate_actor.SetVisibility(1)
                except Exception as e:
                    log_exception(__name__, "update_contour_candidate_node", e)
                    self._plotter.remove_actor("selection_contour_candidate")
                    self._contour_candidate_actor = self._plotter.add_mesh(
                        pt,
                        color=(1.0, 0.95, 0.2),
                        point_size=16,
                        render_points_as_spheres=True,
                        name="selection_contour_candidate",
                    )
            self._draw_candidate_delta_label(float(node[0]), float(node[1]), float(plane_z) + 2.2)
            self._safe_render(min_interval_s=1.0 / 75.0)
        except Exception as e:
            log_exception(__name__, "update_contour_candidate_node", e)
