

// direction: 1 one step down, -1 one step up
export function reorderListItem<T>(list: T[], index: number, direction: number) {
    let otherIndex = index + direction;
    let item = list[index];
    let otherItem = list[otherIndex];
    list[index] = otherItem;
    list[otherIndex] = item;
}

export function removeListItem<T>(list: T[], item: T) {
    let idx = list.indexOf(item)
    if (idx !== -1) {
        list.splice(idx, 1)
    }
}

export function toggleListItem<T>(list: T[], item: T) {
    let idx = list.indexOf(item)
    if (idx !== -1) {
        list.splice(idx, 1)
    } else {
        list.push(item)
    }
}

const CURSOR_DIV_ID = 'global-cursor'

export function setGlobalCursor(cursorStyle: string) {
    // create a transparent div with a specific id
    // if div already exists, remove it and recrate it.
    // I know it's dumb but at least it's more reliable
    {
        let existing = document.getElementById(CURSOR_DIV_ID);
        if (existing) {
            existing.remove();
        }
    }

    let div = document.createElement('div');
    div.id = CURSOR_DIV_ID;

    div.style.position = 'fixed';
    div.style.top = '0';
    div.style.left = '0';
    div.style.right = '0';
    div.style.bottom = '0';

    div.style.background = 'transparent';
    div.style.zIndex = '10000000000';

    div.style.cursor = cursorStyle;

    document.body.appendChild(div);
}

export function unsetGlobalCursor() {
    let existing = document.getElementById(CURSOR_DIV_ID);
    if (existing) {
        existing.remove();
    }
}


export interface ArrayMove {
    src: number;
    dest: number;
}

function arraySwap<T>(array: T[], swap: ArrayMove) {
    let src_item = array[swap.src];
    let dest_item = array[swap.dest];
    array[swap.src] = dest_item;
    array[swap.dest] = src_item;
}

// move item in array without resizing array
// sounds dumb but prevents flicker when array is a list of child nodes
export function arrayBubbleMove<T>(array: T[], swap: ArrayMove) {
    if (swap.src < swap.dest) {
        for (let i = swap.src; i < swap.dest - 1; i++) {
            arraySwap(array, { src: i, dest: i + 1 })
        }
    } else {
        for (let i = swap.src; i > swap.dest; i--) {
            arraySwap(array, { src: i, dest: i - 1 })
        }
    }
}

export function removePrefix(s: string, prefix: string): string {
    if (s.startsWith(prefix)) {
        return s.substring(prefix.length);
    } else {
        return s;
    }
}

export function intUrlArg(route: string, prefix: string, fallback: number = 0): number {
    let arg = removePrefix(route, prefix)
    return parseIntOrFallback(arg, fallback);
}

export function numberOrFallback(numValue: number, fallback: number = 0): number {
    if (Number.isSafeInteger(numValue)) {
        return numValue
    } else {
        return fallback
    }
}

export function parseIntOrFallback(s: string | null, fallback: number = 0): number {
    if (s === null) {
        return fallback
    }
    return numberOrFallback(parseInt(s), fallback)
}

export function urlParams(path: string): URLSearchParams {
    let idx = path.indexOf('?')
    if (idx == 0) {
        return new URLSearchParams()
    } else {
        return new URLSearchParams(path.substring(idx))
    }
}

export function intParam(p: URLSearchParams, name: string, fallback: number = 0): number {
    let strValue = p.get(name)
    return parseIntOrFallback(p.get(name), fallback)
}

export function measure(repeat: number, fn: Function) {
    let t1 = performance.now()
    for(let i = 0; i < repeat; i++) {
        fn()
    }
    let t2 = performance.now()
    let dur = t2 - t1
    return dur.toFixed(2) + "ms"
}

export function timeout(ms: number): Promise<void> {
    if (ms <= 0) {
        return Promise.resolve()
    }
    return new Promise(r => setTimeout(r, ms))
}

export function timeoutUntil(time: number) {
    return timeout(time - Date.now())
}

export async function minimumWait<T>(ms: number, p: Promise<T>): Promise<T> {
    let start = Date.now()
    let result = await p;
    await timeoutUntil(start + ms)
    return result
}

export function assert(t: unknown, msg: string) {
    if (t === null) {
        console.error("assertion error:", msg)
        throw new Error("Assertion Error")
    }
}
