<template>
    <div class="endless-journal-physics">
        <canvas width="64" height="64" ref="canvas" hidden />

        <div class="container" ref="container" />
    </div>
</template>

<script setup>
import Matter from 'matter-js'
import { onMounted, ref, watch } from 'vue'
import { fontAtlas } from '@/libs/fontAtlas'
import { useElementBounding } from '@vueuse/core'
import { themes } from '@shared/config'

const props = defineProps({ input: String })

const container = ref()
const canvas = ref()

const { width, height } = useElementBounding(container)

const chars = [
    '!',
    '?',
    '"',
    "'",
    ',',
    '-',
    '.',
    ':',
    ';',
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    'A',
    'B',
    'C',
    'D',
    'E',
    'F',
    'G',
    'H',
    'I',
    'J',
    'K',
    'L',
    'M',
    'N',
    'O',
    'P',
    'Q',
    'R',
    'S',
    'T',
    'U',
    'V',
    'W',
    'X',
    'Y',
    'Z',
]
const charTextures = ref({})

const generateCharImages = (atlas) => {
    const ctx = canvas.value.getContext('2d')

    const step = 64
    for (let y = 0; y < 8; y++) {
        for (let x = 0; x < 8; x++) {
            const index = y * 8 + x
            if (chars[index]) {
                const char = chars[index]
                ctx.clearRect(0, 0, 64, 64)
                ctx.drawImage(atlas, x * step, y * step, 64, 64, 0, 0, 64, 64)
                charTextures.value[char] = canvas.value.toDataURL()
            }
        }
    }
}

// Matter-js
const Bodies = ref()
const Composite = ref()
const Engine = ref()
const engine = ref()
onMounted(async () => {
    const atlas = await fontAtlas({
        font: {
            size: '60px',
            // family: 'Iosevka Charon Mono',
            family: 'monospace',
            weight: '700',
        },
        step: [64, 64],
        chars: chars,
        color: themes.light.fg
    })
    generateCharImages(atlas)

    const Render = Matter.Render

    Engine.value = Matter.Engine
    Bodies.value = Matter.Bodies
    Composite.value = Matter.Composite

    // create an engine
    engine.value = Engine.value.create()

    // create a renderer
    const render = Render.create({
        element: container.value,
        engine: engine.value,
        options: {
            width: width.value,
            height: height.value,
            wireframes: false,
            background: 'transparent',
        },
    })

    const ground = Bodies.value.rectangle(
        width.value / 2,
        height.value + 50,
        width.value,
        100,
        {
            isStatic: true,
        }
    )
    const wallLeft = Bodies.value.rectangle(
        -50,
        height.value / 2,
        100,
        height.value,
        {
            isStatic: true,
        }
    )
    const wallRight = Bodies.value.rectangle(
        width.value,
        height.value / 2,
        1,
        height.value,
        {
            isStatic: true,
        }
    )

    // add all of the bodies to the world
    Composite.value.add(engine.value.world, [ground, wallLeft, wallRight])

    // run the renderer
    Render.run(render)

    run()
})

const run = () => {
    const bodies = Composite.value.allBodies(engine.value.world)
    bodies.forEach((body) => {
        if (body.speed < 0.05 && !body.isStatic) {
            Matter.Body.setStatic(body, true)
        }
    })

    const delta = 1000 / 60
    Engine.value.update(engine.value, delta)

    requestAnimationFrame(run)
}

watch(
    () => props.input,
    (newInput, oldInput) => {
        if (
            newInput?.length > oldInput?.length ||
            (newInput.length && !oldInput)
        ) {
            const char = newInput.substr(newInput.length - 1).toUpperCase()

            if (charTextures.value[char]) {
                const charBox = Bodies.value.rectangle(
                    width.value / 2,
                    height.value * 0.2,
                    32,
                    32,
                    {
                        render: {
                            sprite: {
                                texture: charTextures.value[char],
                                xScale: 0.5,
                                yScale: 0.5,
                            },
                        },
                    }
                )
                Matter.Body.setVelocity(charBox, {
                    x: Math.random() * 40 - 20,
                    y: 30,
                })
                Composite.value.add(engine.value.world, [charBox])
            }
        }
    }
)
</script>

<style lang="scss">
.endless-journal-physics {
    position: absolute;
    inset: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    pointer-events: none;

    .container {
        position: absolute;
        inset: 5px;
        pointer-events: none;
    }
}
</style>
