<script lang="ts">
    //@ts-nocheck
    import { quintOut } from "svelte/easing";
    import { crossfade } from "svelte/transition";
    import { flip } from "svelte/animate";
    // FLIP ANIMATION
    const [send, receive] = crossfade({
        duration: (d) => Math.sqrt(d * 200),
        fallback(node, params) {
            const style = getComputedStyle(node);
            const transform = style.transform === "none" ? "" : style.transform;
            return {
                duration: 600,
                easing: quintOut,
                css: (t) => `
					transform: ${transform} scale(${t});
					opacity: ${t}
				`,
            };
        },
    });
    // DRAG AND DROP
    let isOver = false;
    let isDragging = false;
    const getDraggedParent = (node) => {
        return node.dataset && node.dataset.index
            ? node.dataset
            : getDraggedParent(node.parentNode);
    };
    const click = (ev) => {
        dispatch("slotclick", {
            source: ev.target.parentElement.dataset.index,
            sourceItem: list[ev.target.parentElement.dataset.index],
        });
    };
    const start = (ev) => {
        isDragging = true;
        ev.dataTransfer.setData("source", ev.target.dataset.index);
    };
    const over = (ev) => {
        ev.preventDefault();
        let dragged = getDraggedParent(ev.target);
        if (isOver !== dragged.id) isOver = JSON.parse(dragged.id);
    };
    const leave = (ev) => {
        let dragged = getDraggedParent(ev.target);
        if (isOver === dragged.id) isOver = false;
    };
    const drop = (ev) => {
        isOver = false;
        ev.preventDefault();
        let dragged = getDraggedParent(ev.target);
        let from = ev.dataTransfer.getData("source");
        let to = dragged.index;
        reorder({ from, to });
        isDragging = false;
    };
    // DISPATCH REORDER
    import {
        createEventDispatcher,
        onMount,
        afterUpdate,
        beforeUpdate,
        tick,
    } from "svelte";

    const dispatch = createEventDispatcher();
    const reorder = ({ from, to }) => {
        let newList = [...list];
        let temp = newList[from];
        newList[from] = newList[to];
        newList[to] = temp;
        //newList[from] = [newList[to], (newList[to] = newList[from])][0];
        dispatch("sort", { data: newList, from: from, to: to });
    };
    // UTILS
    const getKey = (item) => (key ? item[key] : item);

    type ListItem = {
        displayTileSize: number;
        endsGroup: boolean;
        startsNewGroup: boolean;
    };
    // PROPS
    export let list: ListItem[][];
    export let key;
    export let ulclass;

    export let breakon: string | null = null;

    export let breakComponent;
    //    return `<><li class='${breakClass}' style='height:100%; grid-column: span 5;'><div style='width:100%;height:inherit;'></li>`;

    // export let breakPosition = "beforebegin";
    let breakClass = "group-break";

    const Elements = () => document.getElementsByClassName(key);

    const removeOldBreaks = (parent) => {
        if (breakon == null) return;
        if (isDragging) return;

        if (parent.length) {
            const old = parent[0].getElementsByClassName(breakClass);

            for (let i = 0; i < old.length; i = 0) {
                old[i].remove(); // removing the node from the DOM also removes it from this array (WTF??)
            }
        }
    };

    let lastCollection;

    let updatingBreakPoints = false;

    const updateBreak = () => {
        if (isDragging || updatingBreakPoints) return;
        if (breakon == null) return;

        try {
            updatingBreakPoints = true;

            let breaks = 0;
            let last = null;
            let carry = 0;
            let elements = Elements();

            for (let x = 0; x < list.length; x++) {
                if (last != null) {
                    if (list[x][breakon] > last) {
                        if (elements.length) {
                            let targetIndex = x + breaks;

                            if (
                                list.length !==
                                elements[0].children.length - breaks
                            ) {
                                if (lastCollection)
                                    if (lastCollection.length)
                                        targetIndex += lastCollection.length;
                            }
                            //  elements[0].children[targetIndex].insertAdjacentHTML(breakPosition, breakHtml(carry + 1));
                            breaks++;
                            carry = 0;
                        }
                    } else {
                        carry++;
                    }
                }

                last = list[x][breakon] > last ? list[x][breakon] : last;
            }

            lastCollection = list;
        } finally {
            updatingBreakPoints = false;
        }
    };

    //beforeUpdate(() => {updateBreak()});

    let updating = false;

    afterUpdate(async () => {
        if (isDragging) return;
        if (breakon == null) return;

        updating = true;
        removeOldBreaks(Elements());
        await tick();
        updateBreak();

        updating = false;
    });

    const getClass = () => {
        let ret = "";
        if (key) ret += ` ${key}`;
        if (ulclass) ret += ` ${ulclass}`;

        return ret;
    };
</script>

{#if list && list.length}
    <ul class={getClass()}>
        {#each list as item, index (getKey(item))}
            <li
                class:small={item.displayTileSize === 2}
                class:medium={item.displayTileSize === 3}
                class:large={item.displayTileSize === 4}
                class:startsNewGroup={item.startsNewGroup}
                class:endsGroup={item.endsGroup}
                data-index={index}
                data-item={JSON.stringify(item)}
                data-id={JSON.stringify(getKey(item))}
                draggable="true"
                on:dragstart={start}
                on:dragover={over}
                on:dragleave={leave}
                on:drop={drop}
                on:click={click}
                in:receive={{ key: getKey(item) }}
                out:send={{ key: getKey(item) }}
                animate:flip={{ duration: 300 }}
                class:over={getKey(item) === isOver}
            >
                <slot {item} {index}>
                    <p>{getKey(item)}</p>
                </slot>
            </li>
        {/each}
    </ul>
{/if}

<style>
    ul {
        display: inline-grid;
        list-style: none;
        padding: 0;
        width: 100%;
    }

    li {
        display: inline-block;
        border: 2px dotted transparent;
        transition: border 0.1s linear;
        width: 100%;
        height: 100%;
        margin: 0.5%;
    }

    .startsNewGroup {
        grid-column-start: 1;
    }

    .small {
        grid-column-end: span 1;
        grid-row: span 1;
    }

    .medium {
        grid-column-end: span 2;
        grid-row: span 1;
    }

    .large {
        grid-column-end: span 2;
        /*grid-row: span 2; large buttons are double height, but they also push the row below them down (they dont fit around eachother like css-grid) */
    }

    .over {
        border-color: rgba(48, 12, 200, 0.2);
    }
</style>
