/**
 * Merges the range defined by the provided start and end into the list of
 * existing ranges. The merge is done in place on the existing range for
 * performance and is also returned.
 *
 * @param ranges Existing range list
 * @param newRangeStart Start position of the range to merge, inclusive
 * @param newRangeEnd End position of range to merge, exclusive
 */
export default function mergeRange(ranges: [number, number][], newRangeStart: number, newRangeEnd: number): [number, number][] {
    let inRange = false;
    for (let i = 0; i < ranges.length; i++) {
        const range = ranges[i];
        if (!inRange) {
            if (newRangeEnd <= range[0]) {
                // Case 1: New range is before the search range
                ranges.splice(i, 0, [newRangeStart, newRangeEnd]);
                return ranges;
            } else if (newRangeEnd <= range[1]) {
                // Case 2: New range is either wholly contained within the
                // search range or overlaps with the front of it
                range[0] = Math.min(newRangeStart, range[0]);
                return ranges;
            } else if (newRangeStart < range[1]) {
                // Case 3: New range either wholly contains the search range
                // or overlaps with the end of it
                range[0] = Math.min(newRangeStart, range[0]);
                inRange = true;
            } else {
                // Case 4: New range starts after the search range
                continue;
            }
        } else {
            if (newRangeEnd <= range[0]) {
                // Case 5: New range extends from previous range but doesn't
                // reach the current one
                ranges[i - 1][1] = newRangeEnd;
                return ranges;
            } else if (newRangeEnd <= range[1]) {
                // Case 6: New range extends from prvious range into the
                // current range
                ranges[i - 1][1] = Math.max(newRangeEnd, range[1]);
                ranges.splice(i, 1);
                inRange = false;
                return ranges;
            } else {
                // Case 7: New range extends from previous range past the
                // end of the current range
                ranges.splice(i, 1);
                i--;
            }
        }
    }

    if (inRange) {
        // Case 8: New range extends past the last existing range
        ranges[ranges.length - 1][1] = newRangeEnd;
    } else {
        // Case 9: New range starts after the last existing range
        ranges.push([newRangeStart, newRangeEnd]);
    }

    return ranges;
}
