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

/**
 * Was originally written for TAA jitter sequence optimization in order to reduce maximum impulse
 * @template T
 * @param {T[]} sequence
 * @param {number} dimensions
 * @param {number} offset
 * @param {number} count
 * @param {function(address0:number, address1:number, sequence:T[]):number} cost_function
 * @param {*} [cost_function_ctx]
 */
export function optimize_cyclic_sequence_pairwise(
    sequence,
    dimensions,
    offset,
    count,
    cost_function,
    cost_function_ctx
) {

    assert.isNonNegativeInteger(dimensions, 'dimensions');
    assert.isNonNegativeInteger(offset, 'offset');
    assert.isNonNegativeInteger(count, 'count');

    assert.isFunction(cost_function, 'cost_function');

    let swaps = 0;

    let distance_max = (count - 1) >>> 1;

    function index_to_address(index) {
        const wrapped_index = (index + count) % count;
        return offset + wrapped_index * dimensions;
    }

    function move(from, to){
        const i0 = index_to_address(from);
        const i1 = index_to_address(to);

        const clip = sequence.splice(i0, dimensions);
        sequence.splice(i1, 0, ...clip);

    }

    function pair_cost(i0, i1){
        return cost_function.call(cost_function_ctx, index_to_address(i0), index_to_address(i1), sequence)
    }

    main: do {
        swaps = 0;

        for (let from = 0; from < count; from++) {

            for (let distance = -distance_max; distance <= distance_max; distance++) {
                if (distance === 0) {
                    continue;
                }

                // estimate swap benefit
                const to = from+distance;

                const removal_gain = pair_cost(from-1, from+1) - (
                    pair_cost(from-1, from) + pair_cost(from, from+1)
                );

                move(from, to);

                const insertion_gain = ( pair_cost(to-1, to) + pair_cost(to, to+1))
                    - pair_cost(to -1, to+1);

                const move_cost =insertion_gain + removal_gain;

                if (move_cost >= 0) {
                    // bad swap, undo

                    move(to, from);

                    continue;
                }

                swaps++;

                continue main;
            }
        }

    } while (swaps > 0);

}