<template>
    <div class="absolute" ref="dom" :style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-color': node.color?.hex}" :class="{'-z-10': node.type === 'group', 'z-10': node.type !== 'group'}">
        <div v-if="!editing || node.type === 'group'" style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4 cursor-move': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 w-full h-full hover:outline-4">
            <div class="w-full h-full py-2 px-4 flex !bg-opacity-[0.07] overflow-auto" :class="style.bg" @click.left="(e) => { if(node.type !== 'group') selectNode(e) }" @dblclick.left="(e) => { if(node.type !== 'group') editNode(e) }">
                <div v-if="node.text && node.text.length > 0" class="flex items-center">
                    <MarkdownRenderer :content="node.text" :proses="{ a: FakeA }" />
                </div>
            </div>
            <div v-if="focusing">
                <span @mousedown.left="(e) => resizeNode(e, 0, 1, 0, -1)" id="n " class="cursor-n-resize absolute -top-3 -right-3 -left-3  h-6 group">
                    <span @mousedown.left="(e) => dragEdge(e, 'top')" :class="[style.bg]" class="hidden group-hover:block absolute rounded-full border-2 border-light-70 dark:border-dark-70 w-6 h-6 -top-1.5 left-1/2 -translate-x-3"></span>
                </span> <!-- North -->
                <span @mousedown.left="(e) => resizeNode(e, 0, 0, 0, 1)" id="s " class="cursor-s-resize absolute -bottom-3 -right-3 -left-3 h-6 group">
                    <span @mousedown.left="(e) => dragEdge(e, 'bottom')" :class="[style.bg]" class="hidden group-hover:block absolute rounded-full border-2 border-light-70 dark:border-dark-70 w-6 h-6 -bottom-1.5 left-1/2 -translate-x-3"></span>
                </span> <!-- South -->
                <span @mousedown.left="(e) => resizeNode(e, 0, 0, 1, 0)" id="e " class="cursor-e-resize absolute -top-3 -bottom-3 -right-3 w-6 group">
                    <span @mousedown.left="(e) => dragEdge(e, 'right')" :class="[style.bg]" class="hidden group-hover:block absolute rounded-full border-2 border-light-70 dark:border-dark-70 w-6 h-6 -right-1.5 top-1/2 -translate-y-3"></span>
                </span> <!-- East -->
                <span @mousedown.left="(e) => resizeNode(e, 1, 0, -1, 0)" id="w " class="cursor-w-resize absolute -top-3 -bottom-3 -left-3 w-6 group">
                    <span @mousedown.left="(e) => dragEdge(e, 'left')" :class="[style.bg]" class="hidden group-hover:block absolute rounded-full border-2 border-light-70 dark:border-dark-70 w-6 h-6 -left-1.5 top-1/2 -translate-y-3"></span>
                </span> <!-- West -->
                <span @mousedown.left="(e) => resizeNode(e, 1, 1, -1, -1)" id="nw" class="cursor-nw-resize absolute -top-4 -left-4  w-8 h-8"></span> <!-- North West -->
                <span @mousedown.left="(e) => resizeNode(e, 0, 1, 1, -1)" id="ne" class="cursor-ne-resize absolute -top-4 -right-4 w-8 h-8"></span> <!-- North East -->
                <span @mousedown.left="(e) => resizeNode(e, 0,  0, 1, 1)" id="se" class="cursor-se-resize absolute -bottom-4 -right-4 w-8 h-8"></span> <!-- South East -->
                <span @mousedown.left="(e) => resizeNode(e, 1, 0, -1, 1)" id="sw" class="cursor-sw-resize absolute -bottom-4 -left-4  w-8 h-8"></span> <!-- South West -->
            </div>
        </div>
        <div v-else style="outline-style: solid;" :class="[style.border, style.outline, { '!outline-4': focusing }]" class="outline-0 transition-[outline-width] border-2 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full flex py-2" >
            <FramedEditor v-model="node.text" autofocus :gutters="false"/>
        </div>
        <div v-if="!editing && node.type === 'group' && node.label !== undefined"  @click.left="(e) => selectNode(e)" @dblclick.left="(e) => editNode(e)" :class="style.border" style="max-width: 100%; font-size: calc(18px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider border-4 truncate inline-block text-light-100 dark:text-dark-100 absolute bottom-[100%] mb-2 px-2 py-1 font-thin">{{ node.label }}</div>
        <input v-else-if="editing && node.type === 'group'" v-model="node.label" @click="e => e.stopImmediatePropagation()" v-autofocus :class="[style.border, style.outline]" style="max-width: 100%; font-size: calc(18px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider border-4 truncate inline-block text-light-100 dark:text-dark-100 absolute bottom-[100%] appearance-none bg-transparent outline-4 mb-2 px-2 py-1 font-thin min-w-4" />
    </div>
</template>

<style>
.bg-colored
{
    --tw-bg-opacity: 1;
    background-color: rgba(from var(--canvas-color) r g b / var(--tw-bg-opacity));
}
</style>
<script setup lang="ts">
import type { Box, Direction } from '#shared/canvas.util';
import type { Element } from '../CanvasEditor.vue';
import FakeA from '../prose/FakeA.vue';
import type { CanvasNode } from '~/types/canvas';

const { node, zoom, snap } = defineProps<{
    node: CanvasNode
    zoom: number,
    snap: (activeNode: CanvasNode, resizeHandle?: Box) => Partial<Box>,
}>();

const emit = defineEmits<{
    (e: 'select', id: Element): void,
    (e: 'edit', id: Element): void,
    (e: 'move', id: string, x: number, y: number): void,
    (e: 'resize', id: string, x: number, y: number, w: number, h: number): void,
    (e: 'input', id: string, text: string): void,
    (e: 'edge', id: string, _e: MouseEvent, side: Direction): void,
}>();

const dom = useTemplateRef('dom');
const focusing = ref(false), editing = ref(false);
let oldText = node.type === 'group' ? node.label : node.text;

function selectNode(e: Event) {
    if(editing.value)
        return;

    focusing.value = true;
    emit('select', { type: 'node', id: node.id });

    dom.value?.addEventListener('mousedown', dragstart, { passive: true });
}
function editNode(e: Event) {
    focusing.value = true;
    editing.value = true;

    oldText = node.type === 'group' ? node.label : node.text;

    e.stopImmediatePropagation();

    dom.value?.removeEventListener('mousedown', dragstart);
    emit('edit', { type: 'node', id: node.id });
}
function resizeNode(e: MouseEvent, x: number, y: number, w: number, h: number) {
    e.stopImmediatePropagation();

    const startx = node.x, starty = node.y, startw = node.width, starth = node.height;
    let realx = node.x, realy = node.y, realw = node.width, realh = node.height;
    const resizemove = (e: MouseEvent) => {
        if(e.button !== 0)
            return;

        realx = realx + (e.movementX / zoom) * x;
        realy = realy + (e.movementY / zoom) * y;
        realw = Math.max(realw + (e.movementX / zoom) * w, 64);
        realh = Math.max(realh + (e.movementY / zoom) * h, 64);

        const result = e.altKey ? undefined : snap({ ...node, x: realx, y: realy, width: realw, height: realh }, { x, y, w, h });

        node.x = result?.x ?? realx;
        node.y = result?.y ?? realy;
        node.width = result?.w ?? realw;
        node.height = result?.h ?? realh;
    };
    const resizeend = (e: MouseEvent) => {
        if(e.button !== 0)
            return;

        emit('resize', node.id, node.x - startx, node.y - starty, node.width - startw, node.height - starth);

        window.removeEventListener('mousemove', resizemove);
        window.removeEventListener('mouseup', resizeend);
    }

    window.addEventListener('mousemove', resizemove);
    window.addEventListener('mouseup', resizeend);
}
function dragEdge(e: MouseEvent, direction: Direction) {
    e.stopImmediatePropagation();

    emit('edge', node.id, e, direction)
}
function unselect() {
    if(editing.value)
    {
        const text = node.type === 'group' ? node.label! : node.text!;
        
        if(text !== oldText)
        {
            if(node.type === 'group')
                node.label = oldText;
            else
                node.text = oldText;

            emit('input', node.id, text);
        }
    }
    focusing.value = false;
    editing.value = false;

    dom.value?.removeEventListener('mousedown', dragstart);
}

let lastx = 0, lasty = 0;
let realx = 0, realy = 0;
const dragmove = (e: MouseEvent) => {
    if(e.button !== 0)
        return;

    realx += e.movementX / zoom;
    realy += e.movementY / zoom;

    const result = e.altKey ? undefined : snap({ ...node, x: realx, y: realy });

    node.x = result?.x ?? realx;
    node.y = result?.y ?? realy;
};
const dragend = (e: MouseEvent) => {
    if(e.button !== 0)
        return;
    
    window.removeEventListener('mousemove', dragmove);
    window.removeEventListener('mouseup', dragend);

    emit('move', node.id, node.x - lastx, node.y - lasty);
};
const dragstart = (e: MouseEvent) => {
    if(e.button !== 0)
        return;
    
    lastx = node.x, lasty = node.y;
    realx = node.x, realy = node.y;

    window.addEventListener('mousemove', dragmove, { passive: true });
    window.addEventListener('mouseup', dragend, { passive: true });
};

defineExpose({ unselect, dom, id: node.id });

const style = computed(() => {
    return node.color ? node.color?.class ? 
        { bg: `bg-light-${node.color?.class} dark:bg-dark-${node.color?.class}`, border: `border-light-${node.color?.class} dark:border-dark-${node.color?.class}`, outline: `outline-light-${node.color?.class} dark:outline-dark-${node.color?.class}` } : 
        { bg: `bg-colored`, border: `border-[color:var(--canvas-color)]`, outline: `outline-[color:var(--canvas-color)]` } : 
        { border: `border-light-40 dark:border-dark-40`, bg: `bg-light-40 dark:bg-dark-40`, outline: `outline-light-40 dark:outline-dark-40` }
});
</script>