"""Data models, constants, and shared helpers for DJ HUD."""

from dataclasses import dataclass
from typing import Optional

# ─── Constants ───────────────────────────────────────────────────────────────

DEFAULT_CONFIG_FILE = "dj_hud_layout_v8.json"
DEFAULT_FPS = 15
DEFAULT_CANVAS_W = 1080
DEFAULT_CANVAS_H = 80
DEFAULT_REGION_H = 100
PREVIEW_BG = "#111111"
PREVIEW_OUTSIDE_BG = "#1b1b1b"
PREVIEW_PADDING = 40


# ═══════════════════════════════════════════════════════════════════════════════
# CAPTURE REGION
# ═══════════════════════════════════════════════════════════════════════════════

@dataclass
class CaptureRegion:
    """A region captured from the source and placed on the output canvas."""
    name: str
    src_x: int
    src_y: int
    src_w: int
    src_h: int
    out_x: int
    out_y: int
    target_h: int
    enabled: bool = True
    locked: bool = False
    edit_as_crop: bool = False
    raw_out_x: Optional[int] = None
    raw_out_y: Optional[int] = None

    def __post_init__(self):
        if self.raw_out_x is None:
            self.raw_out_x = self.out_x
        if self.raw_out_y is None:
            self.raw_out_y = self.out_y

    def estimated_width(self) -> int:
        if self.src_h <= 0:
            return 1
        return max(1, int(self.src_w * (self.target_h / self.src_h)))


# ═══════════════════════════════════════════════════════════════════════════════
# SNAP HELPER
# ═══════════════════════════════════════════════════════════════════════════════

class SnapHelper:
    """Shared grid snapping logic used by RegionSelector and SourceRegionEditor."""

    def __init__(self, snap_to_grid: bool = False, grid_size: int = 10):
        self.snap_to_grid = snap_to_grid
        self.grid_size = max(1, grid_size)

    def snap_value(self, value: int) -> int:
        if not self.snap_to_grid:
            return value
        return int(round(value / self.grid_size) * self.grid_size)

    def snap_rect_endpoints(self, rect: list) -> list:
        if not self.snap_to_grid:
            return rect
        x1, y1, x2, y2 = rect
        return [self.snap_value(x1), self.snap_value(y1), self.snap_value(x2), self.snap_value(y2)]

    @staticmethod
    def snap_move_rect(rect: list) -> list:
        """Source-region positions remain pixel-precise; snap not applied on move."""
        return rect

    @staticmethod
    def normalize_rect(rect: list) -> list:
        x1, y1, x2, y2 = rect
        if x2 < x1:
            x1, x2 = x2, x1
        if y2 < y1:
            y1, y2 = y2, y1
        return [x1, y1, x2, y2]

    @staticmethod
    def clamp_rect(rect: list, max_w: int, max_h: int) -> list:
        x1, y1, x2, y2 = SnapHelper.normalize_rect(rect)
        x1 = max(0, min(max_w - 1, x1))
        x2 = max(0, min(max_w - 1, x2))
        y1 = max(0, min(max_h - 1, y1))
        y2 = max(0, min(max_h - 1, y2))
        return [x1, y1, x2, y2]
