/**
 * Train Module
 * Handles train model and controller for driving along tracks
 * Works in browser environment with Three.js global
 * @module train
 */

/**
 * Train class - represents a train that can move along tracks
 */
class Train {
    /**
     * Create a train instance
     * @param {Object} options - Train configuration options
     * @param {string} options.id - Unique identifier for the train
     * @param {number} options.speed - Initial speed
     * @param {number} options.maxSpeed - Maximum speed
     * @param {number} options.direction - Direction (1 or -1)
     * @param {number} options.length - Train length for display
     * @param {string} options.color - Train body color
     */
    constructor(options = {}) {
        this.id = options.id || `train-${Date.now()}`;
        this.speed = options.speed || 0;
        this.maxSpeed = options.maxSpeed || 5;
        this.acceleration = options.acceleration || 0.5;
        this.brakeDeceleration = options.brakeDeceleration || 0.3;
        this.direction = options.direction || 1;
        this.length = options.length || 3;
        
        this.isMoving = false;
        this.threeMesh = null;
        this.color = options.color || '#FF6B00';
    }

    /**
     * Accelerate the train
     */
    accelerate() {
        this.speed = Math.min(this.speed + this.acceleration * this.direction, this.maxSpeed * this.direction);
        this.isMoving = this.speed !== 0;
    }

    /**
     * Brake the train
     */
    brake() {
        const brakeAmount = this.brakeDeceleration * this.direction;
        const newSpeed = this.speed - brakeAmount;
        
        if (Math.abs(newSpeed) < Math.abs(this.brakeDeceleration)) {
            this.speed = 0;
            this.isMoving = false;
        } else {
            this.speed = newSpeed;
        }
    }

    /**
     * Reverse direction
     */
    reverse() {
        this.direction *= -1;
    }

    /**
     * Stop the train
     */
    stop() {
        this.speed = 0;
        this.isMoving = false;
    }

    /**
     * Update train physics based on speed
     * @param {number} deltaTime - Time delta in seconds
     */
    update(deltaTime) {
        if (this.isMoving) {
            this.speed = this.speed > 0 ? Math.max(this.speed - 0.1 * this.direction, 0) : Math.min(this.speed + 0.1 * this.direction, 0);
            this.isMoving = this.speed !== 0;
        }
    }

    /**
     * Get train velocity
     * @returns {number} Train velocity
     */
    getVelocity() {
        return this.speed;
    }
}

/**
 * TrainController class - manages train position along track path
 */
class TrainController {
    /**
     * Create a train controller
     * @param {Array} trackPath - Array of {x, y, z} points defining the track path
     */
    constructor(trackPath = []) {
        this.trackPath = trackPath || [];
        this.progress = 0; // 0.0 to 1.0 along the path
        this.distance = 0; // Distance traveled along path
        this.currentPosition = { x: 0, y: 0, z: 0 };
        this.totalPathLength = this._calculatePathLength();
    }

    /**
     * Calculate total path length
     * @returns {number} Total length of the track path
     * @private
     */
    _calculatePathLength() {
        if (this.trackPath.length < 2) return 0;
        
        let totalLength = 0;
        for (let i = 1; i < this.trackPath.length; i++) {
            const p1 = this.trackPath[i - 1];
            const p2 = this.trackPath[i];
            const dx = p2.x - p1.x;
            const dy = p2.y - p1.y;
            const dz = p2.z - p1.z;
            totalLength += Math.sqrt(dx * dx + dy * dy + dz * dz);
        }
        return totalLength;
    }

    /**
     * Set distance traveled along the path
     * @param {number} distance - Distance to set
     */
    setDistance(distance) {
        this.distance = Math.max(0, Math.min(distance, this.totalPathLength));
        this.progress = this.totalPathLength > 0 ? this.distance / this.totalPathLength : 0;
        this.updateCurrentPosition();
    }

    /**
     * Get current position on the track path
     * @returns {Object} Position {x, y, z}
     */
    getCurrentPosition() {
        return this.currentPosition;
    }

    /**
     * Update current position based on progress
     * @private
     */
    updateCurrentPosition() {
        if (this.trackPath.length < 2) {
            this.currentPosition = this.trackPath[0] || { x: 0, y: 0, z: 0 };
            return;
        }

        const totalLength = this.totalPathLength;
        const targetDistance = this.progress * totalLength;
        
        let distance = 0;
        for (let i = 0; i < this.trackPath.length - 1; i++) {
            const p1 = this.trackPath[i];
            const p2 = this.trackPath[i + 1];
            const p1Length = distance;
            const p2Length = distance + this._distanceBetween(p1, p2);
            
            if (targetDistance >= p1Length && targetDistance <= p2Length) {
                const segmentProgress = (targetDistance - p1Length) / (p2Length - p1Length);
                this.currentPosition = {
                    x: p1.x + (p2.x - p1.x) * segmentProgress,
                    y: p1.y + (p2.y - p1.y) * segmentProgress,
                    z: p1.z + (p2.z - p1.z) * segmentProgress
                };
                return;
            }
            distance += this._distanceBetween(p1, p2);
        }
        
        // End of path
        this.currentPosition = this.trackPath[this.trackPath.length - 1];
    }

    /**
     * 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);
    }

    /**
     * Update controller based on train speed
     * @param {number} deltaTime - Time delta in seconds
     * @param {number} speed - Train speed
     */
    update(deltaTime, speed) {
        const distanceDelta = speed * deltaTime;
        this.setDistance(this.distance + distanceDelta);
    }

    /**
     * Set track path
     * @param {Array} path - Array of {x, y, z} points
     */
    setTrackPath(path) {
        this.trackPath = path;
        this.totalPathLength = this._calculatePathLength();
        this.progress = 0;
        this.distance = 0;
        this.updateCurrentPosition();
    }
}

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

// Export for Node.js/Jest
module.exports = { Train, TrainController };

