import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";

const motionBlurShader = {
    uniforms: {
        tDiffuse: { value: null },
        velocity: { value: new THREE.Vector2(0, 0) }, // 摄像机速度
        blurAmount: { value: 0.5 }, // 模糊强度
    },
    vertexShader: /* glsl */`
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `,
    fragmentShader: /* glsl */`
        uniform sampler2D tDiffuse;
        uniform vec2 velocity;
        uniform float blurAmount;
        varying vec2 vUv;

        void main() {
            vec4 color = texture2D(tDiffuse, vUv);
            vec2 offset = velocity * blurAmount * 0.01;
            vec4 blurredColor = texture2D(tDiffuse, vUv + offset);
            blurredColor += texture2D(tDiffuse, vUv - offset);
            gl_FragColor = mix(color, blurredColor, 0.5);
        }
    `,
};

export default class WorldViewport {
    /** @param {HTMLCanvasElement} canvas */
    constructor(canvas) {
        this.canvas = canvas;
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 64);
        this.renderer = new THREE.WebGLRenderer({ antialias: true, canvas });
        this.composer = new EffectComposer(this.renderer);
        this.composer.addPass(new RenderPass(this.scene, this.camera));
        this.composer.addPass(this.motionBlurPass = new ShaderPass(motionBlurShader));
        {
            this.previousCameraPosition = new THREE.Vector3();
            this.velocity = new THREE.Vector2();
        }
        this.resize();
        window.addEventListener('resize', this.resize.bind(this));
    }
    resize() {
        const { innerWidth: width, innerHeight: height } = window;
        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.setSize(width, height);
        this.composer.setPixelRatio(window.devicePixelRatio);
        this.composer.setSize(width, height);
        this.camera.aspect = width / height;
        this.camera.updateProjectionMatrix();

        if (this.bloomPass) this.composer.removePass(this.bloomPass);
        this.bloomPass = new UnrealBloomPass(
            new THREE.Vector2(width, height),
            // 1.5, 0.4, 0.85
            // .1, 0.4, 0.99
            // .2, 0.4, 0.1
            .12, 0.4, 0.1
            // .2, 0.5, 0.2
        );
        this.composer.addPass(this.bloomPass);
    }

    setup() {
        const loader = new GLTFLoader();
        loader.load("assets/models/website_viewport_1.glb", data => {
            const scene = data.scene;
            this.scene.add(scene);
        });
        loader.load("assets/models/website_viewport_2.glb", data => {
            const scene = data.scene;
            scene.position.set(0, 0, 64);
            this.scene.add(scene);
        });

        {
            const floorGeometry = new THREE.BoxGeometry(9.1, 1, 9.2);
            const floorMaterial = new THREE.MeshBasicMaterial( { color: 0x686587 } );
            const floor = new THREE.Mesh(floorGeometry, floorMaterial);
            floor.position.set(-22.017, -15.498, 18.078);
            this.scene.add(floor);
        }

        const ambientLight = new THREE.AmbientLight(0xFFFFFF, 1);
        this.scene.add(ambientLight);
        {
            // const fog = 0xffc3d3;  // pink
            const fog = 0x171843;  // dark blue
            this.scene.fog = new THREE.FogExp2(fog, 0.02);
            // this.scene.fog = new THREE.Fog(fog, 0.9, 64);
            this.scene.background = new THREE.Color(fog);
        }

        const LightColors = {
            Torch: 0xfef4c5,
            EndRod: 0xe4f8fc,
            Lantern: 0xfcfcd2,
            SeaLantern: 0xd1ded6,
            EnchantingTable: 0xa11de6,
            JackOLantern: 0xeced98,
        };
        const lights = [
            [ -28.488, -10.795, -18.502, LightColors.Lantern, 1 ],
            [ -28.488, -10.795, -14.502, LightColors.Lantern, 8, undefined, 2 ],
            [ -28.488, -10.795, -15.502, LightColors.Lantern, 8, undefined, 2 ],
            [ -31.488, -11.795, -11.502, LightColors.Lantern, 1 ],
            [ -29.489, -11.292, -6.774, LightColors.Torch, 1 ],
            [ -25.546, -12.574, -7.592, LightColors.JackOLantern ],
            [ -30.546, -12.574, -7.592, LightColors.JackOLantern ],
            [ -26.489, -11.292, -6.774, LightColors.Torch, 1 ],
            [ -29.488, -13.460, -4.502, LightColors.EndRod, 1, undefined, 2 ],
            [ -27.489, -12.795, 0.497, LightColors.Lantern ],
            [ -21.488, -13.795, -2.502, LightColors.Lantern ],
            [ -19.488, -8.795, 1.497, LightColors.Lantern ],
            [ -18.488, -7.795, -5.502, LightColors.Lantern ],
            [ -17.488, -12.795, 3.497, LightColors.Lantern ],
            [ -8.488, -10.795, -1.502, LightColors.Lantern ],
            [ -8.488, -13.795, 4.497, LightColors.Lantern ],
            [ -9.488, -8.795, -6.502, LightColors.Lantern ],
            [ -6.488, -10.795, 2.497, LightColors.Lantern ],
            [ -9.488, -12.595, -6.502, LightColors.SeaLantern ],
            [ -16.488, -13.795, 6.497, LightColors.Lantern ],
            [ -15.488, -13.795, 11.497, LightColors.EnchantingTable ],
            [ -22.488, -12.795, 13.497, LightColors.Lantern ],
            [ -27.488, -12.795, 20.497, LightColors.Lantern ],
            [ -20.488, -12.795, 23.497, LightColors.Lantern ],
            // === BIG TREE ===
            [ -29.488, -12.795, 36.497, LightColors.Lantern ],
            [ -17.488, -12.795, 42.497, LightColors.Lantern ],
            [ -15.488, -13.595, 48.497, LightColors.EndRod ],
            [ -5.488, -11.795, 46.497, LightColors.Lantern ],
            [ -26.488, -10.795, 54.497, LightColors.Lantern ],
            [ -28.488, -12.795, 57.497, LightColors.Lantern ],
            [ -20.488, -10.795, 60.497, LightColors.Lantern ],
            [ -27.488, -10.795, 65.497, LightColors.Lantern ],
            [ -20.488, -10.795, 68.497, LightColors.Lantern ],
            [ -26.488, -10.795, 73.497, LightColors.Lantern ],
            [ -20.488, -10.795, 78.497, LightColors.Lantern ],
            // === HOUSE ===
            [ -11.488, -4.795, 66.497, LightColors.Lantern ],
            [ -12.488, -4.795, 67.497, LightColors.Lantern ],
            [ -14.488, -8.795, 58.497, LightColors.Lantern ],
            // [ , LightColors.Lantern ],
        ];
        for (const [ x, y, z, color, intensity = 8, distance = 0, decay = 1 ] of lights) {
            const light = new THREE.PointLight(color, intensity, distance, decay);
            light.position.set(x, y, z);
            this.scene.add(light);
        }

        // petals
        {
            const regions = [
                [ [ -18.383, -6.058, -8.695 ], [ -25.021, -5.090, 1.545 ] ],
                [ [ -12.133, -6.117, 5.980 ], [ -17.765, -3.190, 17.714 ] ],
            ].map(
                ([ [ minX, minY, minZ ], [ maxX, maxY, maxZ ] ]) =>
                ({ min: { x: minX, y: minY, z: minZ }, max: { x: maxX, y: maxY, z: maxZ } })
            );

            const petalTexture = new THREE.TextureLoader().load('assets/images/cherry_petal_atlas.png');
            petalTexture.magFilter = THREE.NearestFilter;
            petalTexture.minFilter = THREE.NearestFilter;
            const petalMaterial = new THREE.MeshBasicMaterial({
                map: petalTexture,
                transparent: true,
                side: THREE.DoubleSide,
            });
            const petalGeometry = new THREE.PlaneGeometry(.2, .2);
            const petalSetRandomPosition = (petal, region) => petal.position.set(
                THREE.MathUtils.lerp(region.min.x, region.max.x, Math.random()),
                THREE.MathUtils.lerp(region.min.y, region.max.y, Math.random() * 10),
                THREE.MathUtils.lerp(region.min.z, region.max.z, Math.random())
            );
            const createPetal = region => {
                const petal = new THREE.Mesh(petalGeometry, petalMaterial);

                const petalIndex = Math.floor(Math.random() * 4);
                const uOffset = (petalIndex % 4) * 0.25;
                const vOffset = Math.floor(petalIndex / 4) * 0.33;
                petal.material.map.offset.set(uOffset, vOffset);
                petal.material.map.repeat.set(0.25, 0.33);

                petalSetRandomPosition(petal, region);
                petal.rotation.set(
                    Math.random() * Math.PI,
                    Math.random() * Math.PI,
                    Math.random() * Math.PI
                );
                petal.scale.setScalar(Math.random() * 0.5 + 0.5);

                return petal;
            };
            const petalsGroups = regions.map(region => {
                const group = new THREE.Group();
                for (let i = 0; i < 20; i++) group.add(createPetal(region));
                return group;
            });
            petalsGroups.forEach(group => this.scene.add(group));

            const animatePetal = (petal, region) => {
                petal.position.y -= 0.02;

                // 风向影响
                petal.position.x += 0.001;
                petal.position.z += 0.003;

                // 左右摇摆
                petal.rotation.z += Math.sin(petal.position.y * 0.1) * 0.02;

                // 重置位置
                if (petal.position.y <= -15) petalSetRandomPosition(petal, region);
            };
            this.animatePetals = () => petalsGroups.forEach((group, i) =>
                group.children.forEach(petal => animatePetal(petal, regions[i])));
        }

        this.camera.position.set(-34, -12, -0.1);
        this.camera.rotation.set(0, THREE.MathUtils.degToRad(270), 0);

        this.controller();
        this.animate();
    }

    controller() {
        this.targetZ = this.camera.position.z;
        this.currentZ = this.camera.position.z;
        this.damping = 0.1;
        this.isDragging = false;

        window.addEventListener('wheel', event => this.targetZ += event.deltaY * 0.01);
        this.canvas.addEventListener('mousedown', () => this.isDragging = true);
        this.canvas.addEventListener('mousemove', event => this.isDragging && (this.targetZ -= event.movementX * 0.01));
        window.addEventListener('mouseup', () => this.isDragging = false);

        this.canvas.addEventListener('touchstart', event => this.previousX = event.touches[0].clientX);
        this.canvas.addEventListener('touchmove', event => {
            const currentX = event.touches[0].clientX;
            if (this.previousX) this.targetZ += (this.previousX - currentX) * 0.025;
            this.previousX = currentX;
        });
        window.addEventListener('touchend', () => this.previousX = null);
    }

    renderPseudo;
    animate() {
        requestAnimationFrame(this.animate.bind(this));

        this.velocity.set(this.camera.position.z - this.previousCameraPosition.z, 0);
        this.previousCameraPosition.copy(this.camera.position);
        this.motionBlurPass.uniforms.velocity.value.copy(this.velocity);

        const computedZ = this.currentZ + (this.targetZ - this.currentZ) * this.damping;
        // 当接近边界时，应用一个缓动函数来逐渐减速
        if (computedZ < -17) {
            this.currentZ += (-17 - this.currentZ) * this.damping;
        } else if (computedZ > 64 * 2 - 60) {
            this.currentZ += ((64 * 2 - 60) - this.currentZ) * this.damping;
        } else {
            this.currentZ = computedZ;
        }
        this.camera.position.z = this.currentZ;

        this.animatePetals();

        if (this.renderPseudo) this.renderPseudo();

        this.composer.render();
    }
}

// function mappingFormMc(x, y, z) {
//     // [ -27.489, -12.795, 0.497 ] -> [ 8846, 72, 7507 ]
//     return [ x - 8873.489, y - 84.795, z - 7506.503 ];
// }
