/**
 * Train Renderer
 * Handles Three.js visualization of train models
 * Works in browser environment with Three.js global
 * @module trainRenderer
 */

/**
 * TrainRenderer class - manages 3D train visualization
 */
class TrainRenderer {
    /**
     * Create a train renderer
     * @param {THREE.Scene} scene - Three.js scene
     * @param {Object} options - Configuration options
     * @param {string} options.color - Train body color hex
     */
    constructor(scene, options = {}) {
        this.scene = scene;
        this.trainMeshes = new Map();
        this.trainController = null;
        
        // Default train colors
        this.colors = {
            body: new THREE.Color(options.color || '#FF6B00'),
            windows: new THREE.Color('#87CEEB'),
            wheels: new THREE.Color('#333333'),
            details: new THREE.Color('#FFFFFF')
        };
        
        this.trains = {};
    }

    /**
     * Create a 3D train mesh
     * @param {string} trainId - Unique train identifier
     * @returns {THREE.Group} Train mesh group
     */
    createTrainMesh(trainId) {
        const trainGroup = new THREE.Group();
        trainGroup.userData.id = trainId;

        // Train body (main chassis)
        const bodyGeometry = new THREE.BoxGeometry(2, 1.2, 4);
        const bodyMaterial = new THREE.MeshPhongMaterial({
            color: this.colors.body,
            shininess: 50
        });
        const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
        body.position.y = 0.6;
        trainGroup.add(body);

        // Cab area (front)
        const cabGeometry = new THREE.BoxGeometry(1.8, 0.8, 1);
        const cabMaterial = new THREE.MeshPhongMaterial({
            color: this.colors.windows,
            shininess: 30
        });
        const cab = new THREE.Mesh(cabGeometry, cabMaterial);
        cab.position.set(0.9, 1.1, 0);
        cab.rotation.y = Math.PI / 4;
        trainGroup.add(cab);

        // Wheels (4 visible wheels)
        const wheelGeometry = new THREE.CylinderGeometry(0.3, 0.3, 0.2, 16);
        const wheelMaterial = new THREE.MeshPhongMaterial({
            color: this.colors.wheels,
            shininess: 20
        });
        
        const wheelPositions = [
            { x: 0.3, y: -0.3, z: 1.2, rotZ: Math.PI / 2 },
            { x: 0.3, y: -0.3, z: -1.2, rotZ: Math.PI / 2 },
            { x: -0.3, y: -0.3, z: 1.2, rotZ: Math.PI / 2 },
            { x: -0.3, y: -0.3, z: -1.2, rotZ: Math.PI / 2 }
        ];

        wheelPositions.forEach(pos => {
            const wheel = new THREE.Mesh(wheelGeometry, wheelMaterial);
            wheel.position.set(pos.x, pos.y, pos.z);
            wheel.rotation.z = pos.rotZ;
            trainGroup.add(wheel);
        });

        // Train front marker (red light)
        const lightGeometry = new THREE.SphereGeometry(0.15);
        const lightMaterial = new THREE.MeshBasicMaterial({ color: 0xFF0000 });
        const frontLight = new THREE.Mesh(lightGeometry, lightMaterial);
        frontLight.position.set(1.01, 1.0, 0);
        trainGroup.add(frontLight);

        // Train rear marker (red tail light)
        const rearLight = new THREE.Mesh(lightGeometry, lightMaterial);
        rearLight.position.set(-1.01, 1.0, 0);
        trainGroup.add(rearLight);

        // Add train to scene
        this.scene.add(trainGroup);
        this.trainMeshes.set(trainId, trainGroup);

        return trainGroup;
    }

    /**
     * Remove a train from the scene
     * @param {string} trainId - Train identifier to remove
     */
    removeTrain(trainId) {
        const mesh = this.trainMeshes.get(trainId);
        if (mesh) {
            this.scene.remove(mesh);
            mesh.traverse(child => {
                if (child.geometry) child.geometry.dispose();
                if (child.material) {
                    if (Array.isArray(child.material)) {
                        child.material.forEach(mat => mat.dispose());
                    } else {
                        child.material.dispose();
                    }
                }
            });
            this.trainMeshes.delete(trainId);
            delete this.trains[trainId];
        }
    }

    /**
     * Update train mesh position and rotation based on train state
     * @param {string} trainId - Train identifier
     * @param {Object} position - Position {x, y, z}
     * @param {number} rotation - Rotation angle in radians
     */
    updateTrain(trainId, position, rotation) {
        const mesh = this.trainMeshes.get(trainId);
        if (mesh) {
            mesh.position.set(position.x, position.y, position.z);
            mesh.rotation.y = rotation;
        }
    }

    /**
     * Update all train meshes based on train controller progress
     * @param {number} deltaTime - Time delta in seconds
     * @param {number} speed - Train speed
     */
    updateAll(deltaTime, speed) {
        const progress = this.trainController ? this.trainController.progress : 0;
        const currentPos = this.trainController ? this.trainController.currentPosition : { x: 0, y: 0, z: 0 };
        
        // Calculate orientation based on direction
        let rotation = 0;
        if (this.trainController && this.trainController.trackPath.length >= 2) {
            const totalLength = this.trainController.totalPathLength;
            const targetDistance = progress * totalLength;
            
            let distance = 0;
            for (let i = 0; i < this.trainController.trackPath.length - 1; i++) {
                const p1 = this.trainController.trackPath[i];
                const p2 = this.trainController.trackPath[i + 1];
                const dist = this._distanceBetween(p1, p2);
                
                if (targetDistance >= distance && targetDistance <= distance + dist) {
                    const segmentProgress = (targetDistance - distance) / dist;
                    const x1 = p1.x, y1 = p1.y;
                    const x2 = p2.x, y2 = p2.y;
                    rotation = Math.atan2(y2 - y1, x2 - x1);
                    break;
                }
                distance += dist;
            }
        }

        // Update mesh positions
        this.trainMeshes.forEach((mesh, trainId) => {
            this.updateTrain(trainId, currentPos, rotation);
        });
    }

    /**
     * Calculate distance between two points
     * @param {Object} p1 - First point {x, y, z}
     * @param {Object} p2 - Second point {x, y, z}
     * @returns {number} Distance
     * @private
     */
    _distanceBetween(p1, p2) {
        const dx = p2.x - p1.x;
        const dy = p2.y - p1.y;
        const dz = p2.z - p1.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }

    /**
     * Set train controller reference
     * @param {TrainController} controller - TrainController instance
     */
    setController(controller) {
        this.trainController = controller;
    }

    /**
     * Get all train meshes
     * @returns {Map} Map of trainId to mesh
     */
    getTrainMeshes() {
        return this.trainMeshes;
    }

    /**
     * Dispose resources
     */
    dispose() {
        this.trainMeshes.forEach(mesh => {
            mesh.traverse(child => {
                if (child.geometry) child.geometry.dispose();
                if (child.material) {
                    if (Array.isArray(child.material)) {
                        child.material.forEach(mat => mat.dispose());
                    } else {
                        child.material.dispose();
                    }
                }
            });
        });
        this.trainMeshes.clear();
    }
}

// Export for browser global scope
if (typeof window !== 'undefined') {
    window.TrainRenderer = TrainRenderer;
}
