import { assert } from "../../assert.js";
import { array_swap_one } from "./array_swap_one.js";

const stack = [];

/**
 * Quicksort implementation, instead of compare operator, uses scoring function
 * @template T
 * @param {T[]|ArrayLike<number>|Uint32Array} data
 * @param {function(T):number} score_function
 * @param {*} [score_function_context]
 * @param {number} [start]
 * @param {number} [end]
 * @param {function(T[],number, number):*} [swap_operator]
 * @param {*} [swap_context]
 */
export function array_sort_quick(
    data,
    score_function, score_function_context,
    start = 0, end = data.length - 1,
    swap_operator = array_swap_one, swap_context = undefined
) {
    assert.isFunction(score_function, 'score_function');

    if (start >= end) {
        // section of 0 size, nothing to sort
        return;
    }

    let stackPointer = 2;
    let i, j;

    stack[0] = start;
    stack[1] = end;

    while (stackPointer > 0) {
        stackPointer -= 2;

        const right = stack[stackPointer + 1];
        const left = stack[stackPointer];

        i = left;
        j = right;

        const pivotIndex = (left + right) >> 1;

        const pivot = score_function.call(score_function_context, data[pivotIndex]);

        /* partition */
        while (i <= j) {
            while (score_function.call(score_function_context, data[i]) < pivot) {
                i++;
            }

            while (score_function.call(score_function_context, data[j]) > pivot) {
                j--;
            }

            if (i <= j) {

                if (i !== j) {
                    //do swap
                    swap_operator.call(swap_context, data, i, j);
                }

                i++;
                j--;
            }
        }

        /* recursion */
        if (left < j) {
            stack[stackPointer++] = left;
            stack[stackPointer++] = j;
        }
        if (i < right) {
            stack[stackPointer++] = i;
            stack[stackPointer++] = right;
        }
    }
}

