import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
import { visualizerLibrary } from '../visualizers/index.js';

// --- Shaders for Screen Effects ---
const screenVertexShader = `
varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

const screenFragmentShader = `
uniform sampler2D videoTexture;
uniform float u_effect_type;
uniform float u_effect_strength;
uniform float u_time;
uniform float u_opacity;
uniform vec2 u_resolution;
varying vec2 vUv;

float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
}

void main() {
    // LED Grid Setup
    float ledCountX = u_resolution.x;
    float ledCountY = u_resolution.y;
    
    vec2 gridUV = vec2(vUv.x * ledCountX, vUv.y * ledCountY);
    
    // Anti-aliasing: Calculate grid density
    vec2 gridDeriv = fwidth(gridUV);
    float gridDensity = max(gridDeriv.x, gridDeriv.y);
    float blurFactor = smoothstep(0.3, 0.8, gridDensity);

    vec2 cell = fract(gridUV);
    vec2 pixelatedUV = (floor(gridUV) + 0.5) / vec2(ledCountX, ledCountY);
    vec2 sampleUV = mix(pixelatedUV, vUv, blurFactor);
    
    vec4 color = texture2D(videoTexture, sampleUV);
    
    // Effect 1: Static/Noise (Power On/Off)
    if (u_effect_type > 0.0) {
        float noise = random(pixelatedUV + u_time);
        vec3 noiseColor = vec3(noise);
        color.rgb = mix(color.rgb, noiseColor, u_effect_strength);
    }
    
    float dist = distance(cell, vec2(0.5));
    float edgeSoftness = clamp(gridDensity * 1.5, 0.0, 0.5);
    float mask = 1.0 - smoothstep(0.35 - edgeSoftness, 0.45 + edgeSoftness, dist);
    mask = mix(mask, 1.0, blurFactor);

    gl_FragColor = vec4(color.rgb, mask * u_opacity);
}
`;

let projectionScreenInstance = null;

export class ProjectionScreen extends SceneFeature {
    constructor() {
        super();
        projectionScreenInstance = this;
        this.isVisualizerActive = false;
        this.isSlideshowActive = false;
        this.slideshowTimer = 0;
        this.currentImageIndex = 0;
        this.slideshowDuration = 5.0; // seconds per image
        this.imageTextures = {};
        this.screens = [];
        this.blackoutLerp = 0;
        this.colorBuffer = new Float32Array(16 * 3);
        
        this.standbyState = {
            active: false,
            scrollX: 0,
            text: '',
            header: '',
            subtext: ''
        };
        this.standbyCanvas = null;
        this.standbyCtx = null;
        this.standbyTexture = null;
        
        sceneFeatureManager.register(this);
    }

    init() {
        state.imageUrls = state.imageUrls || [];
        state.imageFilenames = state.imageFilenames || [];
        state.isSlideshowLoaded = state.isSlideshowLoaded || false;

        // --- Initialize State ---
        state.tvScreenPowered = false;
        state.screenEffect = {
            active: false,
            type: 0,
            startTime: 0,
            duration: 1000,
            easing: (t) => t,
            onComplete: null
        };
        state.originalScreenIntensity = 2.0;
        state.screenOpacity = 0.5;
        state.screenOpacityBlackout = 1.0;

        // Ensure video element exists
        if (!state.videoElement) {
            state.videoElement = document.createElement('video');
            state.videoElement.crossOrigin = 'anonymous';
            state.videoElement.playsInline = true;
            state.videoElement.style.display = 'none';
            document.body.appendChild(state.videoElement);
        }

        // --- Create Screen Mesh ---
        // 16:9 Aspect Ratio, large size
        const width = 10;
        const height = width * (9 / 16); 
        const geometry = new THREE.PlaneGeometry(width, height, 32, 32);
        
        // Initial black material
        const material = new THREE.MeshBasicMaterial({ color: 0x000000 });
        
        this.mesh = new THREE.Mesh(geometry, material);
        // High enough to be seen
        this.mesh.position.set(0, 5.5, -20.5); 
        state.scene.add(this.mesh);
        
        this.screens.push({
            mesh: this.mesh,
            originalPositions: geometry.attributes.position.clone(),
            resolution: new THREE.Vector2(200, 200 * (9 / 16))
        });

        // --- Create Side Screens ---
        const sideWidth = 4.5;
        const sideHeight = sideWidth * (9 / 16);
        const sideGeometry = new THREE.PlaneGeometry(sideWidth, sideHeight, 16, 16);

        const createSideScreen = (x, y, z, rotY, resX) => {
            const resY = resX * (9 / 16);
            const mesh = new THREE.Mesh(sideGeometry, material); // Share initial material
            mesh.position.set(x, y, z);
            mesh.rotation.y = rotY;
            state.scene.add(mesh);
            this.screens.push({ mesh, originalPositions: sideGeometry.attributes.position.clone(), resolution: new THREE.Vector2(resX, resY) });
        };

        createSideScreen(-11, 4.0, -15.6, 0.4, 100); // Left (Lower resolution)
        createSideScreen(11, 4.0, -15.5, -0.4, 100); // Right (Lower resolution)
        
        state.tvScreen = this.mesh;

        // --- Screen Light ---
        // A light that projects the screen's color/ambiance into the room
        state.screenLight = new THREE.PointLight(0xffffff, 0, 25);
        state.screenLight.position.set(0, 5.5, -18);
        state.screenLight.castShadow = true;
        state.screenLight.shadow.mapSize.width = 512;
        state.screenLight.shadow.mapSize.height = 512;
        state.scene.add(state.screenLight);

        showStandbyScreen();
    }

    setAllVisible(visible) {
        this.screens.forEach((s, index) => {
            if (index === 0) {
                s.mesh.visible = visible;
            } else {
                s.mesh.visible = visible && state.config.sideScreensEnabled;
            }
        });
    }

    applyMaterialToAll(baseMaterial) {
        this.screens.forEach(s => {
            if (baseMaterial instanceof THREE.ShaderMaterial && baseMaterial.uniforms.u_resolution) {
                const newMat = baseMaterial.clone();
                newMat.uniforms = THREE.UniformsUtils.clone(baseMaterial.uniforms);
                newMat.uniforms.u_resolution.value.copy(s.resolution);
                s.mesh.material = newMat;
            } else {
                s.mesh.material = baseMaterial;
            }
        });
    }

    update(deltaTime) {
        updateScreenEffect();
        
        // --- Slideshow Cycling Logic ---
        if (this.isSlideshowActive && state.imageUrls && state.imageUrls.length > 0) {
            this.slideshowTimer += deltaTime;
            if (this.slideshowTimer >= this.slideshowDuration) {
                this.slideshowTimer = 0;
                if (state.imageUrls.length > 1) {
                    this.currentImageIndex = (this.currentImageIndex + 1) % state.imageUrls.length;
                    this.updateSlideshowTexture();
                }
            }
        }

        // --- Blackout Transition Logic ---
        const targetBlackout = state.blackoutMode ? 1.0 : 0.0;
        const enterBlackoutSpeed = 0.5; // slow light up
        const exitBlackoutSpeed = 5.0;  // quick fade out
        
        if (this.blackoutLerp < targetBlackout) {
            this.blackoutLerp = Math.min(targetBlackout, this.blackoutLerp + deltaTime * enterBlackoutSpeed);
        } else if (this.blackoutLerp > targetBlackout) {
            this.blackoutLerp = Math.max(targetBlackout, this.blackoutLerp - deltaTime * exitBlackoutSpeed);
        }

        const normalOpacity = state.screenOpacity !== undefined ? state.screenOpacity : 0.7;
        const blackoutOpacity = state.screenOpacityBlackout !== undefined ? state.screenOpacityBlackout : 0.2;
        const currentOpacity = THREE.MathUtils.lerp(normalOpacity, blackoutOpacity, this.blackoutLerp);
        const dimFactor = 1.0 - this.blackoutLerp;

        if (this.standbyState.active) {
            this.renderStandbyFrame();
        }

        // Wobble Logic
        const time = state.clock.getElapsedTime();
        const waveSpeed = 0.5;
        const waveFrequency = 1.2;
        const waveAmplitude = 0.3;

        this.screens.forEach(screenObj => {
            const positions = screenObj.mesh.geometry.attributes.position;
            const originalPositions = screenObj.originalPositions;

            for (let i = 0; i < positions.count; i++) {
                const originalX = originalPositions.getX(i);
                const originalZ = originalPositions.getZ(i);
                const zOffset = Math.sin(originalX * waveFrequency + time * waveSpeed) * waveAmplitude;
                positions.setZ(i, originalZ + zOffset);
            }
            positions.needsUpdate = true;
            screenObj.mesh.geometry.computeVertexNormals();
        });

        if (this.isVisualizerActive) {
            const beat = (state.music && state.music.beatIntensity) ? state.music.beatIntensity : 0.0;
            state.screenLight.intensity = state.originalScreenIntensity * (0.5 + beat * 0.5);
            state.screenLight.intensity *= dimFactor;

            // Update color buffer
            const colors = state.config.lightBarColors;
            const colorCount = colors ? Math.min(colors.length, 16) : 0;
            
            if (colorCount > 0) {
                for (let i = 0; i < colorCount; i++) {
                    const c = new THREE.Color(colors[i]);
                    this.colorBuffer[i * 3] = c.r;
                    this.colorBuffer[i * 3 + 1] = c.g;
                    this.colorBuffer[i * 3 + 2] = c.b;
                }
            }

            this.screens.forEach(s => {
                if (s.mesh.material && s.mesh.material.uniforms && s.mesh.material.uniforms.u_time) {
                    s.mesh.material.uniforms.u_time.value = state.clock.getElapsedTime();
                    s.mesh.material.uniforms.u_beat.value = beat;
                    if (s.mesh.material.uniforms.u_colorCount) {
                        s.mesh.material.uniforms.u_colorCount.value = colorCount;
                    }
                    if (s.mesh.material.uniforms.u_seed) {
                        s.mesh.material.uniforms.u_seed.value = state.config.visualizerSeed || 0;
                    }
                }
            });
        } else if (this.isSlideshowActive) {
            state.screenLight.intensity = state.originalScreenIntensity * dimFactor;
        } else {
            // Video Mode
            state.screenLight.intensity = state.originalScreenIntensity * dimFactor;
        }

        // Apply Opacity to all screens (Visualizer or Video)
        this.screens.forEach(s => {
            const material = s.mesh.material;
            if (material && material.uniforms) {
                if (material.uniforms.u_opacity) {
                    material.uniforms.u_opacity.value = currentOpacity;
                }
                // Ensure time is updated for video shader effects even if visualizer is off
                if (!this.isVisualizerActive && material.uniforms.u_time) {
                    material.uniforms.u_time.value = state.clock.getElapsedTime();
                }
            }
        });
    }

    onPartyStart() {
        // Hide load button during playback
        if (state.loadTapeButton) state.loadTapeButton.classList.add('hidden');
        
        // Priority: Video > Slideshow > Visualizer
        if (!state.isVideoLoaded) {
            if (state.isSlideshowLoaded) {
                this.activateSlideshow();
            } else {
                this.activateVisualizer();
            }
        }
    }
    
    onPartyEnd() {
        // Show load button if no video is loaded
        if (state.loadTapeButton && !state.isVideoLoaded) state.loadTapeButton.classList.remove('hidden');
        
        if (this.isVisualizerActive) {
            this.deactivateVisualizer();
        }
        if (this.isSlideshowActive) {
            this.deactivateSlideshow();
        }
    }

    activateVisualizer() {
        this.isVisualizerActive = true;
        state.tvScreenPowered = true;

        const visualizerType = state.config.visualizerType || 'Classic Wave';
        const visualizer = visualizerLibrary.find(v => v.getName() === visualizerType) || visualizerLibrary[0];
        const fragShader = visualizer.getFragmentShader();

        const material = new THREE.ShaderMaterial({
            uniforms: {
                u_time: { value: 0.0 },
                u_beat: { value: 0.0 },
                u_opacity: { value: state.screenOpacity },
                u_resolution: { value: new THREE.Vector2(1, 1) }, // Placeholder, set in applyMaterialToAll
                u_colors: { value: this.colorBuffer },
                u_colorCount: { value: 0 },
                u_seed: { value: state.config.visualizerSeed || 0 }
            },
            vertexShader: screenVertexShader,
            fragmentShader: fragShader,
            side: THREE.DoubleSide,
            transparent: true,
            derivatives: true
        });
        
        this.applyMaterialToAll(material);
        this.setAllVisible(true);
        state.screenLight.intensity = state.originalScreenIntensity;
    }
    
    deactivateVisualizer() {
        this.isVisualizerActive = false;
        turnTvScreenOff();
    }

    activateSlideshow() {
        this.isSlideshowActive = true;
        this.isVisualizerActive = false;
        this.currentImageIndex = 0;
        this.slideshowTimer = 0;
        state.tvScreenPowered = true;

        const material = new THREE.ShaderMaterial({
            uniforms: {
                videoTexture: { value: null },
                u_effect_type: { value: 0.0 },
                u_effect_strength: { value: 0.0 },
                u_time: { value: 0.0 },
                u_opacity: { value: state.screenOpacity !== undefined ? state.screenOpacity : 0.7 },
                u_resolution: { value: new THREE.Vector2(1, 1) }
            },
            vertexShader: screenVertexShader,
            fragmentShader: screenFragmentShader,
            side: THREE.DoubleSide,
            transparent: true,
            derivatives: true
        });

        this.applyMaterialToAll(material);
        this.updateSlideshowTexture();
        this.setAllVisible(true);
        setScreenEffect(1); // Power on effect
    }

    deactivateSlideshow() {
        this.isSlideshowActive = false;
        turnTvScreenOff();
    }

    updateSlideshowTexture() {
        if (!state.imageUrls || state.imageUrls.length === 0) return;
        const url = state.imageUrls[this.currentImageIndex];
        if (!this.imageTextures[url]) {
            this.imageTextures[url] = new THREE.TextureLoader().load(url);
            this.imageTextures[url].colorSpace = THREE.SRGBColorSpace;
        }
        this.screens.forEach(s => {
            if (s.mesh.material && s.mesh.material.uniforms && s.mesh.material.uniforms.videoTexture) {
                s.mesh.material.uniforms.videoTexture.value = this.imageTextures[url];
            }
        });
    }

    activateStandby() {
        this.isVisualizerActive = false;
        
        if (!this.standbyCanvas) {
            this.standbyCanvas = document.createElement('canvas');
            this.standbyCanvas.width = 1024;
            this.standbyCanvas.height = 576;
            this.standbyCtx = this.standbyCanvas.getContext('2d');
            this.standbyTexture = new THREE.CanvasTexture(this.standbyCanvas);
            this.standbyTexture.minFilter = THREE.LinearFilter;
            this.standbyTexture.magFilter = THREE.LinearFilter;
        }

        const songTitle = (state.music && state.music.songTitle) ? state.music.songTitle.toUpperCase() : "";
        
        this.standbyState = {
            active: true,
            scrollX: 0,
            text: songTitle,
            header: "PLEASE STAND BY",
            subtext: "WAITING FOR PARTY START"
        };

        const material = new THREE.MeshBasicMaterial({ 
            map: this.standbyTexture,
            side: THREE.DoubleSide
        });
        
        this.applyMaterialToAll(material);
        this.setAllVisible(true);
        state.screenLight.intensity = 0.5;
        
        // Measure text to set initial scroll
        this.standbyCtx.font = 'bold 80px monospace';
        const textWidth = this.standbyCtx.measureText(this.standbyState.text).width;
        if (textWidth > this.standbyCanvas.width * 0.9) {
             this.standbyState.scrollX = this.standbyCanvas.width;
        }
        
        this.renderStandbyFrame();
    }

    renderStandbyFrame() {
        if (!this.standbyState.active || !this.standbyCtx) return;
        
        const ctx = this.standbyCtx;
        const width = this.standbyCanvas.width;
        const height = this.standbyCanvas.height;
        
        // Draw Background
        const colors = ['#ffffff', '#ffff00', '#00ffff', '#00ff00', '#ff00ff', '#ff0000', '#0000ff'];
        const barWidth = width / colors.length;
        colors.forEach((color, i) => {
            ctx.fillStyle = color;
            ctx.fillRect(i * barWidth, 0, barWidth, height);
        });
        ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
        ctx.fillRect(0, 0, width, height);

        // Header
        ctx.fillStyle = '#ffffff';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.font = 'bold 40px monospace';
        ctx.fillText(this.standbyState.header, width / 2, height * 0.2);

        // Song Title
        const text = this.standbyState.text;
        ctx.font = 'bold 80px monospace';
        const textMetrics = ctx.measureText(text);
        const textWidth = textMetrics.width;
        const centerY = height * 0.5;

        if (textWidth > width * 0.9) {
            // Scroll
            this.standbyState.scrollX -= 3; // Speed
            if (this.standbyState.scrollX < -textWidth) {
                this.standbyState.scrollX = width;
            }
            ctx.textAlign = 'left';
            ctx.fillText(text, this.standbyState.scrollX, centerY);
        } else {
            // Center
            ctx.textAlign = 'center';
            ctx.fillText(text, width / 2, centerY);
        }

        // Subtext
        ctx.font = '30px monospace';
        ctx.fillStyle = '#cccccc';
        ctx.textAlign = 'center';
        ctx.fillText(this.standbyState.subtext, width / 2, height * 0.8);

        if (this.standbyTexture) this.standbyTexture.needsUpdate = true;
    }
}

// --- Exported Control Functions ---

export function showStandbyScreen() {
    if (projectionScreenInstance) {
        projectionScreenInstance.isVisualizerActive = false;
        projectionScreenInstance.standbyState.active = false;
    }

    if (state.posterImage) {
        const texture = new THREE.TextureLoader().load(state.posterImage);
        const material = new THREE.MeshBasicMaterial({ 
            map: texture,
            side: THREE.DoubleSide
        });
        if (projectionScreenInstance) {
            projectionScreenInstance.applyMaterialToAll(material);
            projectionScreenInstance.setAllVisible(true);
        }
        state.screenLight.intensity = 0.5;
        return;
    }
    
    if (projectionScreenInstance) {
        projectionScreenInstance.activateStandby();
    }
}

export function turnTvScreenOn() {
    if (projectionScreenInstance) {
        projectionScreenInstance.isVisualizerActive = false;
        projectionScreenInstance.isSlideshowActive = false;
    }

    // Switch to ShaderMaterial for video playback
    const material = new THREE.ShaderMaterial({
        uniforms: {
            videoTexture: { value: state.videoTexture },
            u_effect_type: { value: 0.0 },
            u_effect_strength: { value: 0.0 },
            u_time: { value: 0.0 },
            u_opacity: { value: state.screenOpacity !== undefined ? state.screenOpacity : 0.7 },
            u_resolution: { value: new THREE.Vector2(1, 1) } // Placeholder
        },
        vertexShader: screenVertexShader,
        fragmentShader: screenFragmentShader,
        side: THREE.DoubleSide,
        transparent: true,
        derivatives: true
    });

    if (projectionScreenInstance) projectionScreenInstance.applyMaterialToAll(material);
    if (projectionScreenInstance) projectionScreenInstance.setAllVisible(true);

    if (!state.tvScreenPowered) {
        state.tvScreenPowered = true;
        setScreenEffect(1); // Trigger power on static effect
    }
}

export function turnTvScreenOff() {
    if (state.tvScreenPowered) {
        state.tvScreenPowered = false;
        setScreenEffect(2, () => {
            // Revert to black material or hide
            const blackMat = new THREE.MeshBasicMaterial({ color: 0x000000 });
            if (projectionScreenInstance) projectionScreenInstance.applyMaterialToAll(blackMat);
            state.screenLight.intensity = 0.0;
        });
    }
}

export function setScreenEffect(effectType, onComplete) {
    const material = state.tvScreen.material;
    if (!material.uniforms) return;
    
    state.screenEffect.active = true;
    state.screenEffect.type = effectType;
    state.screenEffect.startTime = state.clock.getElapsedTime() * 1000;
    state.screenEffect.onComplete = onComplete;
}

export function updateScreenEffect() {
    if (!state.screenEffect || !state.screenEffect.active) return;
    
    const elapsedTime = (state.clock.getElapsedTime() * 1000) - state.screenEffect.startTime;
    const progress = Math.min(elapsedTime / state.screenEffect.duration, 1.0);
    
    let strength = progress;
    if (state.screenEffect.type === 1) {
        strength = 1.0 - progress;
    }

    if (projectionScreenInstance) {
        projectionScreenInstance.screens.forEach(s => {
            const material = s.mesh.material;
            if (!material || !material.uniforms) return;

            if (material.uniforms.u_time) {
                material.uniforms.u_time.value = state.clock.getElapsedTime();
            }
            if (material.uniforms.u_effect_type) {
                material.uniforms.u_effect_type.value = state.screenEffect.type;
            }
            if (material.uniforms.u_effect_strength) {
                material.uniforms.u_effect_strength.value = strength;
            }

            if (progress >= 1.0) {
                if (material.uniforms.u_effect_type) material.uniforms.u_effect_type.value = 0.0;
                if (material.uniforms.u_effect_strength) material.uniforms.u_effect_strength.value = 0.0;
            }
        });
    }

    if (progress >= 1.0) {
        state.screenEffect.active = false;
        if (state.screenEffect.onComplete) {
            state.screenEffect.onComplete();
        }
    }
}

new ProjectionScreen();
