import { Application } from 'pixi.js';
import { Renderer, RendererOptions } from '../render/Renderer';
import { Camera } from '../render/Camera';
import { GameLoop } from './GameLoop';
import { EventBus } from './EventBus';
import { InputManager } from '../input/InputManager';
import { SceneManager } from '../scene/SceneManager';

/**
 * Собирает подсистемы движка. Приложение создаёт Engine, добавляет стартовую
 * сцену через scenes.replace() и вызывает start().
 */
export interface EngineOptions extends RendererOptions {
    /** FPS фиксированного шага. */
    fixedFps: number;
}

export class Engine {
    readonly renderer: Renderer;
    readonly camera: Camera;
    readonly events = new EventBus();
    readonly scenes = new SceneManager();
    input!: InputManager;
    private loop: GameLoop;
    private started = false;

    constructor(options: EngineOptions) {
        this.renderer = new Renderer(options);
        this.camera = new Camera(options.virtualWidth, options.virtualHeight);
        this.loop = new GameLoop(options.fixedFps, {
            update: (dt) => this.tick(dt),
            render: () => this.render()
        });
    }

    /**
     * Инициализация WebGL и ввода. Вызвать до scenes.replace()/start().
     * Возвращает готовый Application (для прямого доступа к Pixi при необходимости).
     */
    async init(): Promise<Application> {
        await this.renderer.setup();
        this.input = new InputManager(
            this.renderer.app.canvas,
            this.renderer.virtualWidth,
            this.renderer.virtualHeight
        );
        return this.renderer.app;
    }

    async start(): Promise<void> {
        if (this.started) return;
        this.started = true;
        this.loop.start();
    }

    stop(): void {
        this.loop.stop();
        this.input?.dispose();
        this.events.clear();
    }

    /** Один фиксированный шаг. */
    private tick(dt: number): void {
        this.scenes.update(dt);
        this.input?.endTick();
    }

    private render(): void {
        this.camera.apply(this.renderer.worldRoot);
        this.scenes.render();
        this.renderer.render();
    }
}