"""Pure-Python proximity maths for the 'Vessels nearby' tab.

Own position is treated as static (a fixed receiver / dialled-in point). Each
vessel has a position plus, when available, SOG (knots) and COG (degrees true).

  * bearing_deg  — initial great-circle bearing own -> vessel (0..360, 0 = N)
  * distance_nm  — great-circle range in nautical miles
  * cpa_tcpa     — closest point of approach against a *stationary* observer

CPA/TCPA: with the observer static, the relative velocity is just the vessel's
velocity, so the vessel is "moving away" whenever its closest approach lies at
or before now (tcpa <= 0). In that case the caller prints "passed". A vessel
with no usable velocity (stationary, or SOG/COG unavailable) has CPA = current
range and an undefined TCPA (returned as None).

No QGIS or third-party imports — unit-testable in isolation.
"""
import math

_R_NM = 3440.065          # mean Earth radius in nautical miles
_NM_PER_DEG = 60.0        # 1 degree of latitude ~= 60 nm


def distance_nm(lat1, lon1, lat2, lon2):
    """Great-circle range between two lat/lon points, in nautical miles."""
    p1, p2 = math.radians(lat1), math.radians(lat2)
    dp = math.radians(lat2 - lat1)
    dl = math.radians(lon2 - lon1)
    a = math.sin(dp / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
    return 2 * _R_NM * math.asin(min(1.0, math.sqrt(a)))


def bearing_deg(lat1, lon1, lat2, lon2):
    """Initial great-circle bearing point1 -> point2, in degrees (0..360, 0=N)."""
    p1, p2 = math.radians(lat1), math.radians(lat2)
    dl = math.radians(lon2 - lon1)
    x = math.sin(dl) * math.cos(p2)
    y = math.cos(p1) * math.sin(p2) - math.sin(p1) * math.cos(p2) * math.cos(dl)
    return (math.degrees(math.atan2(x, y)) + 360.0) % 360.0


def _enu_nm(own_lat, own_lon, lat, lon):
    """Local east/north offset (nm) of a point relative to own position."""
    north = (lat - own_lat) * _NM_PER_DEG
    east = (lon - own_lon) * _NM_PER_DEG * math.cos(math.radians(own_lat))
    return east, north


def cpa_tcpa(own_lat, own_lon, v_lat, v_lon, sog, cog):
    """Return (cpa_nm, tcpa_min, moving_away).

    moving_away is True when the closest approach is now or in the past, i.e.
    the vessel is receding -> caller prints 'passed'. When the vessel has no
    usable velocity, returns (current_range, None, False).
    """
    east, north = _enu_nm(own_lat, own_lon, v_lat, v_lon)
    cur = math.hypot(east, north)
    if not sog or sog <= 0 or cog is None:
        return cur, None, False
    cr = math.radians(cog)
    ve = sog * math.sin(cr)        # knots = nm/hour, east component
    vn = sog * math.cos(cr)        # north component
    vv = ve * ve + vn * vn
    if vv <= 0:
        return cur, None, False
    tstar = -(east * ve + north * vn) / vv          # hours to closest approach
    if tstar <= 0:
        return cur, tstar * 60.0, True              # receding -> 'passed'
    cx, cy = east + ve * tstar, north + vn * tstar
    return math.hypot(cx, cy), tstar * 60.0, False
