# Copyright (C) 2026 Wilhelmus Dingenus de Pooter
# SPDX-License-Identifier: GPL-3.0-or-later
"""
sourceval/coverage_cells.py — native chart-footprint coverage (GDAL-free).
==========================================================================

The Source Validator clips the source register to the chart's coverage so a
register that spans a whole coast is not flagged wholesale as "in source, not
in chart". Coverage normally comes from a *loaded* ENC Coverage layer — but
that forced the user to load a layer first. This module reads the footprint
directly from the scanned cells instead, so a run needs no layers loaded:

  * S-57  → ``M_COVR`` areas with ``CATCOV = 1`` (coverage available; the
            ``CATCOV = 2`` no-data areas are the holes and are skipped);
  * S-101 → ``DataCoverage`` areas.

Returns ``[(exterior, holes), …]`` (lon/lat rings) — the shape
``reconcile.prepare_coverage`` expects.
"""

# Module-level (not lazy) so the package-import check catches a wrong-level
# import in the real QGIS package context. Sibling modules inside sourceval are
# plain-relative (work whether the package is 'sourceval' or 'ENCoreGIS.
# sourceval'); only the plugin-root sibling ``s57`` (one level UP) needs the
# QGIS/headless try/except — matching enc_features_s101's import block.
from .enc_features import extract_areas
from . import enc_features_s101 as _s101x
try:                                   # QGIS: ENCoreGIS.s57
    from ..s57 import S57RecordSet
except ImportError:                    # headless harness: plugin root on path
    from s57 import S57RecordSet


def coverage_polygons_from_cells(cells, catalogue):
    """Native chart footprint from ``cells`` = [(path, product), …] (or bare
    paths). Product is taken from the tag when given, but ALWAYS cross-checked
    against the filename (an S-101 cell basename starts with '101') so a
    mis-tag can't send an S-101 cell to the S-57 reader and vice versa."""
    import os
    s57_paths, s101_paths = [], []
    for entry in cells:
        if isinstance(entry, (tuple, list)):
            path = entry[0]
            prod = entry[1] if len(entry) > 1 else None
        else:
            path, prod = entry, None
        base = os.path.basename(path or "").upper()
        is_s101 = (prod == "S-101") or base.startswith("101")
        (s101_paths if is_s101 else s57_paths).append(path)
    polys = []
    polys += _s57_coverage(s57_paths, catalogue)
    polys += _s101_coverage(s101_paths)
    return polys


def _emit_areas(cf, out):
    for rings in (cf.areas or []):
        if rings and len(rings[0]) >= 4:
            out.append((rings[0], rings[1:]))


def _s57_coverage(paths, catalogue):
    if not paths:
        return []
    out = []
    try:
        rs = S57RecordSet.from_paths(paths)
    except Exception:                                   # noqa: BLE001
        return out
    for cell in rs.cells:
        for cf in extract_areas(cell, "M_COVR", catalogue):
            # CATCOV=1 is the coverage; =2 are the no-data holes. Include when
            # 1 or absent (a bare M_COVR = the cell's charted extent).
            if str(cf.attrs.get("CATCOV")) == "2":
                continue
            _emit_areas(cf, out)
    return out


def _s101_coverage(paths):
    if not paths:
        return []
    out = []
    for path in paths:
        handle = _s101x.open_cell(path)
        if handle is None:
            continue
        for cf in _s101x.extract_areas(handle, "DataCoverage"):
            _emit_areas(cf, out)
    return out
