<script lang="ts">
import { type Position } from '#shared/canvas.util';
import { hasPermissions } from '~/shared/auth.util';

const cancelEvent = (e: Event) => e.preventDefault();
function center(touches: TouchList): Position
{
    const pos = { x: 0, y: 0 };

    for(const touch of touches)
    {
        pos.x += touch.clientX;
        pos.y += touch.clientY;
    }

    pos.x /= touches.length;
    pos.y /= touches.length;
    return pos;
}
function distance(touches: TouchList): number
{
    const [A, B] = touches;
    return Math.hypot(B.clientX - A.clientX, B.clientY - A.clientY);
}

/*

stroke-light-red
stroke-light-orange
stroke-light-yellow
stroke-light-green
stroke-light-cyan
stroke-light-purple
dark:stroke-dark-red
dark:stroke-dark-orange
dark:stroke-dark-yellow
dark:stroke-dark-green
dark:stroke-dark-cyan
dark:stroke-dark-purple
fill-light-red
fill-light-orange
fill-light-yellow
fill-light-green
fill-light-cyan
fill-light-purple
dark:fill-dark-red
dark:fill-dark-orange
dark:fill-dark-yellow
dark:fill-dark-green
dark:fill-dark-cyan
dark:fill-dark-purple
bg-light-red
bg-light-orange
bg-light-yellow
bg-light-green
bg-light-cyan
bg-light-purple
dark:bg-dark-red
dark:bg-dark-orange
dark:bg-dark-yellow
dark:bg-dark-green
dark:bg-dark-cyan
dark:bg-dark-purple
border-light-red
border-light-orange
border-light-yellow
border-light-green
border-light-cyan
border-light-purple
dark:border-dark-red
dark:border-dark-orange
dark:border-dark-yellow
dark:border-dark-green
dark:border-dark-cyan
dark:border-dark-purple
outline-light-red
outline-light-orange
outline-light-yellow
outline-light-green
outline-light-cyan
outline-light-purple
dark:outline-dark-red
dark:outline-dark-orange
dark:outline-dark-yellow
dark:outline-dark-green
dark:outline-dark-cyan
dark:outline-dark-purple

*/
</script>

<script setup lang="ts">
import { Icon } from '@iconify/vue/dist/iconify.js';
import { clamp } from '#shared/general.util';
import type { CanvasContent } from '~/types/content';

const { path } = defineProps<{
    path: string
}>();

const { user } = useUserSession();
const { content, get } = useContent();
const overview = computed(() => content.value.find(e => e.path === path) as CanvasContent | undefined);
const isOwner = computed(() => user.value?.id === overview.value?.owner);

const loading = ref(false);
if(overview.value && !overview.value.content)
{
    loading.value = true;
    await get(path);
    loading.value = false;
}
const canvas = computed(() => overview.value && overview.value.content ? overview.value.content : undefined);

const dispX = ref(0), dispY = ref(0), minZoom = ref(0.1), zoom = ref(0.5);
const canvasRef = useTemplateRef('canvasRef'), transformRef = useTemplateRef('transformRef');

const updateScaleVar = useDebounceFn(() => {
    if(transformRef.value)
    {
        transformRef.value.style.setProperty('--tw-scale', zoom.value.toString());
    }
    if(canvasRef.value)
    {
        canvasRef.value.style.setProperty('--zoom-multiplier', (1 / Math.pow(zoom.value, 0.7)).toFixed(3));
    }
}, 100);

const reset = (_: MouseEvent) => {
    zoom.value = minZoom.value;

    dispX.value = 0;
    dispY.value = 0;

    updateTransform();
}

onMounted(() => {
    let lastX = 0, lastY = 0, lastDistance = 0;
    let box = canvasRef.value?.getBoundingClientRect()!;
    const dragMove = (e: MouseEvent) => {
        dispX.value = dispX.value - (lastX - e.clientX) / zoom.value;
        dispY.value = dispY.value - (lastY - e.clientY) / zoom.value;

        lastX = e.clientX;
        lastY = e.clientY;

        updateTransform();
    };
    const dragEnd = (e: MouseEvent) => {
        window.removeEventListener('mouseup', dragEnd);
        window.removeEventListener('mousemove', dragMove);
    };
    canvasRef.value?.addEventListener('mouseenter', () => { 
        window.addEventListener('wheel', cancelEvent, { passive: false });
        document.addEventListener('gesturestart', cancelEvent);
        document.addEventListener('gesturechange', cancelEvent);

        canvasRef.value?.addEventListener('mouseleave', () => {
            window.removeEventListener('wheel', cancelEvent);
            document.removeEventListener('gesturestart', cancelEvent);
            document.removeEventListener('gesturechange', cancelEvent);
        });
    })
    window.addEventListener('resize', () => box = canvasRef.value?.getBoundingClientRect()!);
    canvasRef.value?.addEventListener('mousedown', (e) => {
        lastX = e.clientX;
        lastY = e.clientY;

        window.addEventListener('mouseup', dragEnd, { passive: true });
        window.addEventListener('mousemove', dragMove, { passive: true });
    }, { passive: true });
    canvasRef.value?.addEventListener('wheel', (e) => {
        if((zoom.value >= 3 && e.deltaY < 0) || (zoom.value <= minZoom.value && e.deltaY > 0))
            return;

        const diff = Math.exp(e.deltaY * -0.001);
        const centerX = (box.x + box.width / 2), centerY = (box.y + box.height / 2);
        const mousex = centerX - e.clientX, mousey = centerY - e.clientY;

        dispX.value = dispX.value - (mousex / (diff * zoom.value) - mousex / zoom.value);
        dispY.value = dispY.value - (mousey / (diff * zoom.value) - mousey / zoom.value);

        zoom.value = clamp(zoom.value * diff, minZoom.value, 3)

        updateTransform();
    }, { passive: true });
    canvasRef.value?.addEventListener('touchstart', (e) => {
        ({ x: lastX, y: lastY } = center(e.touches));

        if(e.touches.length > 1)
        {
            lastDistance = distance(e.touches);
        }

        canvasRef.value?.addEventListener('touchend', touchend, { passive: true });
        canvasRef.value?.addEventListener('touchcancel', touchcancel, { passive: true });
        canvasRef.value?.addEventListener('touchmove', touchmove, { passive: true });
    }, { passive: true });
    const touchend = (e: TouchEvent) => {
        if(e.touches.length > 1)
        {
            ({ x: lastX, y: lastY } = center(e.touches));
        }

        canvasRef.value?.removeEventListener('touchend', touchend);
        canvasRef.value?.removeEventListener('touchcancel', touchcancel);
        canvasRef.value?.removeEventListener('touchmove', touchmove);
    };
    const touchcancel = (e: TouchEvent) => {
        if(e.touches.length > 1)
        {
            ({ x: lastX, y: lastY } = center(e.touches));
        }

        canvasRef.value?.removeEventListener('touchend', touchend);
        canvasRef.value?.removeEventListener('touchcancel', touchcancel);
        canvasRef.value?.removeEventListener('touchmove', touchmove);
    };
    const touchmove = (e: TouchEvent) => {
        const pos = center(e.touches);
        dispX.value = dispX.value - (lastX - pos.x) / zoom.value;
        dispY.value = dispY.value - (lastY - pos.y) / zoom.value;
        lastX = pos.x;
        lastY = pos.y;

        if(e.touches.length === 2)
        {
            const dist = distance(e.touches);
            const diff = dist / lastDistance;
            lastDistance = dist;
            
            zoom.value = clamp(zoom.value * diff, minZoom.value, 3);
        }

        updateTransform();
    };

    updateTransform();
});

function updateTransform()
{
    if(transformRef.value)
    {
        transformRef.value.style.transform = `scale3d(${zoom.value}, ${zoom.value}, 1) translate3d(${dispX.value}px, ${dispY.value}px, 0)`;
    }
    updateScaleVar();
}
</script>

<template>
    <div ref="canvasRef" class="absolute top-0 left-0 overflow-hidden w-full h-full touch-none" :style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
        <div class="flex flex-col absolute sm:top-2 top-10 left-2 z-[35] overflow-hidden gap-4">
            <div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
                <Tooltip message="Zoom avant" side="right">
                    <div @click="zoom = clamp(zoom * 1.1, minZoom, 3); updateTransform()" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:plus" />
                    </div>
                </Tooltip>
                <Tooltip message="Reset" side="right">
                    <div @click="zoom = 1; updateTransform();" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:reload" />
                    </div>
                </Tooltip>
                <Tooltip message="Tout contenir" side="right">
                    <div @click="reset" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:corners" />
                    </div>
                </Tooltip>
                <Tooltip message="Zoom arrière" side="right">
                    <div @click="zoom = clamp(zoom / 1.1, minZoom, 3); updateTransform()" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer">
                        <Icon icon="radix-icons:minus" />
                    </div>
                </Tooltip>
            </div>
            <NuxtLink v-if="overview && isOwner || hasPermissions(user?.permissions ?? [], ['admin', 'editor'])" :to="{ name: 'explore-edit', hash: `#${encodeURIComponent(overview!.path)}` }" class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10">
                <Tooltip message="Modifier" side="right">
                    <Icon icon="radix-icons:pencil-1" class="w-8 h-8 p-2" />
                </Tooltip>
            </NuxtLink>
        </div>
        <div ref="transformRef" :style="{
            'transform-origin': 'center center',
        }" class="h-full">
            <div v-if="canvas" class="absolute top-0 left-0 w-full h-full pointer-events-none *:pointer-events-auto *:select-none touch-none">
                <div>
                    <CanvasNode v-for="node of canvas.nodes" :key="node.id" ref="nodes" :node="node" :zoom="zoom" />
                </div>
                <div>
                    <CanvasEdge v-for="edge of canvas.edges" :key="edge.id" ref="edges" :edge="edge" :nodes="canvas.nodes!" />
                </div>
            </div>
        </div>
    </div>
</template>