import { FunctionCompiler } from "../function/FunctionCompiler.js";
import { assert } from "../assert.js";

/**
 * Builds an encoder/decoder to simplify process of reading/writing various parts of a 32 bit field
 * @param {number[]} bitCounts
 * @returns {{encode: function(...number):number, decode: function(value:number, result:number[]):void}}
 */
export function create32BitCodec(bitCounts) {
    assert.isArray(bitCounts, 'bitCounts');
    assert.lessThanOrEqual(bitCounts.length, 32, `at most 32 groups are supported (1 bit each), instead ${bitCounts} was requested`);

    const layout = [];

    const encoderFunctionBody = [];
    const encoderArguments = [];

    const decoderFunctionBody = [];

    let offsetCursor = 0;

    const group_count = bitCounts.length;

    for (let i = 0, numVariables = group_count; i < numVariables; i++) {
        const size = bitCounts[i];

        assert.isNonNegativeInteger(size, `size[${i}]`);
        assert.greaterThan(size, 0, `size[${i}] bust be greater than 0, instead was ${size}`);

        const offset = offsetCursor;

        layout.push({ offset, size });

        offsetCursor += size;

        const encoderArgument = 'v' + i;

        encoderArguments.push(encoderArgument);

        const bitMask = (Math.pow(2, size) - 1) << offset;

        encoderFunctionBody.push(`${i > 0 ? '|' : 'return '} ( ( ${encoderArgument} ${offset > 0 ? `<<${offset}` : ''}) & ${bitMask} ) ${i === numVariables - 1 ? ';' : ''}`);

        decoderFunctionBody.push(`result[${i}] = (value & ${bitMask}) ${offset > 0 ? `>>${offset}` : ''};`);
    }


    if (offsetCursor > 32) {
        throw new Error(
            `Only up to 32 bits are supported, ${offsetCursor} were required`
        );
    }


    const encode = FunctionCompiler.INSTANCE.compile({
        args: encoderArguments,
        code: encoderFunctionBody.join('\n')
    });
    const decode = FunctionCompiler.INSTANCE.compile({
        args: ['value', 'result'],
        code: decoderFunctionBody.join('\n')
    });

    return {
        encode,
        decode,
        layout
    };
}
