/**
 * Main bridge between TypeScript ECS and Rust Engine.
 * TypeScript ECS与Rust引擎之间的主桥接层。
 */

import type { SpriteRenderData, TextureLoadRequest, EngineStats, CameraConfig } from '../types';
import type { IEngineBridge } from '@esengine/asset-system';
import type { GameEngine } from '../wasm/es_engine';

/**
 * Engine bridge configuration.
 * 引擎桥接配置。
 */
export interface EngineBridgeConfig {
    /** Canvas element ID. | Canvas元素ID。 */
    canvasId: string;
    /** Initial canvas width. | 初始画布宽度。 */
    width?: number;
    /** Initial canvas height. | 初始画布高度。 */
    height?: number;
    /** Maximum sprites per batch. | 每批次最大精灵数。 */
    maxSprites?: number;
    /** Enable debug mode. | 启用调试模式。 */
    debug?: boolean;
}

/**
 * Bridge for communication between ECS Framework and Rust Engine.
 * ECS框架与Rust引擎之间的通信桥接。
 *
 * This class manages data transfer between the TypeScript ECS layer
 * and the WebAssembly-based Rust rendering engine.
 * 此类管理TypeScript ECS层与基于WebAssembly的Rust渲染引擎之间的数据传输。
 *
 * @example
 * ```typescript
 * const bridge = new EngineBridge({ canvasId: 'game-canvas' });
 * await bridge.initialize();
 *
 * // In game loop | 在游戏循环中
 * bridge.clear(0, 0, 0, 1);
 * bridge.submitSprites(spriteDataArray);
 * bridge.render();
 * ```
 */
export class EngineBridge implements IEngineBridge {
    private engine: GameEngine | null = null;
    private config: Required<EngineBridgeConfig>;
    private initialized = false;

    // Path resolver for converting file paths to URLs
    // 用于将文件路径转换为URL的路径解析器
    private pathResolver: ((path: string) => string) | null = null;

    // Pre-allocated typed arrays for batch submission
    // 预分配的类型数组用于批量提交
    private transformBuffer: Float32Array;
    private textureIdBuffer: Uint32Array;
    private uvBuffer: Float32Array;
    private colorBuffer: Uint32Array;

    // Statistics | 统计信息
    private stats: EngineStats = {
        fps: 0,
        drawCalls: 0,
        spriteCount: 0,
        frameTime: 0
    };

    private lastFrameTime = 0;
    private frameCount = 0;
    private fpsAccumulator = 0;

    /**
     * Create a new engine bridge.
     * 创建新的引擎桥接。
     *
     * @param config - Bridge configuration | 桥接配置
     */
    constructor(config: EngineBridgeConfig) {
        this.config = {
            canvasId: config.canvasId,
            width: config.width ?? 800,
            height: config.height ?? 600,
            maxSprites: config.maxSprites ?? 10000,
            debug: config.debug ?? false
        };

        // Pre-allocate buffers | 预分配缓冲区
        const maxSprites = this.config.maxSprites;
        this.transformBuffer = new Float32Array(maxSprites * 7); // x, y, rot, sx, sy, ox, oy
        this.textureIdBuffer = new Uint32Array(maxSprites);
        this.uvBuffer = new Float32Array(maxSprites * 4); // u0, v0, u1, v1
        this.colorBuffer = new Uint32Array(maxSprites);
    }

    /**
     * Initialize the engine bridge with WASM module.
     * 使用WASM模块初始化引擎桥接。
     *
     * @param wasmModule - Pre-imported WASM module | 预导入的WASM模块
     */
    async initializeWithModule(wasmModule: any): Promise<void> {
        if (this.initialized) {
            console.warn('EngineBridge already initialized | EngineBridge已初始化');
            return;
        }

        try {
            // Initialize WASM | 初始化WASM
            if (wasmModule.default) {
                await wasmModule.default();
            }

            // Create engine instance | 创建引擎实例
            this.engine = new wasmModule.GameEngine(this.config.canvasId);
            this.initialized = true;

            if (this.config.debug) {
                console.log('EngineBridge initialized | EngineBridge初始化完成');
            }
        } catch (error) {
            throw new Error(`Failed to initialize engine: ${error} | 引擎初始化失败: ${error}`);
        }
    }

    /**
     * Initialize the engine bridge.
     * 初始化引擎桥接。
     *
     * Loads the WASM module and creates the engine instance.
     * 加载WASM模块并创建引擎实例。
     *
     * @param wasmPath - Path to WASM package | WASM包路径
     * @deprecated Use initializeWithModule instead | 请使用 initializeWithModule 代替
     */
    async initialize(wasmPath = '@esengine/engine'): Promise<void> {
        if (this.initialized) {
            console.warn('EngineBridge already initialized | EngineBridge已初始化');
            return;
        }

        try {
            // Dynamic import of WASM module | 动态导入WASM模块
            const wasmModule = await import(/* @vite-ignore */ wasmPath);
            await this.initializeWithModule(wasmModule);
        } catch (error) {
            throw new Error(`Failed to initialize engine: ${error} | 引擎初始化失败: ${error}`);
        }
    }

    /**
     * Check if bridge is initialized.
     * 检查桥接是否已初始化。
     */
    get isInitialized(): boolean {
        return this.initialized;
    }

    /**
     * Get canvas width.
     * 获取画布宽度。
     */
    get width(): number {
        return this.engine?.width ?? 0;
    }

    /**
     * Get canvas height.
     * 获取画布高度。
     */
    get height(): number {
        return this.engine?.height ?? 0;
    }

    /**
     * Get engine instance (throws if not initialized)
     * 获取引擎实例（未初始化时抛出异常）
     */
    private getEngine(): GameEngine {
        if (!this.engine) {
            throw new Error('Engine not initialized. Call initialize() first.');
        }
        return this.engine;
    }

    /**
     * Clear the screen.
     * 清除屏幕。
     *
     * @param r - Red (0-1) | 红色
     * @param g - Green (0-1) | 绿色
     * @param b - Blue (0-1) | 蓝色
     * @param a - Alpha (0-1) | 透明度
     */
    clear(r: number, g: number, b: number, a: number): void {
        if (!this.initialized) return;
        this.getEngine().clear(r, g, b, a);
    }

    /**
     * Submit sprite data for rendering.
     * 提交精灵数据进行渲染。
     *
     * @param sprites - Array of sprite render data | 精灵渲染数据数组
     */
    submitSprites(sprites: SpriteRenderData[]): void {
        if (!this.initialized || sprites.length === 0) return;

        const count = Math.min(sprites.length, this.config.maxSprites);

        // Fill typed arrays | 填充类型数组
        for (let i = 0; i < count; i++) {
            const sprite = sprites[i];
            const tOffset = i * 7;
            const uvOffset = i * 4;

            // Transform data | 变换数据
            this.transformBuffer[tOffset] = sprite.x;
            this.transformBuffer[tOffset + 1] = sprite.y;
            this.transformBuffer[tOffset + 2] = sprite.rotation;
            this.transformBuffer[tOffset + 3] = sprite.scaleX;
            this.transformBuffer[tOffset + 4] = sprite.scaleY;
            this.transformBuffer[tOffset + 5] = sprite.originX;
            this.transformBuffer[tOffset + 6] = sprite.originY;

            // Texture ID | 纹理ID
            this.textureIdBuffer[i] = sprite.textureId;

            // UV coordinates | UV坐标
            this.uvBuffer[uvOffset] = sprite.uv[0];
            this.uvBuffer[uvOffset + 1] = sprite.uv[1];
            this.uvBuffer[uvOffset + 2] = sprite.uv[2];
            this.uvBuffer[uvOffset + 3] = sprite.uv[3];

            // Color | 颜色
            this.colorBuffer[i] = sprite.color;
        }

        // Submit to engine (single WASM call) | 提交到引擎（单次WASM调用）
        this.getEngine().submitSpriteBatch(
            this.transformBuffer.subarray(0, count * 7),
            this.textureIdBuffer.subarray(0, count),
            this.uvBuffer.subarray(0, count * 4),
            this.colorBuffer.subarray(0, count)
        );

        this.stats.spriteCount = count;
    }

    /**
     * Render the current frame.
     * 渲染当前帧。
     */
    render(): void {
        if (!this.initialized) return;

        const startTime = performance.now();
        this.getEngine().render();
        const endTime = performance.now();

        // Update statistics | 更新统计信息
        this.stats.frameTime = endTime - startTime;
        this.stats.drawCalls = 1; // Currently single batch | 当前单批次

        // Calculate FPS | 计算FPS
        this.frameCount++;
        this.fpsAccumulator += endTime - this.lastFrameTime;
        this.lastFrameTime = endTime;

        if (this.fpsAccumulator >= 1000) {
            this.stats.fps = this.frameCount;
            this.frameCount = 0;
            this.fpsAccumulator = 0;
        }
    }

    /**
     * Render sprites as overlay without clearing the screen.
     * 渲染精灵作为叠加层，不清除屏幕。
     *
     * This is used for UI rendering on top of world content.
     * 用于在世界内容上渲染 UI。
     */
    renderOverlay(): void {
        if (!this.initialized) return;
        this.getEngine().renderOverlay();
    }

    /**
     * Load a texture.
     * 加载纹理。
     *
     * @param id - Texture ID | 纹理ID
     * @param url - Image URL | 图片URL
     */
    loadTexture(id: number, url: string): Promise<void> {
        if (!this.initialized) return Promise.resolve();
        this.getEngine().loadTexture(id, url);
        // Currently synchronous, but return Promise for interface compatibility
        // 目前是同步的，但返回Promise以兼容接口
        return Promise.resolve();
    }

    /**
     * Load multiple textures.
     * 加载多个纹理。
     *
     * @param requests - Texture load requests | 纹理加载请求
     */
    async loadTextures(requests: Array<{ id: number; url: string }>): Promise<void> {
        for (const req of requests) {
            await this.loadTexture(req.id, req.url);
        }
    }

    /**
     * Load texture by path, returning texture ID.
     * 按路径加载纹理，返回纹理ID。
     *
     * @param path - Image path/URL | 图片路径/URL
     * @returns Texture ID | 纹理ID
     */
    loadTextureByPath(path: string): number {
        if (!this.initialized) return 0;
        return this.getEngine().loadTextureByPath(path);
    }

    /**
     * Get texture ID by path.
     * 按路径获取纹理ID。
     *
     * @param path - Image path | 图片路径
     * @returns Texture ID or undefined | 纹理ID或undefined
     */
    getTextureIdByPath(path: string): number | undefined {
        if (!this.initialized) return undefined;
        return this.getEngine().getTextureIdByPath(path);
    }

    /**
     * Set path resolver for converting file paths to URLs.
     * 设置路径解析器用于将文件路径转换为URL。
     *
     * @param resolver - Function to resolve paths | 解析路径的函数
     */
    setPathResolver(resolver: (path: string) => string): void {
        this.pathResolver = resolver;
    }

    /**
     * Get or load texture by path.
     * 按路径获取或加载纹理。
     *
     * @param path - Image path/URL | 图片路径/URL
     * @returns Texture ID | 纹理ID
     */
    getOrLoadTextureByPath(path: string): number {
        if (!this.initialized) return 0;

        // Resolve path if resolver is set
        // 如果设置了解析器，则解析路径
        const resolvedPath = this.pathResolver ? this.pathResolver(path) : path;
        return this.getEngine().getOrLoadTextureByPath(resolvedPath);
    }

    /**
     * Unload texture from GPU.
     * 从GPU卸载纹理。
     *
     * @param id - Texture ID | 纹理ID
     */
    unloadTexture(id: number): void {
        if (!this.initialized) return;
        // TODO: Implement in Rust engine
        // TODO: 在Rust引擎中实现
        console.warn('unloadTexture not yet implemented in engine');
    }

    /**
     * Get texture information.
     * 获取纹理信息。
     *
     * @param id - Texture ID | 纹理ID
     */
    getTextureInfo(id: number): { width: number; height: number } | null {
        if (!this.initialized) return null;
        // TODO: Implement in Rust engine
        // TODO: 在Rust引擎中实现
        // Return default values for now / 暂时返回默认值
        return { width: 64, height: 64 };
    }

    /**
     * Check if a key is pressed.
     * 检查按键是否按下。
     *
     * @param keyCode - Key code | 键码
     */
    isKeyDown(keyCode: string): boolean {
        if (!this.initialized) return false;
        return this.getEngine().isKeyDown(keyCode);
    }

    /**
     * Update input state (call once per frame).
     * 更新输入状态（每帧调用一次）。
     */
    updateInput(): void {
        if (!this.initialized) return;
        this.getEngine().updateInput();
    }

    /**
     * Get engine statistics.
     * 获取引擎统计信息。
     */
    getStats(): EngineStats {
        return { ...this.stats };
    }

    /**
     * Resize the viewport.
     * 调整视口大小。
     *
     * @param width - New width | 新宽度
     * @param height - New height | 新高度
     */
    resize(width: number, height: number): void {
        if (!this.initialized) return;
        const engine = this.getEngine();
        if (engine.resize) {
            engine.resize(width, height);
        }
    }

    /**
     * Set camera position, zoom, and rotation.
     * 设置相机位置、缩放和旋转。
     *
     * @param config - Camera configuration | 相机配置
     */
    setCamera(config: CameraConfig): void {
        if (!this.initialized) return;
        this.getEngine().setCamera(config.x, config.y, config.zoom, config.rotation);
    }

    /**
     * Get camera state.
     * 获取相机状态。
     */
    getCamera(): CameraConfig {
        if (!this.initialized) {
            return { x: 0, y: 0, zoom: 1, rotation: 0 };
        }
        const state = this.getEngine().getCamera();
        return {
            x: state[0],
            y: state[1],
            zoom: state[2],
            rotation: state[3]
        };
    }

    /**
     * Set grid visibility.
     * 设置网格可见性。
     */
    setShowGrid(show: boolean): void {
        if (!this.initialized) return;
        this.getEngine().setShowGrid(show);
    }

    /**
     * Set clear color (background color).
     * 设置清除颜色（背景颜色）。
     *
     * @param r - Red component (0.0-1.0) | 红色分量
     * @param g - Green component (0.0-1.0) | 绿色分量
     * @param b - Blue component (0.0-1.0) | 蓝色分量
     * @param a - Alpha component (0.0-1.0) | 透明度分量
     */
    setClearColor(r: number, g: number, b: number, a: number): void {
        if (!this.initialized) return;
        this.getEngine().setClearColor(r, g, b, a);
    }

    /**
     * Add a rectangle gizmo outline.
     * 添加矩形Gizmo边框。
     *
     * @param x - Center X position | 中心X位置
     * @param y - Center Y position | 中心Y位置
     * @param width - Rectangle width | 矩形宽度
     * @param height - Rectangle height | 矩形高度
     * @param rotation - Rotation in radians | 旋转角度（弧度）
     * @param originX - Origin X (0-1) | 原点X (0-1)
     * @param originY - Origin Y (0-1) | 原点Y (0-1)
     * @param r - Red (0-1) | 红色
     * @param g - Green (0-1) | 绿色
     * @param b - Blue (0-1) | 蓝色
     * @param a - Alpha (0-1) | 透明度
     * @param showHandles - Whether to show transform handles | 是否显示变换手柄
     */
    addGizmoRect(
        x: number,
        y: number,
        width: number,
        height: number,
        rotation: number,
        originX: number,
        originY: number,
        r: number,
        g: number,
        b: number,
        a: number,
        showHandles: boolean = true
    ): void {
        if (!this.initialized) return;
        this.getEngine().addGizmoRect(x, y, width, height, rotation, originX, originY, r, g, b, a, showHandles);
    }

    /**
     * Add a circle outline gizmo (native rendering).
     * 添加圆形边框Gizmo（原生渲染）。
     */
    addGizmoCircle(
        x: number,
        y: number,
        radius: number,
        r: number,
        g: number,
        b: number,
        a: number
    ): void {
        if (!this.initialized) return;
        this.getEngine().addGizmoCircle(x, y, radius, r, g, b, a);
    }

    /**
     * Add a line gizmo (native rendering).
     * 添加线条Gizmo（原生渲染）。
     */
    addGizmoLine(
        points: number[],
        r: number,
        g: number,
        b: number,
        a: number,
        closed: boolean
    ): void {
        if (!this.initialized) return;
        this.getEngine().addGizmoLine(new Float32Array(points), r, g, b, a, closed);
    }

    /**
     * Add a capsule outline gizmo (native rendering).
     * 添加胶囊边框Gizmo（原生渲染）。
     */
    addGizmoCapsule(
        x: number,
        y: number,
        radius: number,
        halfHeight: number,
        rotation: number,
        r: number,
        g: number,
        b: number,
        a: number
    ): void {
        if (!this.initialized) return;
        this.getEngine().addGizmoCapsule(x, y, radius, halfHeight, rotation, r, g, b, a);
    }

    /**
     * Set transform tool mode.
     * 设置变换工具模式。
     *
     * @param mode - 0=Select, 1=Move, 2=Rotate, 3=Scale
     */
    setTransformMode(mode: number): void {
        if (!this.initialized) return;
        this.getEngine().setTransformMode(mode);
    }

    /**
     * Set gizmo visibility.
     * 设置辅助工具可见性。
     */
    setShowGizmos(show: boolean): void {
        if (!this.initialized) return;
        this.getEngine().setShowGizmos(show);
    }

    // ===== Multi-viewport API =====
    // ===== 多视口 API =====

    /**
     * Register a new viewport.
     * 注册新视口。
     *
     * @param id - Unique viewport identifier | 唯一视口标识符
     * @param canvasId - HTML canvas element ID | HTML canvas元素ID
     */
    registerViewport(id: string, canvasId: string): void {
        if (!this.initialized) return;
        this.getEngine().registerViewport(id, canvasId);
    }

    /**
     * Unregister a viewport.
     * 注销视口。
     */
    unregisterViewport(id: string): void {
        if (!this.initialized) return;
        this.getEngine().unregisterViewport(id);
    }

    /**
     * Set the active viewport.
     * 设置活动视口。
     */
    setActiveViewport(id: string): boolean {
        if (!this.initialized) return false;
        return this.getEngine().setActiveViewport(id);
    }

    /**
     * Set camera for a specific viewport.
     * 为特定视口设置相机。
     */
    setViewportCamera(viewportId: string, config: CameraConfig): void {
        if (!this.initialized) return;
        this.getEngine().setViewportCamera(viewportId, config.x, config.y, config.zoom, config.rotation);
    }

    /**
     * Get camera for a specific viewport.
     * 获取特定视口的相机。
     */
    getViewportCamera(viewportId: string): CameraConfig | null {
        if (!this.initialized) return null;
        const state = this.getEngine().getViewportCamera(viewportId);
        if (!state) return null;
        return {
            x: state[0],
            y: state[1],
            zoom: state[2],
            rotation: state[3]
        };
    }

    /**
     * Set viewport configuration.
     * 设置视口配置。
     */
    setViewportConfig(viewportId: string, showGrid: boolean, showGizmos: boolean): void {
        if (!this.initialized) return;
        this.getEngine().setViewportConfig(viewportId, showGrid, showGizmos);
    }

    /**
     * Resize a specific viewport.
     * 调整特定视口大小。
     */
    resizeViewport(viewportId: string, width: number, height: number): void {
        if (!this.initialized) return;
        this.getEngine().resizeViewport(viewportId, width, height);
    }

    /**
     * Render to a specific viewport.
     * 渲染到特定视口。
     */
    renderToViewport(viewportId: string): void {
        if (!this.initialized) return;
        this.getEngine().renderToViewport(viewportId);
    }

    /**
     * Get all registered viewport IDs.
     * 获取所有已注册的视口ID。
     */
    getViewportIds(): string[] {
        if (!this.initialized) return [];
        return this.getEngine().getViewportIds();
    }

    // ===== Screen Space Mode API =====
    // ===== 屏幕空间模式 API =====

    // Saved world space camera state
    // 保存的世界空间相机状态
    private savedWorldCamera: CameraConfig | null = null;

    /**
     * Push screen space rendering mode.
     * 进入屏幕空间渲染模式。
     *
     * Saves the current world camera and switches to a fixed orthographic projection
     * centered at (0, 0) with the specified canvas size.
     * 保存当前世界相机并切换到以 (0, 0) 为中心的固定正交投影。
     *
     * @param canvasWidth - UI canvas width (design resolution) | UI 画布宽度（设计分辨率）
     * @param canvasHeight - UI canvas height (design resolution) | UI 画布高度（设计分辨率）
     */
    pushScreenSpaceMode(canvasWidth: number, canvasHeight: number): void {
        if (!this.initialized) return;

        // Save current world camera state
        // 保存当前世界相机状态
        this.savedWorldCamera = this.getCamera();

        // Switch to screen space camera:
        // - Position at origin (0, 0)
        // - Zoom = 1 (1 pixel = 1 world unit)
        // - No rotation
        // 切换到屏幕空间相机：
        // - 位置在原点 (0, 0)
        // - 缩放 = 1（1 像素 = 1 世界单位）
        // - 无旋转
        //
        // For screen space UI, we want the camera to show exactly canvasWidth x canvasHeight pixels
        // centered at (0, 0). This means the visible area is:
        // X: [-canvasWidth/2, canvasWidth/2]
        // Y: [-canvasHeight/2, canvasHeight/2]
        // 对于屏幕空间 UI，我们希望相机精确显示 canvasWidth x canvasHeight 像素
        // 以 (0, 0) 为中心。这意味着可见区域是：
        // X: [-canvasWidth/2, canvasWidth/2]
        // Y: [-canvasHeight/2, canvasHeight/2]

        // Get current viewport size to calculate proper zoom
        // 获取当前视口尺寸以计算正确的缩放
        // Note: This assumes canvas.width/height match actual rendering size
        // 注意：这假设 canvas.width/height 与实际渲染尺寸匹配
        const canvas = document.getElementById(this.config.canvasId) as HTMLCanvasElement;
        if (canvas) {
            // Calculate zoom so that canvasWidth x canvasHeight fits exactly in the viewport
            // 计算缩放使 canvasWidth x canvasHeight 正好适合视口
            // zoom = viewport_size / world_visible_size
            // For UI, we want 1 UI unit = 1 pixel on screen when canvas matches viewport
            // 对于 UI，当画布与视口匹配时，我们希望 1 UI 单位 = 1 屏幕像素
            const viewportWidth = canvas.width;
            const viewportHeight = canvas.height;

            // Calculate zoom based on the design canvas size vs actual viewport
            // 根据设计画布尺寸与实际视口计算缩放
            // This scales UI to fit the viewport while maintaining aspect ratio
            const zoomX = viewportWidth / canvasWidth;
            const zoomY = viewportHeight / canvasHeight;

            // Use minimum to ensure entire canvas is visible (letterbox if needed)
            // 使用最小值确保整个画布可见（如需要则显示黑边）
            const zoom = Math.min(zoomX, zoomY);

            this.setCamera({
                x: 0,
                y: 0,
                zoom: zoom,
                rotation: 0
            });
        } else {
            // Fallback: use zoom = 1
            // 回退：使用 zoom = 1
            this.setCamera({
                x: 0,
                y: 0,
                zoom: 1,
                rotation: 0
            });
        }
    }

    /**
     * Pop screen space rendering mode.
     * 退出屏幕空间渲染模式。
     *
     * Restores the previously saved world camera.
     * 恢复之前保存的世界相机。
     */
    popScreenSpaceMode(): void {
        if (!this.initialized) return;

        // Restore world camera
        // 恢复世界相机
        if (this.savedWorldCamera) {
            this.setCamera(this.savedWorldCamera);
            this.savedWorldCamera = null;
        }
    }

    /**
     * Dispose the bridge and release resources.
     * 销毁桥接并释放资源。
     */
    dispose(): void {
        this.engine = null;
        this.initialized = false;
    }
}
