<script>

    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);
    // PROPS
    export let list;
    export let key;
    export let ulclass;

    export let breakon;

    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>

<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: .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>

{#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}