# CelerisLab/common/preprocess.py
"""Host-side geometric helpers for immersed-boundary preprocessing.

Responsibilities:
    Circle-ray intersection along fluid-to-neighbour links and simple
    discrete sensor-area counting used when building IBM masks and probes.
"""

import math
import numpy as np


def find_circle_intersection(x, y, x_neb, y_neb, xc, yc, r0):
    """Return (px, py) of the closest intersection along fluid->neighbour ray,
    or None if no valid root in [0, 1]."""
    dx, dy = x_neb - x, y_neb - y
    a = dx ** 2 + dy ** 2
    b = 2 * (dx * (x - xc) + dy * (y - yc))
    c = (x - xc) ** 2 + (y - yc) ** 2 - r0 ** 2
    det = b ** 2 - 4 * a * c

    if det < 0:
        return None

    t1 = (-b - math.sqrt(det)) / (2 * a)
    t2 = (-b + math.sqrt(det)) / (2 * a)

    # Pick the smallest valid t (closest to fluid node).
    for t in (t1, t2):
        if 0 <= t <= 1:
            return x + t * dx, y + t * dy
    return None


def find_sensor_area(radius):
    area = 0
    for i in range(int(np.floor(-radius)), int(np.ceil(radius))):
        for j in range(int(np.floor(-radius)), int(np.ceil(radius))):
            if i ** 2 + j ** 2 <= radius ** 2:
                area += 1
    return area


def build_triangle_release_points(
    layout: dict,
    *,
    nx: int,
    ny: int,
    diameter_cells: float = 20.0,
    upstream_D: float = 4.0,
    margin: float = 5.0,
    n_seeds: int = 4,
) -> np.ndarray:
    """Upstream release seeds for triangle layout, clamped inside the domain."""
    release_x = min(layout["x_apex"], layout["x_rear"]) - upstream_D * diameter_cells
    release_x = float(np.clip(release_x, margin, nx - 1 - margin))
    y_low = float(np.clip(layout["y_lower"] - layout["radius_lb"], margin, ny - 1 - margin))
    y_high = float(np.clip(layout["y_upper"] + layout["radius_lb"], margin, ny - 1 - margin))
    ys = np.linspace(y_low, y_high, max(2, int(n_seeds)), dtype=np.float64)
    return np.column_stack([np.full(ys.shape[0], release_x, dtype=np.float64), ys])


def cylinders_from_triangle_layout(layout: dict) -> "list[tuple[tuple[float, float], float]]":
    """Build cylinder list from triangle-layout dict used in experiment notebook."""
    radius = float(layout["radius_lb"])
    return [
        ((float(layout["x_apex"]), float(layout["y_center"])), radius),
        ((float(layout["x_rear"]), float(layout["y_lower"])), radius),
        ((float(layout["x_rear"]), float(layout["y_upper"])), radius),
    ]
