<template>
    <div id="waves" />
</template>

<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import * as THREE from "three";

const amountX = ref(50);
const amountY = ref(50);
const color = ref("#91a3c2");
const topOffset = ref(650);

let count = 0;
let mouseX = 0;
let windowHalfX = null;
let camera = null;
let scene = null;
let particles = null;
let renderer = null;

const init = () => {
    const SEPARATION = 100;
    const SCREEN_WIDTH = window.innerWidth;
    const SCREEN_HEIGHT = window.innerHeight;
    const container = document.createElement("div");
    windowHalfX = window.innerWidth / 2;
    container.style.position = "relative";
    container.style.top = `${topOffset.value}px`;
    container.style.height = `${SCREEN_HEIGHT - topOffset.value}px`;

    const waves = document.getElementById("waves");
    waves.appendChild(container);

    camera = new THREE.PerspectiveCamera(
        75,
        SCREEN_WIDTH / SCREEN_HEIGHT,
        1,
        10000,
    );
    camera.position.z = 1000;
    scene = new THREE.Scene();
    const numParticles = amountX.value * amountY.value;
    const positions = new Float32Array(numParticles * 3);
    const scales = new Float32Array(numParticles);
    let i = 0;
    let j = 0;
    for (let ix = 0; ix < amountX.value; ix++) {
        for (let iy = 0; iy < amountY.value; iy++) {
            positions[i] = ix * SEPARATION - (amountX.value * SEPARATION) / 2;
            positions[i + 1] = 0;
            positions[i + 2] = iy * SEPARATION - (amountY.value * SEPARATION) / 2;
            scales[j] = 1;
            i += 3;
            j++;
        }
    }
    const geometry = new THREE.BufferGeometry();
    geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
    geometry.setAttribute("scale", new THREE.BufferAttribute(scales, 1));
    const material = new THREE.ShaderMaterial({
        uniforms: {
            color: { value: new THREE.Color(color.value) },
        },
        vertexShader: `
        attribute float scale;
        void main() {
          vec4 mvPosition = modelViewMatrix * vec4( position, 2.0 );
          gl_PointSize = scale * ( 300.0 / - mvPosition.z );
          gl_Position = projectionMatrix * mvPosition;
        }
      `,
        fragmentShader: `
        uniform vec3 color;
        void main() {
          if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
          gl_FragColor = vec4( color, 1.0 );
        }
      `,
    });
    particles = new THREE.Points(geometry, material);
    scene.add(particles);
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(container.clientWidth, container.clientHeight);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setClearAlpha(0);
    container.appendChild(renderer.domElement);
    window.addEventListener("resize", onWindowResize, { passive: false });
    // document.addEventListener("mousemove", onDocumentMouseMove, {
    //     passive: false,
    // });
    // document.addEventListener("touchstart", onDocumentTouchStart, {
    //     passive: false,
    // });
    // document.addEventListener("touchmove", onDocumentTouchMove, {
    //     passive: false,
    // });
};

const render = () => {
    camera.position.x += (mouseX - camera.position.x) * 0.05;
    camera.position.y = 400;
    camera.lookAt(scene.position);
    const positions = particles.geometry.attributes.position.array;
    const scales = particles.geometry.attributes.scale.array;

    let i = 0;
    let j = 0;
    for (let ix = 0; ix < amountX.value; ix++) {
        for (let iy = 0; iy < amountY.value; iy++) {
            positions[i + 1] =
                Math.sin((ix + count) * 0.3) * 100 + Math.sin((iy + count) * 0.5) * 100;
            scales[j] =
                (Math.sin((ix + count) * 0.3) + 1) * 8 +
                (Math.sin((iy + count) * 0.5) + 1) * 8;
            i += 3;
            j++;
        }
    }

    particles.geometry.attributes.position.needsUpdate = true;
    particles.geometry.attributes.scale.needsUpdate = true;
    renderer.render(scene, camera);
    count += 0.08;
};

const animate = () => {
    requestAnimationFrame(animate);
    render();
};

const onDocumentMouseMove = (event) => {
    mouseX = event.clientX - windowHalfX;
};

const onDocumentTouchStart = (event) => {
    if (event.touches.length === 1) {
        mouseX = event.touches[0].pageX - windowHalfX;
    }
};

const onDocumentTouchMove = (event) => {
    if (event.touches.length === 1) {
        event.preventDefault();
        mouseX = event.touches[0].pageX - windowHalfX;
    }
};

const onWindowResize = () => {
    windowHalfX = window.innerWidth / 2;
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
};

onMounted(() => {
    init();
    animate();
});

onUnmounted(() => {
    window.removeEventListener("resize", onWindowResize);
    document.removeEventListener("mousemove", onDocumentMouseMove);
    document.removeEventListener("touchstart", onDocumentTouchStart);
    document.removeEventListener("touchmove", onDocumentTouchMove);
});
</script>