/**
 * Изометрия 2:1 (классический «ромб»).
 *
 * Сетка — целочисленные координаты тайлов (tx, ty).
 * Экран — виртуальные пиксели. Тайл рисуется ромбом шириной tileW и высотой tileH (2:1).
 *
 * Формулы:
 *   screenX = (tx - ty) * (tileW / 2)
 *   screenY = (tx + ty) * (tileH / 2)
 * Обратная:
 *   tx = (screenX / (tileW/2) + screenY / (tileH/2)) / 2
 *   ty = (screenY / (tileH/2) - screenX / (tileW/2)) / 2
 */

export interface IsoLayout {
    /** Ширина ромба тайла (пиксели, чётное) — проекция 1 мирового юнита. */
    tileW: number;
    /** Высота ромба тайла (пиксели, tileW / 2). */
    tileH: number;
    /** Экранные координаты верхнего левого угла нулевого тайла. */
    originX: number;
    originY: number;
}

/** Стандартный тайл 32x16. */
export const DEFAULT_ISO: IsoLayout = { tileW: 32, tileH: 16, originX: 0, originY: 0 };

export function isoToScreen(tx: number, ty: number, iso: IsoLayout = DEFAULT_ISO): { x: number; y: number } {
    return {
        x: Math.round((tx - ty) * (iso.tileW / 2) + iso.originX),
        y: Math.round((tx + ty) * (iso.tileH / 2) + iso.originY)
    };
}

export function screenToIso(sx: number, sy: number, iso: IsoLayout = DEFAULT_ISO): { x: number; y: number } {
    const halfW = iso.tileW / 2;
    const halfH = iso.tileH / 2;
    const dx = (sx - iso.originX) / halfW;
    const dy = (sy - iso.originY) / halfH;
    return { x: Math.floor((dx + dy) / 2), y: Math.floor((dy - dx) / 2) };
}

/**
 * Точное попадание внутрь ромба тайла (floor может дать соседний тайл на границе).
 * Возвращает координаты тайла, ромб которого содержит точку, или null.
 */
export function screenToIsoExact(
    sx: number,
    sy: number,
    mapW: number,
    mapH: number,
    iso: IsoLayout = DEFAULT_ISO
): { x: number; y: number } | null {
    const cand = screenToIso(sx, sy, iso);
    for (let dy = 0; dy <= 1; dy++) {
        for (let dx = 0; dx <= 1; dx++) {
            const tx = cand.x + dx;
            const ty = cand.y + dy;
            const c = isoToScreen(tx, ty, iso);
            const rx = sx - c.x;
            const ry = sy - c.y;
            // Ромб: |x/halfW| + |y/halfH| <= 1
            if (Math.abs(rx) / (iso.tileW / 2) + Math.abs(ry) / (iso.tileH / 2) <= 1) {
                if (tx >= 0 && ty >= 0 && tx < mapW && ty < mapH) {
                    return { x: tx, y: ty };
                }
                return null;
            }
        }
    }
    return null;
}

// ---------- мировые юниты ----------
//
// Позиции хранятся в мировых юнитах (1 юнит = 1 тайл), float в мировой плоскости.
// Экран — виртуальные пиксели; перевод мир→экран — проекция, округление только
// на границе отрисовки (Camera.apply). Две «линейки»:
//   • точка (позиция) — анизотропная проекция: tileW/2 и tileH/2 px на юнит;
//   • скаляр (дистанция, радиус, скорость, высота) — линейка проекции: tileW px на юнит.

import type { Vec2 } from './Vec2';

/** Мировая позиция → экранные пиксели (float, без округления). */
export function worldToScreen(wx: number, wy: number, iso: IsoLayout = DEFAULT_ISO): Vec2 {
    return {
        x: (wx - wy) * (iso.tileW / 2) + iso.originX,
        y: (wx + wy) * (iso.tileH / 2) + iso.originY
    };
}

/** Экранные пиксели → мировая позиция (float). */
export function screenToWorld(sx: number, sy: number, iso: IsoLayout = DEFAULT_ISO): Vec2 {
    const halfW = iso.tileW / 2;
    const halfH = iso.tileH / 2;
    const dx = (sx - iso.originX) / halfW;
    const dy = (sy - iso.originY) / halfH;
    return { x: (dx + dy) / 2, y: (dy - dx) / 2 };
}

/** Скаляр (дистанция, скорость…) из юнитов в пиксели. */
export function unitsToPx(units: number, iso: IsoLayout = DEFAULT_ISO): number {
    return units * iso.tileW;
}

/** Скаляр из пикселей в юниты. */
export function pxToUnits(px: number, iso: IsoLayout = DEFAULT_ISO): number {
    return px / iso.tileW;
}

/** Длина смещения в юнитах по «метрике проекции»: |M(d)| / tileW. */
export function worldLen(dx: number, dy: number, iso: IsoLayout = DEFAULT_ISO): number {
    const sx = (dx - dy) * (iso.tileW / 2);
    const sy = (dx + dy) * (iso.tileH / 2);
    return Math.hypot(sx, sy) / iso.tileW;
}

/** Нормализованный мировой вектор (нулевой -> {0, 0}). */
export function worldNorm(dx: number, dy: number, iso: IsoLayout = DEFAULT_ISO): Vec2 {
    const l = worldLen(dx, dy, iso);
    if (l < 1e-8) return { x: 0, y: 0 };
    return { x: dx / l, y: dy / l };
}

/** Расстояние между точками в юнитах по метрике проекции. */
export function worldDist(a: Vec2, b: Vec2, iso: IsoLayout = DEFAULT_ISO): number {
    return worldLen(b.x - a.x, b.y - a.y, iso);
}

/** Шаг из a в сторону b на step юнитов (аналог moveTo в мировых юнитах). */
export function moveTowardsW(a: Vec2, b: Vec2, step: number, iso: IsoLayout = DEFAULT_ISO): Vec2 {
    const dx = b.x - a.x;
    const dy = b.y - a.y;
    const l = worldLen(dx, dy, iso);
    if (l <= step || l < 1e-8) return { x: b.x, y: b.y };
    const k = step / l;
    return { x: a.x + dx * k, y: a.y + dy * k };
}

/** Центр тайла в мировых юнитах. */
export function tileToWorld(tx: number, ty: number): Vec2 {
    return { x: tx + 0.5, y: ty + 0.5 };
}

/** Мировая позиция -> тайл (floor + границы карты); вне карты -> null. */
export function worldToTile(
    wx: number,
    wy: number,
    mapW: number,
    mapH: number
): { x: number; y: number } | null {
    const tx = Math.floor(wx);
    const ty = Math.floor(wy);
    if (tx < 0 || ty < 0 || tx >= mapW || ty >= mapH) return null;
    return { x: tx, y: ty };
}

/** Мировой прямоугольник -> экранный bbox (для bounds камеры). */
export function worldRectToScreen(
    x: number,
    y: number,
    w: number,
    h: number,
    iso: IsoLayout = DEFAULT_ISO
): { x: number; y: number; width: number; height: number } {
    const tl = worldToScreen(x, y, iso);
    const hw = iso.tileW / 2;
    const hh = iso.tileH / 2;
    // Левый край bbox — у соседа-близнеца (x, y + h), правый — у (x + w, y).
    return {
        x: tl.x - h * hw,
        y: tl.y,
        width: (w + h) * hw,
        height: (w + h) * hh
    };
}