"""Adapter for VPAIR (Aerial Visual Place Recognition) dataset sample release.

Real VPAIR sample layout:
  <root>/
    poses_query.txt            header: filepath,x,y,z,undulation,roll,pitch,yaw,landcover
    queries/0000N.png          referenced by filepath column
    reference_views/           (not used by this adapter)
    distractors/               (not used)

Conversions performed by the adapter:
  - ECEF (x,y,z metres) → WGS84 (lat, lon, alt) via gps_denied.testing.coord.
  - Euler (roll, pitch, yaw radians, ZYX aerospace) → quaternion.
  - Timestamps synthesised at 5 Hz (200 ms period) based on row order,
    because the VPAIR sample has no native timestamp column.
"""

from __future__ import annotations

import csv
from pathlib import Path
from typing import Iterator

from gps_denied.testing.coord import ecef_to_wgs84, euler_to_quaternion
from gps_denied.testing.datasets.base import (
    DatasetAdapter,
    DatasetCapabilities,
    DatasetFrame,
    DatasetIMU,
    DatasetNotAvailableError,
    DatasetPose,
    PlatformClass,
)

# Synthesised frame period. VPAIR paper quotes ~1 Hz query rate; we use 5 Hz
# (200 ms) to align with the product's nominal frame-processing budget from
# the e2e design doc. Callers that need tighter pacing can subclass.
_SYNTH_FRAME_PERIOD_NS = 200_000_000


class VPAIRAdapter(DatasetAdapter):
    """Reads the VPAIR sample bundle (queries/ + poses_query.txt)."""

    def __init__(self, root: Path) -> None:
        self._root = Path(root)
        self._queries_dir = self._root / "queries"
        self._poses_txt = self._root / "poses_query.txt"
        if not self._queries_dir.is_dir():
            raise DatasetNotAvailableError(
                f"VPAIR sample missing queries/ at {self._root}. "
                "Download the sample from "
                "https://github.com/AerVisLoc/vpair (Zenodo link) and unpack."
            )
        if not self._poses_txt.is_file():
            raise DatasetNotAvailableError(
                f"VPAIR sample missing poses_query.txt at {self._root}."
            )

    @property
    def name(self) -> str:
        return f"vpair:{self._root.name}"

    @property
    def capabilities(self) -> DatasetCapabilities:
        return DatasetCapabilities(
            has_raw_imu=False,   # VPAIR ships poses, not raw IMU
            has_rtk_gt=False,    # GNSS/INS ~1 m accuracy, not RTK
            has_loop_closures=False,
            platform_class=PlatformClass.FIXED_WING,
        )

    def iter_frames(self) -> Iterator[DatasetFrame]:
        for idx, row in enumerate(self._rows()):
            filepath = row["filepath"].strip()
            yield DatasetFrame(
                frame_idx=idx,
                timestamp_ns=idx * _SYNTH_FRAME_PERIOD_NS,
                image_path=str(self._root / filepath),
            )

    def iter_imu(self) -> Iterator[DatasetIMU]:
        return
        yield  # empty generator

    def iter_ground_truth(self) -> Iterator[DatasetPose]:
        for idx, row in enumerate(self._rows()):
            x = float(row["x"])
            y = float(row["y"])
            z = float(row["z"])
            roll = float(row["roll"])
            pitch = float(row["pitch"])
            yaw = float(row["yaw"])
            lat, lon, alt = ecef_to_wgs84(x, y, z)
            qx, qy, qz, qw = euler_to_quaternion(roll, pitch, yaw)
            yield DatasetPose(
                timestamp_ns=idx * _SYNTH_FRAME_PERIOD_NS,
                lat=lat,
                lon=lon,
                alt=alt,
                qx=qx, qy=qy, qz=qz, qw=qw,
            )

    # ------------------------------------------------------------------
    def _rows(self):
        """Yield dict rows from poses_query.txt."""
        with self._poses_txt.open() as fh:
            reader = csv.DictReader(fh)
            for row in reader:
                yield row
