/**
 * Throttle function
 */
export const throttle = (func: Function, timeout: number) => {
    let ready: boolean = true
    return (...args: any) => {
        if (!ready) return
        ready = false
        func(...args)
        setTimeout(() => ready = true, timeout)
    }
}


/**
 * Debounce function
 */
export const debounce = (func: Function, timeout: number) => {
    let timer: NodeJS.Timeout
    return (...args: any) => {
        clearTimeout(timer)
        timer = setTimeout(() => func(...args), timeout)
    }
}


/**
 * Split text
 * @description Split a string into words or characters
 * @returns string[]
 */
 export const splitText = (text: string, mode: string = 'words'): string[] => {
    // Split by words
    if (mode === 'words') {
        const words = text
            .replace(/\\n/g, '\n')
            .replace(/\s+/g, m => m.includes('\n') ? '\n ' : ' ')
            .trim()
            .split(' ')

        return words
    }
    // Split by chars
    else if (mode === 'chars') {
        const chars = Array.from(text).map(char => char === ' ' ? '\xa0' : char)
        return chars
    }
}


/**
 * Linear Interpolation
 */
export const lerp = (start: number, end: number, amt: number): number => {
    return (1 - amt) * start + amt * end
}


/**
 * Re-maps a number from one range to another
 * @param value the incoming value to be converted
 * @param start1 lower bound of the value's current range
 * @param stop1 upper bound of the value's current range
 * @param start2 lower bound of the value's target range
 * @param stop2 upper bound of the value's target range
 * @param [withinBounds] constrain the value to the newly mapped range
 * @return remapped number
 */
export const map = (n: number, start1: number, stop1: number, start2: number, stop2: number, withinBounds: boolean): number => {
    const value = (n - start1) / (stop1 - start1) * (stop2 - start2) + start2
    if (!withinBounds) return value
    if (start2 < stop2) {
        return clamp(value, start2, stop2)
    } else {
        return clamp(value, stop2, start2)
    }
}


/**
 * Clamp a number
 */
export const clamp = (num: number, a: number, b: number) => {
    return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b))
}



/**
 * Return a random element from an array
 */
export const getRandomElement = (array: any[]): any => {
    return ~~(array.length * Math.random())
}


/**
 * Get a DOM element's position
 */
export const getPosition = (node, scope?: HTMLElement) => {
    const root = scope || document
    let offsetTop = node.offsetTop
    let offsetLeft = node.offsetLeft
    while (node && node.offsetParent && node.offsetParent != document && node !== root && root !== node.offsetParent) {
        offsetTop += node.offsetParent.offsetTop
        offsetLeft += node.offsetParent.offsetLeft
        node = node.offsetParent
    }
    return {
        top: offsetTop,
        left: offsetLeft
    }
}