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

# -*- coding: utf-8 -*-
# gui/components/model_view/_mv_grid_preview_core.py
# Основное превью/выделение: куб предпросмотра, высота/размеры, якорь, завершение/отмена.
from __future__ import annotations
from typing import Optional, Tuple, TYPE_CHECKING
from PySide6.QtGui import QColor

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 ContourPreviewComponent:
    def _get_max_height_for_start_z(
        self: "ModelViewWidget",
        start_z: Optional[float] = None,
    ) -> float:
        if start_z is None:
            start_z = float(getattr(self, "_last_volume_start_height", 0.0))
        else:
            start_z = float(start_z)
        base_z = float(self._get_grid_plane_z(getattr(self, "_grid_origin", None)))
        max_top_z = base_z + float(self._MAX_VOLUME_HEIGHT)
        return max(0.0, max_top_z - start_z)

    def _update_selection_visualization(self: "ModelViewWidget"):
        if not self._plotter or not self._grid_cells:
            return

        camera_position = None
        try:
            camera_position = self._plotter.camera_position
        except Exception as e:
            log_exception(__name__, "_update_selection_visualization", e)

        disable_render = getattr(self._plotter, "disable_render", None)
        enable_render = getattr(self._plotter, "enable_render", None)
        if callable(disable_render):
            try:
                disable_render()
            except Exception as e:
                log_exception(__name__, "_update_selection_visualization", e)
                disable_render = None

        try:
            if self._preview_zone is not None:
                self._plotter.remove_actor(self._preview_zone)
                self._preview_zone = None
        except Exception as e:
            log_exception(__name__, "_update_selection_visualization", e)

        if not self._selected_cells:
            self._selection_anchor = None
            self.grid_volume_changed.emit(0.0, 0.0, 0.0)
            if callable(enable_render):
                try:
                    enable_render()
                except Exception as e:
                    log_exception(__name__, "_update_selection_visualization", e)
            self._plotter.update()
            return

        cells = [self._grid_cells[cid] for cid in self._selected_cells]
        mn_x = min(c[0] for c in cells)
        mx_x = max(c[1] for c in cells)
        mn_y = min(c[2] for c in cells)
        mx_y = max(c[3] for c in cells)
        floor_z = self._get_grid_plane_z(self._grid_origin)

        # Режим наложения: поднять базу объёма на верхнюю границу родительской зоны.
        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)

        self._last_volume_start_height = floor_z

        center_x = (mn_x + mx_x) / 2
        center_y = (mn_y + mx_y) / 2
        width = mx_x - mn_x
        depth = mx_y - mn_y
        step = max(1.0, float(self._current_zone_size))
        z_step = max(1.0, float(getattr(self, "_current_zone_z_size", self._current_zone_size)))

        height = max(z_step, z_step + float(self._selected_height))
        max_height = self._get_max_height_for_start_z(self._last_volume_start_height)
        if height > max_height:
            height = max_height
            self._selected_height = max(0.0, height - z_step)
        center_z = floor_z + height / 2

        try:
            contour_locked = bool(
                getattr(self, "_volume_locked_from_contour", False)
                or self.is_scenario_active("contour_edit")
                or getattr(self, "_contour_points", None)
            )
            levels = max(1, int(round(height / z_step)))
            total_cubes = len(self._selected_cells) * levels
            use_fast_preview = (not contour_locked) and total_cubes > self._FAST_PREVIEW_CUBE_LIMIT

            cube = None
            if not use_fast_preview:
                if contour_locked:
                    polygon = self._extract_orthogonal_polygon() if hasattr(self, "_extract_orthogonal_polygon") else None
                    if polygon and len(polygon) >= 3:
                        try:
                            pts = [[float(px), float(py), float(floor_z)] for px, py in polygon]
                            faces = [len(pts)] + list(range(len(pts)))
                            base = pv.PolyData(pts, faces)
                            try:
                                base = base.triangulate()
                            except Exception as e:
                                log_exception(__name__, "_update_selection_visualization", e)
                            cube = base.extrude([0.0, 0.0, float(height)], capping=True)
                        except Exception as e:
                            log_exception(__name__, "_update_selection_visualization", e)
                            cube = None
                if cube is None and contour_locked:
                    cube = pv.Cube(
                        center=(center_x, center_y, center_z),
                        x_length=width, y_length=depth, z_length=height,
                    )
                elif cube is None:
                    unit_cubes = []
                    for cid in self._selected_cells:
                        c_mn_x, c_mx_x, c_mn_y, c_mx_y = self._grid_cells[cid]
                        c_cx = (c_mn_x + c_mx_x) * 0.5
                        c_cy = (c_mn_y + c_mx_y) * 0.5
                        c_w = c_mx_x - c_mn_x
                        c_d = c_mx_y - c_mn_y
                        if contour_locked:
                            unit_cubes.append(pv.Cube(
                                center=(c_cx, c_cy, center_z),
                                x_length=c_w, y_length=c_d, z_length=height,
                            ))
                        else:
                            for level in range(levels):
                                c_cz = floor_z + (level + 0.5) * z_step
                                unit_cubes.append(pv.Cube(
                                    center=(c_cx, c_cy, c_cz),
                                    x_length=c_w, y_length=c_d, z_length=z_step,
                                ))
                    if unit_cubes:
                        try:
                            merged = unit_cubes[0]
                            for part in unit_cubes[1:]:
                                merged = merged.merge(part, merge_points=True)
                            cube = merged
                        except Exception as e:
                            log_exception(__name__, "_update_selection_visualization", e)
                            cube = None

            if cube is None or not hasattr(cube, "n_points") or int(cube.n_points) == 0:
                cube = pv.Cube(
                    center=(center_x, center_y, center_z),
                    x_length=width, y_length=depth, z_length=height,
                )

            qcolor = QColor(self._current_zone_color)
            rgb_color = (qcolor.redF(), qcolor.greenF(), qcolor.blueF())
            opacity = qcolor.alphaF()

            self._preview_zone = self._plotter.add_mesh(
                cube,
                color=rgb_color,
                opacity=opacity,
                show_edges=False,
                line_width=2,
                name="selection_preview",
            )
        except Exception as e:
            log_exception(__name__, "_update_selection_visualization", e)

        self.grid_volume_changed.emit(float(width), float(depth), float(height))

        try:
            if camera_position is not None:
                self._plotter.camera_position = camera_position
        except Exception as e:
            log_exception(__name__, "_update_selection_visualization", e)
        if callable(enable_render):
            try:
                enable_render()
            except Exception as e:
                log_exception(__name__, "_update_selection_visualization", e)
        try:
            self._plotter.update()
        except Exception as e:
            log_exception(__name__, "_update_selection_visualization", e)
