from __future__ import annotations

import collections
import math

from PIL import Image, ImageDraw, ImageFont

from planemapper.constants import (
    COLOUR_STALE_OUTLINE,
    COLOUR_TRAIL,
    TRAIL_DOT_SIZE_MAX,
    TRAIL_DOT_SIZE_MIN,
)
from planemapper.models import Aircraft
from planemapper.renderer.colours import altitude_to_colour


def _rotate_point(x: float, y: float, angle_deg: float) -> tuple[float, float]:
    r = math.radians(angle_deg)
    return (x * math.cos(r) - y * math.sin(r), x * math.sin(r) + y * math.cos(r))


def _draw_arrow(
    draw: ImageDraw.ImageDraw,
    cx: int,
    cy: int,
    heading: float,
    colour: tuple[int, int, int],
    is_mlat: bool,
) -> None:
    tip = _rotate_point(0, -12, heading)
    left = _rotate_point(-6, 8, heading)
    right = _rotate_point(6, 8, heading)
    pts = [
        (cx + tip[0], cy + tip[1]),
        (cx + left[0], cy + left[1]),
        (cx + right[0], cy + right[1]),
    ]
    if is_mlat:
        draw.polygon(pts, fill=None, outline=colour)
    else:
        draw.polygon(pts, fill=colour)


def _draw_label(
    draw: ImageDraw.ImageDraw,
    cx: int,
    cy: int,
    aircraft: Aircraft,
    colour: tuple[int, int, int],
) -> None:
    font = ImageFont.load_default()
    if aircraft.callsign:
        text = f"{aircraft.callsign}\n{aircraft.altitude_ft}ft"
    else:
        text = f"{aircraft.altitude_ft}ft"
    draw.text((cx + 12, cy - 8), text, fill=colour, font=font)


def _draw_trail(
    draw: ImageDraw.ImageDraw,
    trail: collections.deque[tuple[int, int]],
) -> None:
    n = len(trail)
    if n == 0:
        return
    for i, (tx, ty) in enumerate(trail):
        if n > 1:
            size = int(TRAIL_DOT_SIZE_MAX - i * (TRAIL_DOT_SIZE_MAX - TRAIL_DOT_SIZE_MIN) / (n - 1))
        else:
            size = TRAIL_DOT_SIZE_MAX
        r = max(size // 2, 1)
        draw.ellipse((tx - r, ty - r, tx + r, ty + r), fill=COLOUR_TRAIL)


def draw_aircraft(
    image: Image.Image,
    aircraft: Aircraft,
    pos: tuple[int, int],
    trail: collections.deque[tuple[int, int]],
) -> None:
    cx, cy = pos
    colour = altitude_to_colour(aircraft.altitude_ft)
    draw = ImageDraw.Draw(image)
    _draw_trail(draw, trail)
    if aircraft.is_stale:
        _draw_arrow(draw, cx, cy, aircraft.heading or 0.0, COLOUR_STALE_OUTLINE, is_mlat=True)
    else:
        _draw_arrow(draw, cx, cy, aircraft.heading or 0.0, colour, aircraft.is_mlat)
    _draw_label(draw, cx, cy, aircraft, colour)
