/**
 * SparkSplatViewer - 3D高斯泼溅渲染器组件
 * 基于 Three.js + SparkJS 封装，使用本地文件
 * 
 * 使用方法:
 * const viewer = new SparkSplatViewer(container, options);
 * viewer.loadModel(url, options);
 * viewer.destroy();
 */

import * as THREE from "./js/three.module.js";
import { SparkRenderer, SplatMesh } from "./js/spark.module.js";
import { OrbitControls } from "./js/OrbitControls.js";

export class SparkSplatViewer {
    /**
     * @param {HTMLElement} container - 渲染容器DOM元素
     * @param {Object} options - 配置选项
     */
    constructor(container, options = {}) {
        this.container = container;
        this.options = {
            // 场景配置
            backgroundColor: options.backgroundColor ?? 0x1a1a2e,

            // 相机配置
            fov: options.fov ?? 45,
            fovMobile: options.fovMobile ?? 20,
            near: options.near ?? 0.001,
            far: options.far ?? 10000,
            initialPosition: options.initialPosition ?? new THREE.Vector3(0, 0, 5),

            // 控制器配置
            dampingFactor: options.dampingFactor ?? 0.08,
            maxDistance: options.maxDistance ?? 100,
            minDistance: options.minDistance ?? 0.01,
            minPolarAngle: options.minPolarAngle ?? (Math.PI / 2 - Math.PI / 6),
            maxPolarAngle: options.maxPolarAngle ?? (Math.PI / 2 + Math.PI / 6),
            minAzimuthAngle: options.minAzimuthAngle ?? -Math.PI / 10,
            maxAzimuthAngle: options.maxAzimuthAngle ?? Math.PI / 10,
            enablePan: options.enablePan ?? false,

            // Spark渲染器配置
            maxStdDev: options.maxStdDev ?? Math.sqrt(5),
            sortRadial: options.sortRadial ?? true,
            enableLod: options.enableLod ?? true,
            lodSplatScale: options.lodSplatScale ?? 1.0,

            // 模型配置
            defaultModelUrl: options.defaultModelUrl ?? "./Advance.spz",
            modelRotationX: options.modelRotationX ?? -Math.PI,

            // 缩放动画配置
            zoomDuration: options.zoomDuration ?? 500,
            desktopZoom: options.desktopZoom ?? 2.5,
            mobileZoom: options.mobileZoom ?? 0.55,
            tabletZoom: options.tabletZoom ?? 1.0,

            // 自动旋转配置
            autoRotate: options.autoRotate ?? true,
            autoRotateSpeed: options.autoRotateSpeed ?? 0.3,
            autoRotateIdleDelay: options.autoRotateIdleDelay ?? 2000,

            // 回调函数
            onLoad: options.onLoad ?? null,
            onProgress: options.onProgress ?? null,
            onError: options.onError ?? null,
        };

        // 内部状态
        this.scene = null;
        this.camera = null;
        this.renderer = null;
        this.controls = null;
        this.spark = null;
        this.splatMesh = null;
        this.zoomAnimId = null;
        this.isDestroyed = false;
        this.HOME_POS = new THREE.Vector3();
        this.HOME_TARGET = new THREE.Vector3();

        // 自动旋转状态
        this._isUserInteracting = false;
        this._idleTimer = null;
        this._autoRotateTime = 0;
        this._clock = new THREE.Clock();
        this._lastCameraPos = new THREE.Vector3();
        this._lastCameraQuat = new THREE.Quaternion();
        this._isAutoRotating = false;
        this._spherical = new THREE.Spherical();
        this._quat = new THREE.Quaternion();
        this._quatInverse = new THREE.Quaternion();
        this._offset = new THREE.Vector3();

        this._init();
    }

    /** 初始化场景 */
    _init() {
        const { options } = this;
        const aspect = this.container.clientWidth / this.container.clientHeight;
        const deviceType = this._getDeviceType();

        // 1. 场景
        this.scene = new THREE.Scene();
        this.scene.background = new THREE.Color(options.backgroundColor);

        // 2. 相机
        const fov = deviceType === "desktop" ? options.fov : options.fovMobile;
        this.camera = new THREE.PerspectiveCamera(fov, aspect, options.near, options.far);
        this.camera.position.copy(options.initialPosition);

        // 3. 渲染器
        this.renderer = new THREE.WebGLRenderer({
            antialias: false,
            alpha: false,
            canvas: options.canvas || undefined,
        });
        this.renderer.setSize(this.container.clientWidth, this.container.clientHeight);
        this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
        this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
        this.renderer.toneMappingExposure = 1;

        if (!options.canvas) {
            this.container.appendChild(this.renderer.domElement);
        }

        // 4. 控制器
        this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        this.controls.enableDamping = true;
        this.controls.dampingFactor = options.dampingFactor;
        this.controls.maxDistance = options.maxDistance;
        this.controls.minDistance = options.minDistance;
        this.controls.minPolarAngle = options.minPolarAngle;
        this.controls.maxPolarAngle = options.maxPolarAngle;
        this.controls.minAzimuthAngle = options.minAzimuthAngle;
        this.controls.maxAzimuthAngle = options.maxAzimuthAngle;
        this.controls.target.set(0, 0, 0);
        this.controls.object.up.set(0, 0, 0);

        if (!options.enablePan) {
            this.controls.mouseButtons = {
                LEFT: THREE.MOUSE.ROTATE,
                MIDDLE: THREE.MOUSE.DOLLY,
                RIGHT: null,
            };
        }

        this.controls.update();

        // 保存初始视角
        this.HOME_POS.copy(this.camera.position);
        this.HOME_TARGET.copy(this.controls.target);

        // 初始化自动旋转所需的四元数（用于坐标转换）
        this._quat.setFromUnitVectors(this.camera.up, new THREE.Vector3(0, 1, 0));
        this._quatInverse.copy(this._quat).invert();

        // 立即启动自动旋转（无需等待模型加载）
        if (this.options.autoRotate) {
            this._updateSphericalFromCamera();
        }

        // 5. 监听交互事件
        this._setupInteractionListeners();

        // 6. Spark渲染器
        this.spark = new SparkRenderer({
            renderer: this.renderer,
            maxStdDev: options.maxStdDev,
            sortRadial: options.sortRadial,
            enableLod: options.enableLod,
            lodSplatScale: options.lodSplatScale,
        });
        this.scene.add(this.spark);

        // 7. 窗口大小变化监听
        this._onResize = this._handleResize.bind(this);
        window.addEventListener("resize", this._onResize);

        // 8. 启动渲染循环
        this._startRenderLoop();

        // 9. 加载默认模型
        if (options.defaultModelUrl) {
            this.loadModel(options.defaultModelUrl, {
                autoFit: true,
                onComplete: options.onLoad,
            });
        }
    }

    /** 设置交互监听器 */
    _setupInteractionListeners() {
        // 用户开始交互时暂停自动旋转
        this.controls.addEventListener("start", () => {
            this._isUserInteracting = true;
            this._isAutoRotating = false;
            if (this._idleTimer) {
                clearTimeout(this._idleTimer);
                this._idleTimer = null;
            }
        });

        // 用户结束交互时启动空闲计时器
        this.controls.addEventListener("end", () => {
            this._isUserInteracting = false;
            // 延迟恢复自动旋转，让用户有时间观察
            this._idleTimer = setTimeout(() => {
                if (!this._isUserInteracting && this.options.autoRotate) {
                    this._startAutoRotateFromCurrent();
                }
            }, this.options.autoRotateIdleDelay);
        });
    }

    /** 从当前相机位置开始自动旋转 */
    _startAutoRotateFromCurrent() {
        if (!this.controls || this.isDestroyed) return;

        // 记录当前相机位置对应的 spherical 坐标，作为自动旋转的起点
        this._updateSphericalFromCamera();
        this._isAutoRotating = true;
    }

    /** 根据当前相机位置更新 spherical 坐标 */
    _updateSphericalFromCamera() {
        this._offset.copy(this.camera.position).sub(this.controls.target);
        this._offset.applyQuaternion(this._quat);
        this._spherical.setFromVector3(this._offset);
    }

    /** 应用 spherical 坐标到相机 */
    _applySphericalToCamera() {
        this._offset.setFromSpherical(this._spherical);
        this._offset.applyQuaternion(this._quatInverse);
        this.camera.position.copy(this.controls.target).add(this._offset);
        this.camera.lookAt(this.controls.target);
    }

    /** 执行自动旋转 */
    _doAutoRotate(deltaTime) {
        if (!this._isAutoRotating || this._isUserInteracting) return;

        const opts = this.options;
        const speed = opts.autoRotateSpeed;

        // 计算角度范围
        const thetaRange = opts.maxAzimuthAngle - opts.minAzimuthAngle;
        const phiRange = opts.maxPolarAngle - opts.minPolarAngle;

        // 使用正弦波在限制范围内摆动，覆盖整个区域
        // theta: 水平方向（左右）
        // phi: 垂直方向（上下）
        // 使用不同频率创造"8字形"或椭圆轨迹，确保覆盖整个可见区域
        this._autoRotateTime += deltaTime * speed;

        // 水平方向：完整遍历 minAzimuthAngle ~ maxAzimuthAngle
        const thetaCenter = (opts.minAzimuthAngle + opts.maxAzimuthAngle) / 2;
        const thetaAmp = thetaRange / 2 * 0.95; // 留一点边距避免卡在边界
        const targetTheta = thetaCenter + Math.sin(this._autoRotateTime) * thetaAmp;

        // 垂直方向：完整遍历 minPolarAngle ~ maxPolarAngle
        // 使用 2 倍频率，确保上下也能充分覆盖
        const phiCenter = (opts.minPolarAngle + opts.maxPolarAngle) / 2;
        const phiAmp = phiRange / 2 * 0.95;
        const targetPhi = phiCenter + Math.sin(this._autoRotateTime * 1.618) * phiAmp;

        // 平滑插值到目标角度（避免突变）
        const lerpFactor = Math.min(deltaTime * 2.0, 1.0);
        this._spherical.theta += (targetTheta - this._spherical.theta) * lerpFactor;
        this._spherical.phi += (targetPhi - this._spherical.phi) * lerpFactor;

        // 限制在范围内
        this._spherical.theta = Math.max(
            opts.minAzimuthAngle,
            Math.min(opts.maxAzimuthAngle, this._spherical.theta)
        );
        this._spherical.phi = Math.max(
            opts.minPolarAngle,
            Math.min(opts.maxPolarAngle, this._spherical.phi)
        );

        this._spherical.makeSafe();

        // 应用到相机
        this._applySphericalToCamera();
    }

    /** 渲染循环 */
    _startRenderLoop() {
        const animate = () => {
            if (this.isDestroyed) return;

            const deltaTime = this._clock.getDelta();

            // 自动旋转（仅在用户未交互时）
            if (this.options.autoRotate && !this._isUserInteracting) {
                this._doAutoRotate(deltaTime);
            }

            this.controls.update();
            this.renderer.render(this.scene, this.camera);
            requestAnimationFrame(animate);
        };
        requestAnimationFrame(animate);
    }

    /** 窗口大小变化处理 */
    _handleResize() {
        if (!this.container || this.isDestroyed) return;
        const w = this.container.clientWidth;
        const h = this.container.clientHeight;
        this.camera.aspect = w / h;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(w, h);
    }

    /** 获取设备类型 */
    _getDeviceType() {
        const ua = navigator.userAgent;
        const isTablet = /iPad|Android(?!.*Mobile)/i.test(ua) || 
                        (/Android/i.test(ua) && window.innerWidth > 768);
        if (/Mobi|Android|iPhone/i.test(ua) && !isTablet) return "mobile";
        if (isTablet) return "tablet";
        return "desktop";
    }

    /** 
     * 获取模型包围盒
     * @returns {THREE.Box3|null}
     */
    getModelBounds() {
        if (!this.splatMesh) return null;
        const box = new THREE.Box3();
        this.splatMesh.traverse((child) => {
            if (child.isMesh && child.geometry) {
                box.expandByObject(child);
            } else if (child.isPoints && child.geometry) {
                box.expandByObject(child);
            }
        });
        if (box.isEmpty()) {
            box.setFromObject(this.splatMesh);
        }
        return box;
    }

    /** 
     * 适配相机到模型
     */
    fitCameraToModel() {
        const box = this.getModelBounds();
        if (!box || box.isEmpty()) {
            console.warn("[SparkSplatViewer] 无法计算包围盒，使用默认范围");
            const size = 0.3;
            const dist = size / (2 * Math.tan((this.camera.fov * Math.PI) / 360));
            this.camera.position.set(0, 0, dist * 3);
            this.controls.target.set(0, 0, 0);
            this.controls.update();
            return;
        }

        const center = new THREE.Vector3();
        box.getCenter(center);
        const size = new THREE.Vector3();
        box.getSize(size);
        const maxDim = Math.max(size.x, size.y, size.z);

        this.controls.target.copy(center);

        const fovRad = (this.camera.fov * Math.PI) / 180;
        const dist = maxDim / (2 * Math.tan(fovRad / 2)) * 0.25;

        const newPos = center.clone();
        newPos.z += Math.max(dist, 0.1);

        this.camera.position.copy(newPos);
        this.camera.lookAt(center);
        this.controls.update();

        this.controls.maxDistance = dist * 3;
        this.controls.minDistance = maxDim * 0.1;

        this.HOME_POS.copy(this.camera.position);
        this.HOME_TARGET.copy(this.controls.target);

        // 自动旋转从当前位置继续（模型加载完成后立即启动）
        if (this.options.autoRotate) {
            this._startAutoRotateFromCurrent();
        }
    }

    /**
     * 加载新模型（替换旧模型）
     * @param {string} url - 模型文件路径
     * @param {Object} loadOptions - 加载配置
     * @param {boolean} loadOptions.autoFit - 是否自动调整相机视角，默认 true
     * @param {number} loadOptions.zoomDuration - 缩放动画时长(ms)，默认 500
     * @param {number} loadOptions.desktopZoom - 桌面端目标 zoom，默认 2.5
     * @param {number} loadOptions.mobileZoom - 移动端目标 zoom，默认 0.55
     * @param {number} loadOptions.tabletZoom - 平板端目标 zoom，默认 1.0
     * @param {Function} loadOptions.onComplete - 加载完成回调
     * @param {Function} loadOptions.onError - 加载失败回调
     * @param {Function} loadOptions.onProgress - 加载进度回调
     * @returns {SplatMesh} 新创建的 SplatMesh 实例
     */
    loadModel(url, loadOptions = {}) {
        const {
            autoFit = true,
            zoomDuration = this.options.zoomDuration,
            desktopZoom = this.options.desktopZoom,
            mobileZoom = this.options.mobileZoom,
            tabletZoom = this.options.tabletZoom,
            onComplete = null,
            onError = null,
            onProgress = null,
        } = loadOptions;

        // 1. 移除旧模型
        this._disposeCurrentModel();

        // 2. 创建新模型
        const newMesh = new SplatMesh({
            url: url,
            onLoad: (mesh) => {
                console.log(`[SparkSplatViewer] 模型加载完成: ${url}`);

                // 旋转修正
                mesh.rotation.x = this.options.modelRotationX;

                if (autoFit) {
                    this.fitCameraToModel();
                    const deviceType = this._getDeviceType();
                    let targetZoom = desktopZoom;
                    if (deviceType === "mobile") targetZoom = mobileZoom;
                    else if (deviceType === "tablet") targetZoom = tabletZoom;
                    this.animateZoomTo(targetZoom, zoomDuration);
                }

                // 模型加载完成后立即启动自动旋转
                if (this.options.autoRotate) {
                    this._startAutoRotateFromCurrent();
                }

                this.controls.update();
                if (onComplete) onComplete(mesh);
                if (this.options.onLoad) this.options.onLoad(mesh);
            },
            onProgress: (event) => {
                if (event.lengthComputable) {
                    const pct = ((event.loaded / event.total) * 100).toFixed(1);
                    console.log(`[SparkSplatViewer] 加载进度: ${pct}%`);
                }
                if (onProgress) onProgress(event);
                if (this.options.onProgress) this.options.onProgress(event);
            },
            onError: (err) => {
                console.error(`[SparkSplatViewer] 模型加载失败: ${url}`, err);
                if (onError) onError(err);
                if (this.options.onError) this.options.onError(err);
            },
        });

        // 3. 添加到场景
        this.scene.add(newMesh);
        this.splatMesh = newMesh;

        return newMesh;
    }

    /** 清理当前模型资源 */
    _disposeCurrentModel() {
        if (!this.splatMesh) return;

        // 停止进行中的 zoom 动画
        if (this.zoomAnimId) {
            cancelAnimationFrame(this.zoomAnimId);
            this.zoomAnimId = null;
        }

        // 从场景中移除
        this.scene.remove(this.splatMesh);

        // 清理资源
        this.splatMesh.traverse((child) => {
            if (child.geometry) child.geometry.dispose();
            if (child.material) {
                if (Array.isArray(child.material)) {
                    child.material.forEach((m) => {
                        if (m.map) m.map.dispose();
                        m.dispose();
                    });
                } else {
                    if (child.material.map) child.material.map.dispose();
                    child.material.dispose();
                }
            }
        });

        if (typeof this.splatMesh.dispose === "function") {
            this.splatMesh.dispose();
        }

        this.splatMesh = null;
        console.log("[SparkSplatViewer] 旧模型已移除，资源已清理");
    }

    /**
     * 平滑过渡 zoom
     * @param {number} targetZoom - 目标缩放值
     * @param {number} duration - 动画时长(ms)
     */
    animateZoomTo(targetZoom, duration = 500) {
        if (this.zoomAnimId) cancelAnimationFrame(this.zoomAnimId);
        const startZoom = this.camera.zoom;
        const startTime = performance.now();

        const step = (time) => {
            const t = Math.min((time - startTime) / duration, 1);
            const ease = t < 0.5 
                ? 4 * t * t * t 
                : 1 - Math.pow(-2 * t + 2, 3) / 2;
            this.camera.zoom = startZoom + (targetZoom - startZoom) * ease;
            this.camera.updateProjectionMatrix();
            if (t < 1) {
                this.zoomAnimId = requestAnimationFrame(step);
            } else {
                this.zoomAnimId = null;
            }
        };
        this.zoomAnimId = requestAnimationFrame(step);
    }

    /** 重置相机到初始位置 */
    resetCamera() {
        this.camera.position.copy(this.HOME_POS);
        this.controls.target.copy(this.HOME_TARGET);
        this.controls.update();
        if (this.options.autoRotate) {
            this._updateSphericalFromCamera();
        }
    }

    /** 获取当前相机信息 */
    getCameraInfo() {
        const p = this.camera.position;
        const t = this.controls.target;
        return {
            position: { x: p.x, y: p.y, z: p.z },
            target: { x: t.x, y: t.y, z: t.z },
            zoom: this.camera.zoom,
        };
    }

    /** 销毁组件 */
    destroy() {
        this.isDestroyed = true;

        if (this.zoomAnimId) {
            cancelAnimationFrame(this.zoomAnimId);
            this.zoomAnimId = null;
        }

        if (this._idleTimer) {
            clearTimeout(this._idleTimer);
            this._idleTimer = null;
        }

        this._disposeCurrentModel();

        if (this.spark) {
            this.scene.remove(this.spark);
            this.spark = null;
        }

        if (this.controls) {
            this.controls.dispose();
            this.controls = null;
        }

        if (this.renderer) {
            this.renderer.dispose();
            if (this.renderer.domElement.parentNode) {
                this.renderer.domElement.parentNode.removeChild(this.renderer.domElement);
            }
            this.renderer = null;
        }

        window.removeEventListener("resize", this._onResize);

        this.scene = null;
        this.camera = null;
        this.container = null;

        console.log("[SparkSplatViewer] 组件已销毁");
    }
}

export default SparkSplatViewer;