/**
 * GdPlayerCube — визуальные эффекты на куб игрока (этап G7).
 *
 * Находит primitive с id=10001 (REF_BODY в скрипте уровня) и навешивает:
 *   1. Чёрный outline толщиной 0.04 (renderOutline).
 *   2. Бледный glow в primary-цвете скина (через GlowLayer.referenceMeshToUseItsOwnMaterial).
 *   3. Squash & stretch: при увеличении скорости падения сжимается по Y,
 *      при отскоке от земли — растягивается.
 *
 * Использование:
 *   const pc = new GdPlayerCube();
 *   pc.attach(scene, scene3d);
 *   pc.dispose();
 */
import { Color3, GlowLayer } from '@babylonjs/core';

const PLAYER_PRIM_ID = 10001;

export class GdPlayerCube {
    constructor() {
        this.scene = null;
        this._scene3d = null;
        this._cubeMesh = null;
        this._glow = null;
        this._onBeforeRender = null;
        this._prevPlayerY = null;
        this._prevVy = 0;
        this._baseScaleY = 1;
    }

    attach(scene, scene3d) {
        if (!scene || !scene3d) return;
        this.scene = scene;
        this._scene3d = scene3d;
        // Откладываем — primitive может быть не создан в момент enterPlayMode
        let attempts = 0;
        const tryAttach = () => {
            attempts++;
            const pm = scene3d.primitiveManager;
            const data = pm?.instances?.get(PLAYER_PRIM_ID);
            const mesh = data?.mesh;
            if (!mesh) {
                if (attempts < 10) setTimeout(tryAttach, 200);
                else console.warn('[GdPlayerCube] куб игрока не найден');
                return;
            }
            this._cubeMesh = mesh;
            this._baseScaleY = mesh.scaling?.y || 1;
            this._setupGlow();
            this._setupSquash();
            console.log('[GdPlayerCube] прикреплён, mesh=', mesh.name);
        };
        tryAttach();
    }

    /** Чёрная обводка по силуэту куба. */
    _setupOutline() {
        const m = this._cubeMesh;
        try {
            m.renderOutline = true;
            m.outlineWidth = 0.04;
            m.outlineColor = new Color3(0, 0, 0);
        } catch (e) { console.warn('[GdPlayerCube] outline failed', e); }
    }

    /** GlowLayer — куб светится своим primary цветом скина.
     *  GlowLayer создаётся один раз на сцену; если уже есть — используем его. */
    _setupGlow() {
        try {
            // Не создаём GlowLayer если он уже есть на сцене (другой код мог)
            let glow = this.scene.effectLayers?.find?.((l) => l.name === 'gd_glow');
            if (!glow) {
                glow = new GlowLayer('gd_glow', this.scene, { mainTextureFixedSize: 256, blurKernelSize: 24 });
                glow.intensity = 0.6;
                this._glow = glow;
            } else {
                this._glow = null;   // не наш, не disposить
            }
            glow.addIncludedOnlyMesh(this._cubeMesh);
        } catch (e) { console.warn('[GdPlayerCube] glow failed', e); }
    }

    /** Squash при падении / stretch при взлёте.
     *  Считаем vy = (y_now - y_prev) / dt. При большой |vy| меняем scaling.
     *  При приземлении (vy резко 0) — короткий «бум» сжатия по Y. */
    _setupSquash() {
        this._prevPlayerY = null;
        this._prevVy = 0;
        this._landSquashLeft = 0;   // секунд осталось эффекта squash после удара

        this._onBeforeRender = () => {
            const m = this._cubeMesh;
            const pp = this._scene3d?.player?._pos;
            if (!m || !pp || !m.scaling) return;
            const dt = this.scene.getEngine().getDeltaTime() / 1000;
            if (dt <= 0 || dt > 0.2) return;
            const y = pp.y;
            if (this._prevPlayerY == null) {
                this._prevPlayerY = y;
                return;
            }
            const vy = (y - this._prevPlayerY) / dt;
            // Детект приземления: была отрицательная vy, стала ≈ 0
            if (this._prevVy < -8 && Math.abs(vy) < 2) {
                this._landSquashLeft = 0.18;
            }
            this._prevPlayerY = y;
            this._prevVy = vy;

            // Целевой масштаб
            let sy = this._baseScaleY;
            let sxz = 1;
            if (this._landSquashLeft > 0) {
                // Сжатие после удара: y 0.7, xz 1.2 → линейный возврат
                const t = this._landSquashLeft / 0.18;
                const squashAmt = Math.sin(t * Math.PI) * 0.3;
                sy = this._baseScaleY * (1 - squashAmt);
                sxz = 1 + squashAmt * 0.5;
                this._landSquashLeft -= dt;
                if (this._landSquashLeft < 0) this._landSquashLeft = 0;
            } else if (vy > 5) {
                // Взлёт — растягивание по y
                const k = Math.min(1, vy / 15);
                sy = this._baseScaleY * (1 + 0.18 * k);
                sxz = 1 - 0.08 * k;
            } else if (vy < -5) {
                // Падение — лёгкое сжатие по y (минимальное, чтобы не дёргалось)
                const k = Math.min(1, -vy / 15);
                sy = this._baseScaleY * (1 - 0.10 * k);
                sxz = 1 + 0.05 * k;
            }
            // Плавно интерполируем к целевому
            const lerp = (a, b, t) => a + (b - a) * t;
            const speed = 12;
            m.scaling.y = lerp(m.scaling.y, sy, Math.min(1, dt * speed));
            m.scaling.x = lerp(m.scaling.x, sxz, Math.min(1, dt * speed));
            m.scaling.z = lerp(m.scaling.z, sxz, Math.min(1, dt * speed));
        };
        this.scene.onBeforeRenderObservable.add(this._onBeforeRender);
    }

    dispose() {
        if (!this.scene) return;
        try {
            if (this._onBeforeRender) {
                this.scene.onBeforeRenderObservable.removeCallback(this._onBeforeRender);
                this._onBeforeRender = null;
            }
        } catch (e) {}
        if (this._cubeMesh) {
            try {
                this._cubeMesh.renderOutline = false;
                this._cubeMesh.scaling.set(1, 1, 1);
            } catch (e) {}
        }
        if (this._glow) {
            try { this._glow.dispose(); } catch (e) {}
            this._glow = null;
        }
        this._cubeMesh = null;
        this._scene3d = null;
        this.scene = null;
    }
}
