# -*- coding: utf-8 -*-
# gui/components/model_view/contour/contour_visualization_component_part2.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 ContourVisualizationComponentPart2:
    def _hide_contour_candidate_visuals(self: "ModelViewWidget") -> None:
        """Скрыть candidate/дельта визуалы без частого пересоздания."""
        actor = getattr(self, "_contour_candidate_actor", None)
        if actor is not None:
            try:
                actor.SetVisibility(0)
            except Exception as e:
                log_exception(__name__, "_hide_contour_candidate_visuals", e)
        for actor_name in (
            "selection_contour_delta_line",
            "selection_contour_delta_label",
        ):
            try:
                self._plotter.remove_actor(actor_name)
            except Exception as e:
                log_exception(__name__, "_hide_contour_candidate_visuals", e)
        self._contour_candidate_last_node = None

    def _draw_candidate_delta_label(self: "ModelViewWidget", nx: float, ny: float, z: float) -> None:
        """Отрисовать расстояние между последней точкой контура и текущим узлом-кандидатом."""
        if not self._plotter or pv is None:
            return
        if len(getattr(self, "_contour_points", [])) < 1:
            return
        last = self._contour_points[-1]
        lx, ly = float(last[0]), float(last[1])
        dx, dy = nx - lx, ny - ly
        seg_len = math.hypot(dx, dy)
        if seg_len <= 1e-3:
            return
        ux, uy = dx / seg_len, dy / seg_len
        step = max(1.0, float(getattr(self, "_current_zone_size", 500.0)))
        label_text = f"{seg_len:.0f} мм"
        if hasattr(pv, "Text3D"):
            try:
                text_mesh = pv.Text3D(label_text, depth=0.2)
                b = text_mesh.bounds
                text_w = max(1e-6, float(b[1] - b[0]))
                text_h = max(1e-6, float(b[3] - b[2]))
                base_w = max(step * 0.25, seg_len * 0.55)
                base_h = max(step * 0.12, 40.0)
                max_w = min(seg_len * 0.88, base_w * self._DELTA_LABEL_SCALE_FACTOR)
                max_h = base_h * self._DELTA_LABEL_SCALE_FACTOR
                scale = min(max_w / text_w, max_h / text_h)
                text_mesh.scale([scale, scale, scale], inplace=True)
                sb = text_mesh.bounds
                cx, cy, cz = 0.5*(sb[0]+sb[1]), 0.5*(sb[2]+sb[3]), 0.5*(sb[4]+sb[5])
                text_mesh.translate([-cx, -cy, -cz], inplace=True)
                angle = math.degrees(math.atan2(uy, ux))
                if angle > 90.0:
                    angle -= 180.0
                elif angle < -90.0:
                    angle += 180.0
                text_mesh.rotate_z(angle, inplace=True)
                mx = 0.5 * (lx + nx)
                my = 0.5 * (ly + ny)
                text_mesh.translate([mx, my, z], inplace=True)
                tb = text_mesh.bounds
                label_span = max(
                    abs(tb[1] - tb[0]),
                    abs(tb[3] - tb[2]),
                )
                gap = min(seg_len * 0.9, label_span + max(step * 0.20, 90.0))
                if seg_len > gap + 1e-3:
                    half_run = 0.5 * (seg_len - gap)
                    p0 = (lx, ly, z)
                    p1 = (lx + ux * half_run, ly + uy * half_run, z)
                    p2 = (nx - ux * half_run, ny - uy * half_run, z)
                    p3 = (nx, ny, z)
                    line_pts = [list(p0), list(p1), list(p2), list(p3)]
                    line_cells = [2, 0, 1, 2, 2, 3]
                    helper = pv.PolyData(line_pts)
                    helper.lines = line_cells
                    self._plotter.add_mesh(
                        helper,
                        color=(1.0, 0.95, 0.2),
                        line_width=2,
                        pickable=False,
                        name="selection_contour_delta_line",
                    )
                self._plotter.add_mesh(
                    text_mesh,
                    color=(1.0, 0.95, 0.2),
                    pickable=False,
                    lighting=False,
                    name="selection_contour_delta_label",
                )
                return
            except Exception as e:
                log_exception(__name__, "_draw_candidate_delta_label", e)
        # Запасной вариант без поддержки ориентированного 3D-текста.
        mx = 0.5 * (lx + nx)
        my = 0.5 * (ly + ny)
        self._plotter.add_point_labels(
            [[mx, my, z]],
            [label_text],
            font_size=max(12, int(round(18 * self._DELTA_LABEL_SCALE_FACTOR))),
            text_color="#FFF176",
            shape=None,
            show_points=False,
            always_visible=True,
            pickable=False,
            reset_camera=False,
            name="selection_contour_delta_label",
        )

    def _restore_overlay_parent_visual(self: "ModelViewWidget") -> None:
        zone_id = str(getattr(self, "_overlay_parent_zone_id", "") or "")
        if not zone_id:
            return
        actor = self._zones.get(zone_id)
        if actor is not None:
            try:
                orig_vis = getattr(self, "_overlay_parent_original_visibility", None)
                orig_opacity = getattr(self, "_overlay_parent_original_opacity", None)
                if orig_vis is not None:
                    actor.SetVisibility(1 if bool(orig_vis) else 0)
                if orig_opacity is not None:
                    actor.GetProperty().SetOpacity(float(orig_opacity))
            except Exception as e:
                log_exception(__name__, "_restore_overlay_parent_visual", e)
        self._overlay_parent_zone_id = None
        self._overlay_parent_original_visibility = None
        self._overlay_parent_original_opacity = None
