"""Shared plotting helpers for the literal MATLAB-to-Python conversions."""

from __future__ import annotations

from pathlib import Path
from typing import Iterable, Sequence

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.axes import Axes
from matplotlib.figure import Figure


COLOR_ORDER = np.array(
    [
        [0.0, 0.0, 1.0],
        [0.0, 0.5, 0.0],
        [1.0, 0.0, 0.0],
        [0.0, 0.75, 0.75],
        [0.75, 0.0, 0.75],
        [0.75, 0.75, 0.0],
        [0.25, 0.25, 0.25],
    ]
)


def set_color_order() -> None:
    """Use the MATLAB color order specified in the original scripts."""
    plt.rcParams["axes.prop_cycle"] = plt.cycler(color=COLOR_ORDER)


def matlab_figure(
    position: Sequence[float],
    *,
    num: int | None = None,
    clear: bool = True,
) -> Figure:
    """Create a figure from a MATLAB [left bottom width height] pixel position.

    The pixel dimensions are converted to inches at 100 dpi and capped so the
    plot fits comfortably inside a typical desktop plot window.
    """
    width = max(4.0, min(float(position[2]) / 100.0, 12.0))
    height = max(3.0, min(float(position[3]) / 100.0, 8.0))
    fig = plt.figure(num=num, figsize=(width, height), dpi=100, clear=clear)
    return fig


def set_axes_font(ax: Axes, fontsize: float, fontweight: str = "bold") -> None:
    """Approximate MATLAB set(gca,'FontSize',...,'FontWeight',...)."""
    ax.tick_params(axis="both", which="major", labelsize=fontsize)
    for label in ax.get_xticklabels() + ax.get_yticklabels():
        label.set_fontweight(fontweight)
    if hasattr(ax, "get_zticklabels"):
        for label in ax.get_zticklabels():
            label.set_fontweight(fontweight)
            label.set_fontsize(fontsize)
    ax.xaxis.label.set_fontsize(fontsize)
    ax.yaxis.label.set_fontsize(fontsize)
    ax.xaxis.label.set_fontweight(fontweight)
    ax.yaxis.label.set_fontweight(fontweight)
    if hasattr(ax, "zaxis"):
        ax.zaxis.label.set_fontsize(fontsize)
        ax.zaxis.label.set_fontweight(fontweight)


def finish_figure(fig: Figure) -> None:
    """Scale the drawing area to the current window without changing geometry."""
    try:
        fig.canvas.manager.set_window_title(Path(__file__).parent.name)
    except Exception:
        pass
    fig.canvas.draw_idle()


def require_data_file(filename: str, script_file: str) -> Path:
    """Locate a data file beside the script and raise a clear error if absent."""
    path = Path(script_file).resolve().parent / filename
    if not path.exists():
        raise FileNotFoundError(
            f"Required data file '{filename}' was not included. "
            f"Place it in {path.parent} and run the script again."
        )
    return path
