import * as THREE from 'three';
import { state } from '../state.js';
import { SceneFeature } from './SceneFeature.js';
import sceneFeatureManager from './SceneFeatureManager.js';
import { applyVibrancyToMaterial } from '../shaders/vibrant-billboard-shader.js';
const musicianTextureUrls = [
    '/textures/musician1.png',
    '/textures/musician2.png',
    '/textures/musician3.png',
    '/textures/musician4.png',
];

// --- Stage dimensions for positioning ---
        const stageHeight = 1.5;
        const stageDepth = 5;
        const length = 40;

        // --- Billboard Properties ---
        const musicianHeight = 2.5;
        const musicianWidth = 2.5;

export class MedievalMusicians extends SceneFeature {
    constructor() {
        super();
        this.musicians = [];
        sceneFeatureManager.register(this);
    }

    async init() {
        const processTexture = (texture) => {
            // 1. Draw texture to canvas to process it
            const image = texture.image;
            const canvas = document.createElement('canvas');
            canvas.width = image.width;
            canvas.height = image.height;
            const context = canvas.getContext('2d');
            context.drawImage(image, 0, 0);

            // 2. Get the key color from the top-left pixel
            const keyPixelData = context.getImageData(0, 0, 1, 1).data;
            const keyColor = { r: keyPixelData[0], g: keyPixelData[1], b: keyPixelData[2] };

            // 3. Process the entire canvas to make background transparent
            const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
            const data = imageData.data;
            const threshold = 25; // Adjust this for more/less color tolerance

            for (let i = 0; i < data.length; i += 4) {
                const r = data[i];
                const g = data[i + 1];
                const b = data[i + 2];
                
                const distance = Math.sqrt(Math.pow(r - keyColor.r, 2) + Math.pow(g - keyColor.g, 2) + Math.pow(b - keyColor.b, 2));

                if (distance < threshold) {
                    data[i + 3] = 0; // Set alpha to 0 (transparent)
                }
            }
            context.putImageData(imageData, 0, 0);

            return new THREE.CanvasTexture(canvas);
        };

        // Load and process all textures, creating a material for each
        const materials = await Promise.all(musicianTextureUrls.map(async (url) => {
            const texture = await state.loader.loadAsync(url);
            const processedTexture = processTexture(texture);
            const material = new THREE.MeshStandardMaterial({
                map: processedTexture,
                side: THREE.DoubleSide,
                alphaTest: 0.5, // Treat pixels with alpha < 0.5 as fully transparent
                roughness: 0.7,
                metalness: 0.1,
            });

            applyVibrancyToMaterial(material, processedTexture);
            return material;
        }));

        const createMusicians = () => {
            // 6. Create and position the musicians
            const geometry = new THREE.PlaneGeometry(musicianWidth, musicianHeight);

            const musicianPositions = [
                new THREE.Vector3(-2, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1),
                new THREE.Vector3(0, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1.5),
                new THREE.Vector3(2.5, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 1.2),
                new THREE.Vector3(1.2, stageHeight + musicianHeight / 2, -length / 2 + stageDepth / 2 - 0.4),
            ];

            musicianPositions.forEach((pos, index) => {
                // Randomly pick one of the created materials
                const material = materials[Math.floor(index % materials.length)];
                const musician = new THREE.Mesh(geometry, material);
                musician.visible = false; // Start invisible
                musician.position.copy(pos);
                state.scene.add(musician);

                // Store musician object with state for animation
                this.musicians.push({
                    mesh: musician,
                    // --- State for complex movement ---
                    currentPlane: 'stage', // 'stage' or 'floor'
                    state: 'WAITING',
                    targetPosition: pos.clone(),
                    waitStartTime: 0,
                    waitTime: 1 + Math.random() * 2, // Wait 1-3 seconds
                    jumpStartPos: null,
                    jumpEndPos: null,
                    jumpProgress: 0,
                    isMirrored: false,
                    canChangePose: true,

                    // --- State for jumping in place ---
                    isJumping: false,
                    jumpStartTime: 0,
                });
            });
        };

        createMusicians();
    }

    update(deltaTime) {
        // Billboard effect: make each musician face the camera
        if (this.musicians.length > 0 && state.partyStarted) {
            const cameraPosition = new THREE.Vector3();
            state.camera.getWorldPosition(cameraPosition);

            const time = state.clock.getElapsedTime();
            const moveSpeed = 2.0;
            const stageArea = { x: 9, z: 3.8, y: stageHeight, centerZ: -length / 2 + stageDepth / 2 };
            const floorArea = { x: 10, z: 3.5, y: 0, centerZ: -length / 2 + stageDepth + 2 };
            const planeEdgeZ = -length / 2 + stageDepth + 0.5;
            const planeJumpChance = 0.1;
            const jumpChance = 0.005;
            const jumpDuration = 0.5;
            const jumpHeight = 1.0;
            const jumpVariance = 1.0;
            const jumpPlaneVariance = 2.0;

            this.musicians.forEach(musicianObj => {
                const { mesh } = musicianObj;

                // We only want to rotate on the Y axis to keep them upright
                mesh.lookAt(cameraPosition.x, mesh.position.y, cameraPosition.z);

                // --- Mirroring on Beat ---
                if (state.music) {
                    if (state.music.beatIntensity > 0.8 && musicianObj.canChangePose) {
                        musicianObj.isMirrored = Math.random() < 0.5;
                        mesh.material.map.repeat.x = musicianObj.isMirrored ? -1 : 1;
                        mesh.material.map.offset.x = musicianObj.isMirrored ? 1 : 0;
                        musicianObj.canChangePose = false;
                    } else if (state.music.beatIntensity < 0.2) {
                        musicianObj.canChangePose = true;
                    }
                }

                // --- Main State Machine ---
                const area = musicianObj.currentPlane === 'stage' ? stageArea : floorArea;
                const otherArea = musicianObj.currentPlane === 'stage' ? floorArea : stageArea;                        
                if (musicianObj.state === 'WAITING') {
                    if (time > musicianObj.waitStartTime + musicianObj.waitTime) {
                        if (Math.random() < planeJumpChance) {
                            // --- Decide to jump to the other plane ---
                            musicianObj.state = 'PREPARING_JUMP';
                            const targetX = (Math.random() - 0.5) * area.x;
                            musicianObj.targetPosition = new THREE.Vector3(targetX, area.y + musicianHeight/2, planeEdgeZ);
                        } else {
                            // --- Decide to move to a new spot on the current plane ---
                            const newTarget = new THREE.Vector3(
                                (Math.random() - 0.5) * area.x,
                                area.y + musicianHeight/2,
                                area.centerZ + (Math.random() - 0.5) * area.z
                            );
                            musicianObj.targetPosition = newTarget;
                            musicianObj.state = 'MOVING';
                        }
                    }
                } else if (musicianObj.state === 'MOVING') {
                    const distance = mesh.position.distanceTo(musicianObj.targetPosition);
                    if (distance > 0.1) {
                        const direction = musicianObj.targetPosition.clone().sub(mesh.position).normalize();
                        mesh.position.add(direction.multiplyScalar(moveSpeed * deltaTime));
                    } else {
                        musicianObj.state = 'WAITING';
                        musicianObj.waitStartTime = time;
                        musicianObj.waitTime = 1 + Math.random() * 2;
                    }
                } else if (musicianObj.state === 'PREPARING_JUMP') {
                    const distance = mesh.position.distanceTo(musicianObj.targetPosition);
                    if (distance > 0.1) {
                        const direction = musicianObj.targetPosition.clone().sub(mesh.position).normalize();
                        mesh.position.add(direction.multiplyScalar(moveSpeed * deltaTime));
                    } else {
                        // --- Arrived at edge, start the plane jump ---
                        musicianObj.state = 'JUMPING_PLANE';
                        musicianObj.jumpHeight = jumpHeight + Math.random() * jumpPlaneVariance;
                        musicianObj.jumpStartPos = mesh.position.clone();
                        const targetPlane = musicianObj.currentPlane === 'stage' ? 'floor' : 'stage';
                        const targetArea = targetPlane === 'stage' ? stageArea : floorArea;
                        musicianObj.jumpEndPos = new THREE.Vector3(
                            mesh.position.x,
                            targetArea.y + musicianHeight/2,
                            planeEdgeZ + (targetPlane === 'stage' ? -1 : 1)
                        );
                        musicianObj.targetPosition = musicianObj.jumpEndPos.clone();
                        musicianObj.currentPlane = targetPlane;
                        musicianObj.jumpProgress = 0;
                    }
                } else if (musicianObj.state === 'JUMPING_PLANE') {
                    musicianObj.jumpProgress += deltaTime / jumpDuration;
                    if (musicianObj.jumpProgress < 1) {
                        // Determine base height based on which half of the jump we're in
                        const baseHeight = musicianObj.jumpProgress < 0.5 ? musicianObj.jumpStartPos.y : musicianObj.jumpEndPos.y;
                        const arcHeight = Math.sin(musicianObj.jumpProgress * Math.PI) * musicianObj.jumpHeight;

                        // Interpolate horizontal position
                        const horizontalProgress = musicianObj.jumpProgress;
                        mesh.position.x = THREE.MathUtils.lerp(musicianObj.jumpStartPos.x, musicianObj.jumpEndPos.x, horizontalProgress);
                        mesh.position.z = THREE.MathUtils.lerp(musicianObj.jumpStartPos.z, musicianObj.jumpEndPos.z, horizontalProgress);
                        
                        // Apply vertical arc
                        mesh.position.y = baseHeight + arcHeight;
                    } else {
                        // Landed
                        mesh.position.copy(musicianObj.jumpEndPos);
                        musicianObj.state = 'WAITING';
                        musicianObj.waitStartTime = time;
                        musicianObj.waitTime = 1 + Math.random() * 2;
                    }
                }

                // --- Jumping in place (can happen in any state except during a plane jump) ---
                if (musicianObj.isJumping) {
                    const jumpProgress = (time - musicianObj.jumpStartTime) / jumpDuration;
                    if (jumpProgress < 1) {
                        const baseHeight = area.y + musicianHeight/2;
                        mesh.position.y = baseHeight + Math.sin(jumpProgress * Math.PI) * musicianObj.jumpHeight;
                    } else {
                        musicianObj.isJumping = false;
                        mesh.position.y = area.y + musicianHeight / 2;
                    }
                } else {
                    let currentJumpChance = jumpChance * deltaTime; // Base chance over time
                    const musicTime = state.clock.getElapsedTime();
                    if (state.music && state.music.isLoudEnough && state.music.beatIntensity > 0.8 && musicTime > 15) {
                        currentJumpChance = 0.1; // High, fixed chance on the beat
                    }

                    if (Math.random() < currentJumpChance && musicianObj.state !== 'JUMPING_PLANE' && musicianObj.state !== 'PREPARING_JUMP') {
                        musicianObj.isJumping = true;
                        musicianObj.jumpHeight = jumpHeight + Math.random() * jumpVariance;
                        musicianObj.jumpStartTime = time;
                    }
                }
            });
        }
    }

    onPartyStart() {
        this.musicians.forEach(musicianObj => {
            musicianObj.mesh.visible = true;
            // Teleport to stage
            musicianObj.state = 'WAITING';
            musicianObj.waitStartTime = state.clock.getElapsedTime();
        });
    }

    onPartyEnd() {
        this.musicians.forEach(musicianObj => {
            musicianObj.isJumping = false;
            musicianObj.state = 'WAITING';
            musicianObj.waitStartTime = state.clock.getElapsedTime();
            //musicianObj.mesh.visible = false;
        });
    }
}

new MedievalMusicians();