/**
 * Game Module
 * Main game logic, UI interactions, and initialization
 * Works in browser environment
 * @module game
 */

/**
 * Game class - main game controller
 */
class Game {
    constructor() {
        // Initialize Three.js renderer
        this.renderer = new Renderer();
        
        // Initialize world (track placement)
        this.world = new World(this.renderer);
        
        // Initialize train system
        this.trainController = new TrainController([]);
        this.trainRenderer = new TrainRenderer(this.renderer.scene, { color: '#FF6B00' });
        this.trainRenderer.setController(this.trainController);
        this.createTrain();
        
        // Initialize controls
        this.controls = new OrbitControls(this.renderer.camera, this.renderer.domElement);
        this.controls.enableDamping = true;
        this.controls.dampingFactor = 0.05;
        this.controls.maxPolarAngle = Math.PI / 2.2;
        this.controls.minDistance = 5;
        this.controls.maxDistance = 100;

        // Input state
        this.selectedTrackType = null;
        this.keysPressed = {};
        
        // Stats tracking
        this.stats = {
            totalTracks: 0,
            trackCounts: {},
            lastUpdate: Date.now()
        };
        
        // Initialize UI elements
        this.initializeUI();
        
        // Bind events
        this.bindEvents();
        
        // Start game loop
        this.lastTime = Date.now();
        this.animate();
        
        console.log('🚂 Railtrack Pro initialized!');
    }

    /**
     * Create a train for the world
     */
    createTrain() {
        const train = new Train({
            id: 'main-train',
            speed: 0,
            maxSpeed: 5,
            direction: 1,
            color: '#FF6B00'
        });
        
        this.train = train;
        this.trainMesh = this.trainRenderer.createTrainMesh('main-train');
        this.train.threeMesh = this.trainMesh;
    }

    /**
     * Initialize UI elements
     */
    initializeUI() {
        // Stats panel
        this.statsPanel = document.getElementById('statsPanel');
        this.trackStatsList = document.getElementById('trackStatsList');
        
        // Train controls
        this.trainControls = {
            accelerateBtn: document.getElementById('accelerateTrain'),
            brakeBtn: document.getElementById('brakeTrain'),
            reverseBtn: document.getElementById('reverseTrain'),
            stopBtn: document.getElementById('stopTrain'),
            statusDiv: document.getElementById('trainStatus')
        };

        // Track type buttons
        this.trackButtons = document.querySelectorAll('[data-track-type]');
        
        // Info panel
        this.infoPanel = document.getElementById('infoPanel');
        
        // Update stats display
        this.updateStatsDisplay();
        this.updateTrainStatus();
    }

    /**
     * Bind event listeners
     */
    bindEvents() {
        // Track type selection
        this.trackButtons.forEach(btn => {
            btn.addEventListener('click', () => {
                const trackType = btn.dataset.trackType;
                this.selectTrackType(trackType, btn);
            });
        });

        // Train control buttons
        this.trainControls.accelerateBtn.addEventListener('click', () => this.accelerate());
        this.trainControls.brakeBtn.addEventListener('click', () => this.brake());
        this.trainControls.reverseBtn.addEventListener('click', () => this.reverse());
        this.trainControls.stopBtn.addEventListener('click', () => this.stop());

        // Keyboard controls
        document.addEventListener('keydown', (e) => {
            this.keysPressed[e.code] = true;
            
            // Train controls via keyboard
            switch(e.code) {
                case 'KeyW':
                case 'ArrowUp':
                    this.accelerate();
                    break;
                case 'KeyS':
                case 'ArrowDown':
                    this.brake();
                    break;
                case 'KeyR':
                case 'KeyD':
                    this.reverse();
                    break;
                case 'Space':
                    this.stop();
                    break;
            }
        });

        document.addEventListener('keyup', (e) => {
            this.keysPressed[e.code] = false;
        });

        // Mouse click for track placement/removal
        this.renderer.domElement.addEventListener('click', (event) => {
            this.handleMouseClick(event);
        });

        // Handle window resize
        window.addEventListener('resize', () => {
            this.onWindowResize();
        });
    }

    /**
     * Select track type for placement
     * @param {string} trackType - Type of track to select
     * @param {HTMLElement} button - Button element clicked
     */
    selectTrackType(trackType, button) {
        this.selectedTrackType = trackType;
        
        // Update button styles
        this.trackButtons.forEach(btn => {
            btn.classList.remove('selected');
        });
        button.classList.add('selected');

        console.log(`Selected track type: ${trackType}`);
    }

    /**
     * Handle mouse click on canvas
     * @param {MouseEvent} event - Mouse click event
     */
    handleMouseClick(event) {
        // Check if clicking on train first
        const clickedMesh = event.target;
        if (clickedMesh === this.trainMesh || clickedMesh.parent === this.trainMesh) {
            // Train selected - show train info
            this.showTrainInfo();
            return;
        }

        // If no track type selected, show instruction
        if (!this.selectedTrackType) {
            this.showInfo('Select a track type to place! Click track buttons in the toolbar.');
            return;
        }

        // Check if clicking on existing track (to remove)
        const clickedTrack = this.checkClickedTrack(event);
        if (clickedTrack) {
            this.world.removeTrack(clickedTrack);
            this.updateStatsDisplay();
            this.showInfo(`Removed track at ${clickedTrack.position.x}, ${clickedTrack.position.y}`);
            return;
        }

        // Check if clicking on canvas
        if (this.world.isPlacementValid(event)) {
            this.placeTrack(event);
        } else {
            this.showInfo('Click on the grid to place tracks. Click existing tracks to remove them.');
        }
    }

    /**
     * Check if mouse clicked on an existing track
     * @param {MouseEvent} event - Mouse click event
     * @returns {THREE.Object3D|null} Clicked track object or null
     */
    checkClickedTrack(event) {
        const mouse = new THREE.Vector2();
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

        const raycaster = new THREE.Raycaster();
        raycaster.setFromCamera(mouse, this.renderer.camera);

        const intersects = raycaster.intersectObjects(this.world.allTracks, true);
        if (intersects.length > 0) {
            // Find parent group (the actual track piece)
            let parent = intersects[0].object;
            while (parent && !parent.userData.type) {
                parent = parent.parent;
            }
            if (parent && parent.userData.type) {
                return parent;
            }
        }
        return null;
    }

    /**
     * Place a track at the clicked location
     * @param {MouseEvent} event - Mouse click event
     */
    placeTrack(event) {
        const gridPos = this.world.getGridPosition(event);
        if (gridPos) {
            const track = this.world.addTrack(
                this.selectedTrackType,
                gridPos.x,
                gridPos.y,
                this.renderer
            );
            if (track) {
                this.stats.totalTracks++;
                this.stats.trackCounts[this.selectedTrackType] = (this.stats.trackCounts[this.selectedTrackType] || 0) + 1;
                this.updateStatsDisplay();
                this.showInfo(`Placed ${this.selectedTrackType} at ${gridPos.x}, ${gridPos.y}`);
            } else {
                this.showInfo(`Cannot place ${this.selectedTrackType} at that location.`, 'warning');
            }
        }
    }

    /**
     * Accelerate the train (W/Up arrow)
     */
    accelerate() {
        if (this.train) {
            this.train.accelerate();
            this.updateTrainStatus();
            console.log('⚡ Train accelerating - Speed:', this.train.speed);
        }
    }

    /**
     * Brake the train (S/Down arrow)
     */
    brake() {
        if (this.train) {
            this.train.brake();
            this.updateTrainStatus();
            console.log('🛑 Train braking - Speed:', this.train.speed);
        }
    }

    /**
     * Reverse train direction (R/D)
     */
    reverse() {
        if (this.train) {
            this.train.reverse();
            this.updateTrainStatus();
            console.log('🔄 Train reversed - Direction:', this.train.direction);
        }
    }

    /**
     * Stop the train (Space)
     */
    stop() {
        if (this.train) {
            this.train.stop();
            this.updateTrainStatus();
            console.log('🛑 Train stopped');
        }
    }

    /**
     * Show train information in info panel
     */
    showTrainInfo() {
        if (!this.train) return;

        this.infoPanel.innerHTML = `
            <h3>🚂 Train Information</h3>
            <p><strong>ID:</strong> ${this.train.id}</p>
            <p><strong>Speed:</strong> <latex>${Math.abs(this.train.speed).toFixed(1)} units/s</latex></p>
            <p><strong>Direction:</strong> ${this.train.direction > 0 ? '➡️ Forward' : '⬅️ Reverse'}</p>
            <p><strong>Status:</strong> ${this.train.isMoving ? '🚂 Moving' : '🛑 Stopped'}</p>
            <p><strong>Keyboard:</strong> W/↑ = Accelerate, S/↓ = Brake, R/D = Reverse, SPACE = Stop</p>
        `;
    }

    /**
     * Show information message in info panel
     * @param {string} message - Message to display
     * @param {string} type - Type (info, warning, error)
     */
    showInfo(message, type = 'info') {
        this.infoPanel.innerHTML = `
            <h3>${type === 'warning' ? '⚠️' : type === 'error' ? '❌' : 'ℹ️'} ${type.toUpperCase()}</h3>
            <p>${message}</p>
        `;
        
        // Clear info after 3 seconds
        setTimeout(() => {
            if (this.infoPanel && this.infoPanel.innerHTML.includes(message)) {
                this.infoPanel.innerHTML = '<p>Click tracks to place/remove. Select a track type to start building!</p>';
            }
        }, 3000);
    }

    /**
     * Update stats display in UI
     */
    updateStatsDisplay() {
        if (!this.statsPanel) return;

        let statsHTML = `
            <h3>📊 Statistics</h3>
            <p><strong>Total Tracks:</strong> ${this.stats.totalTracks}</p>
        `;

        if (this.stats.trackCounts && Object.keys(this.stats.trackCounts).length > 0) {
            statsHTML += `
                <h4>Track Types:</h4>
                <ul id="trackStatsList">
            `;
            for (const [type, count] of Object.entries(this.stats.trackCounts)) {
                statsHTML += `<li>${type}: <strong>${count}</strong></li>`;
            }
            statsHTML += `</ul>`;
        }

        this.statsPanel.innerHTML = statsHTML;
    }

    /**
     * Update train status display in UI
     */
    updateTrainStatus() {
        if (!this.trainControls.statusDiv || !this.train) return;

        const status = this.train.isMoving ? '🚂 Moving' : '🛑 Stopped';
        const direction = this.train.direction > 0 ? '➡️ Forward' : '⬅️ Reverse';
        
        this.trainControls.statusDiv.innerHTML = `
            <p><strong>Status:</strong> ${status}</p>
            <p><strong>Direction:</strong> ${direction}</p>
            <p><strong>Speed:</strong> <latex>${Math.abs(this.train.speed).toFixed(1)} units/s</latex></p>
        `;
    }

    /**
     * Handle window resize
     */
    onWindowResize() {
        this.renderer.onWindowResize();
    }

    /**
     * Update game state based on input
     * @param {number} deltaTime - Time delta in seconds
     */
    update(deltaTime) {
        // Update train controls based on keyboard
        if (this.train) {
            // Keyboard controls with smoothing
            if (this.keysPressed['KeyW'] || this.keysPressed['ArrowUp']) {
                this.accelerate();
            }
            if (this.keysPressed['KeyS'] || this.keysPressed['ArrowDown']) {
                this.brake();
            }
            if (this.keysPressed['KeyR'] || this.keysPressed['KeyD']) {
                this.reverse();
            }
            
            // Update train physics
            this.train.update(deltaTime);
            this.updateTrainStatus();
            
            // Update train controller if we have a track path
            if (this.trainController.trackPath.length > 0) {
                this.trainController.update(deltaTime, this.train.speed);
                
                // Get current position and update train mesh
                const currentPosition = this.trainController.getCurrentPosition();
                const rotation = this.getTrainRotation();
                this.trainRenderer.updateTrain('main-train', currentPosition, rotation);
            }
        }
        
        // Update controller
        this.controls.update();
    }

    /**
     * Calculate train rotation based on track direction
     * @returns {number} Rotation angle in radians
     */
    getTrainRotation() {
        let rotation = 0;
        
        if (this.trainController && this.trainController.trackPath.length >= 2) {
            const totalLength = this.trainController.totalPathLength;
            const targetDistance = this.trainController.distance;
            
            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 x1 = p1.x, y1 = p1.y;
                    const x2 = p2.x, y2 = p2.y;
                    rotation = Math.atan2(y2 - y1, x2 - x1);
                    // Adjust for train direction
                    if (this.train && this.train.direction < 0) {
                        rotation += Math.PI;
                    }
                    break;
                }
                distance += dist;
            }
        }
        
        return 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);
    }

    /**
     * Animation loop - main game loop
     */
    animate() {
        requestAnimationFrame(() => this.animate());
        
        const now = Date.now();
        const deltaTime = (now - this.lastTime) / 1000;
        this.lastTime = now;

        this.update(deltaTime);
        this.renderer.render();
    }

    /**
     * Set track path for train
     * @param {Array} path - Array of {x, y, z} points
     */
    setTrainPath(path) {
        this.trainController.setTrackPath(path);
    }

    /**
     * Dispose game resources
     */
    dispose() {
        this.trainRenderer.dispose();
        this.world.dispose();
        this.renderer.dispose();
        window.removeEventListener('resize', this.onWindowResize);
    }
}
