# src/palettize/exporters/gdal.py

"""GDAL Color-Relief Exporter for Palettize."""

from palettize.core import (
    Colormap,
    ScalingFunction,
)  # Use type hints from actual modules
from typing import Optional, Tuple, Union, Dict, Any
from ._base import BaseExporter


class GdalExporter(BaseExporter):
    """
    Exporter for GDAL Color Relief text files.
    """

    @property
    def identifier(self) -> str:
        return "gdal"

    @property
    def name(self) -> str:
        return "GDAL Color Relief Text"

    @property
    def default_file_extension(self) -> str:
        return "txt"

    def export(
        self,
        colormap: Colormap,
        scaler: ScalingFunction,
        domain_min: float,
        domain_max: float,
        options: Optional[Dict[str, Any]] = None,
    ) -> str:
        """
        Exports the colormap to GDAL color-relief text format.
        Samples the colormap at `num_colors` points across the given data domain.

        Accepted options:
            num_colors (int): The number of color steps to output. Default 256.
            nodata_value (Union[float, str]): Nodata value. Use "nv" for GDAL keyword.
            nodata_color (Tuple[int,int,int,int]): RGBA tuple for nodata color. Default (0,0,0,0).
            scaler_type (str): The type of scaler used. Default "unknown".
        """
        options = options or {}
        num_colors = options.get("num_colors", 256)
        nodata_value = options.get("nodata_value")
        nodata_color = options.get("nodata_color", (0, 0, 0, 0))
        scaler_type = options.get("scaler_type", "unknown")

        if not isinstance(num_colors, int):
            raise ValueError("Option 'num_colors' must be an integer.")
        if nodata_value is not None and not (
            isinstance(nodata_value, (float, int, str))
        ):
            raise ValueError("Option 'nodata_value' must be a float, int, or string.")
        if nodata_color is not None and not (
            isinstance(nodata_color, tuple)
            and len(nodata_color) == 4
            and all(isinstance(c, int) for c in nodata_color)
        ):
            raise ValueError("Option 'nodata_color' must be an RGBA tuple of integers.")

        return _export_gdal_color_relief_impl(
            colormap=colormap,
            scaler=scaler,
            domain_min=domain_min,
            domain_max=domain_max,
            num_colors=num_colors,
            nodata_value=nodata_value,
            nodata_color=nodata_color,
            scaler_type=scaler_type,
        )


def _export_gdal_color_relief_impl(  # Renamed original function
    colormap: Colormap,
    scaler: ScalingFunction,
    domain_min: float,
    domain_max: float,
    num_colors: int = 256,
    nodata_value: Optional[Union[float, str]] = None,  # str for "nv"
    nodata_color: Optional[Tuple[int, int, int, int]] = (
        0,
        0,
        0,
        0,
    ),  # Default nodata transparent black
    scaler_type: str = "unknown",
) -> str:
    """
    Internal implementation for exporting the colormap to GDAL color-relief text format.
    """
    if domain_min >= domain_max:
        raise ValueError("domain_min must be less than domain_max.")
    if num_colors < 2:
        raise ValueError("num_colors must be at least 2 for GDAL exporter.")

    lines = []
    header = "# GDAL Color Relief File generated by Palettize\n"
    lines.append(header)
    if colormap.name:
        lines.append(f"# Colormap name: {colormap.name}\n")
    lines.append(f"# Domain: [{domain_min}, {domain_max}], Scaler: {scaler_type}\n")
    lines.append(
        f"# Interpolation Space for Colormap: {colormap.interpolation_space}\n"
    )
    lines.append("# Uncomment next line for default nodata transparency\n")
    lines.append("# nv 0 0 0 0\n")

    # Handle nodata line if specified
    if nodata_value is not None:
        if (
            nodata_color is None
        ):  # This check is technically redundant due to default but good practice
            raise ValueError(
                "nodata_color must be specified if nodata_value is provided."
            )
        r, g, b, a = nodata_color
        if isinstance(nodata_value, str) and nodata_value.lower() == "nv":
            lines.append(f"nv {r} {g} {b} {a}\n")
        else:  # Assume numeric nodata_value
            lines.append(f"{nodata_value} {r} {g} {b} {a}\n")

    # Generate color entries by sampling the domain
    for i in range(num_colors):
        # Calculate data value for the current sample point
        # For num_colors = 2, i=0 -> t=0, i=1 -> t=1
        # For num_colors = N, i=N-1 -> t=1
        t = i / (num_colors - 1)
        data_val = domain_min + t * (domain_max - domain_min)

        # Apply the scaler to get the normalized position for the colormap
        # The Colormap.apply_scaler method already handles clamping of normalized_pos
        # and calls get_color with output_format="rgba_tuple".
        try:
            # Using colormap.apply_scaler which internally calls scaler and then get_color
            # Need to ensure apply_scaler exists and works as expected.
            # The apply_scaler method returns a tuple (R,G,B,A) if output_format="rgba_tuple"
            color_components = colormap.apply_scaler(
                data_val, scaler, output_format="rgba_tuple"
            )
            if not (isinstance(color_components, tuple) and len(color_components) == 4):
                # Handle cases where apply_scaler might not return the expected tuple
                # (e.g., if output_format was different or an error occurred internally)
                print(
                    f"Warning: Color for data_val {data_val} not in expected RGBA tuple format. Got: {color_components}. Skipping."
                )
                continue
            r, g, b, a = color_components  # type: ignore
        except Exception as e:
            # Provide more context if color generation fails for a specific value
            # This might happen if scaler produces NaN or invalid values for get_color
            print(
                f"Warning: Could not generate color for data_val {data_val} (normalized_pos from scaler). Error: {e}"
            )
            # Skip this point or use a default color? For now, skipping.
            continue

        lines.append(f"{data_val} {r} {g} {b} {a}\n")

    return "".join(lines)
