<template>
    <primitive v-if="arrowMesh" :object="arrowMesh" />
</template>

<script setup>
import { InstancedMesh, ShaderMaterial, InstancedBufferAttribute } from 'three'
import { createArrowGeometry } from '@/libs/three/createArrowGeometry'
import FluidSimulation from '@/libs/glsl/fluid/fluidSimulation'
import fragmentShader from '@/libs/glsl/arrows/fragment.glsl'
import vertexShader from '@/libs/glsl/arrows/vertex.glsl'
import { shallowRef, watch, onMounted } from 'vue'
import { useLoop, useTres } from '@tresjs/core'
import { Color } from 'three'

const props = defineProps({
    gridSize: {
        type: Number,
        default: 50,
    },
    width: {
        type: Number,
        default: 2,
    },
    height: {
        type: Number,
        default: 2,
    },
    arrowScale: {
        type: Number,
        default: 0.015,
    },
    scaleMultiplier: {
        type: Number,
        default: 2.0,
    },
    color: {
        type: String,
        default: '#ffffff',
    },
})

const arrowMesh = shallowRef(null)

const { renderer } = useTres()
const { onRender } = useLoop()

// Fluid simulation
const simulation = new FluidSimulation({
    renderer: renderer,
    size: 1280,
})

onRender(() => {
    if (arrowMesh.value) {
        arrowMesh.value.material.uniforms.u_velocity.value = simulation.update()
    }
})

onMounted(() => {
    createArrowField()
})

const createArrowField = () => {
    const geometry = createArrowGeometry()
    const instanceCount = props.gridSize * props.gridSize

    // Create instanced buffer attribute for grid positions
    const instanceGrid = new Float32Array(instanceCount * 2)

    let idx = 0
    for (let y = 0; y < props.gridSize; y++) {
        for (let x = 0; x < props.gridSize; x++) {
            instanceGrid[idx++] = x
            instanceGrid[idx++] = y
        }
    }

    // Add instance attribute to geometry
    geometry.setAttribute(
        'instanceGrid',
        new InstancedBufferAttribute(instanceGrid, 2),
    )

    // Parse color
    const color = new Color(props.color)

    // Auto-calculate spacing to fill viewport
    // Use the larger dimension to ensure coverage
    const spacingX = props.width / (props.gridSize - 1)
    const spacingY = props.height / (props.gridSize - 1)
    const spacing = Math.max(spacingX, spacingY)

    // Create shader material
    const material = new ShaderMaterial({
        vertexShader,
        fragmentShader,
        uniforms: {
            u_velocity: { value: simulation.update() },
            u_grid_size: { value: [props.gridSize, props.gridSize] },
            u_spacing: { value: spacing },
            u_arrow_scale: { value: props.arrowScale },
            u_scale_multiplier: { value: props.scaleMultiplier },
            u_color: { value: color },
        },
    })

    // Create instanced mesh
    arrowMesh.value = new InstancedMesh(geometry, material, instanceCount)
    arrowMesh.value.frustumCulled = false
}

// Watch for prop changes
watch(
    () => [
        props.gridSize,
        props.width,
        props.height,
        props.arrowScale,
        props.scaleMultiplier,
        props.color,
    ],
    () => {
        if (arrowMesh.value) {
            // Recalculate spacing
            const spacingX = props.width / (props.gridSize - 1)
            const spacingY = props.height / (props.gridSize - 1)
            const spacing = Math.max(spacingX, spacingY)

            arrowMesh.value.material.uniforms.u_grid_size.value = [
                props.gridSize,
                props.gridSize,
            ]
            arrowMesh.value.material.uniforms.u_spacing.value = spacing
            arrowMesh.value.material.uniforms.u_arrow_scale.value =
                props.arrowScale
            arrowMesh.value.material.uniforms.u_scale_multiplier.value =
                props.scaleMultiplier
            arrowMesh.value.material.uniforms.u_color.value = new Color(
                props.color,
            )
        }
    },
)
</script>
