"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ptr16DeviceTable = exports.DeviceTable = exports.EmptyDeviceTable = exports.DeviceDeltaBits = void 0;
const bin_composite_types_1 = require("@ot-builder/bin-composite-types");
const bin_util_1 = require("@ot-builder/bin-util");
var DeltaFormat;
(function (DeltaFormat) {
    DeltaFormat[DeltaFormat["LOCAL_2_BIT_DELTAS"] = 1] = "LOCAL_2_BIT_DELTAS";
    DeltaFormat[DeltaFormat["LOCAL_4_BIT_DELTAS"] = 2] = "LOCAL_4_BIT_DELTAS";
    DeltaFormat[DeltaFormat["LOCAL_8_BIT_DELTAS"] = 3] = "LOCAL_8_BIT_DELTAS";
    // eslint-disable-next-line @typescript-eslint/no-duplicate-enum-values
    DeltaFormat[DeltaFormat["FORMAT_MASK"] = 3] = "FORMAT_MASK";
    DeltaFormat[DeltaFormat["VARIATION_INDEX"] = 32768] = "VARIATION_INDEX";
    DeltaFormat[DeltaFormat["Reserved"] = 32764] = "Reserved";
})(DeltaFormat || (DeltaFormat = {}));
function deviceDeltaSizeFromFormat(format) {
    switch (format & DeltaFormat.FORMAT_MASK) {
        case DeltaFormat.LOCAL_8_BIT_DELTAS:
            return 8;
        case DeltaFormat.LOCAL_4_BIT_DELTAS:
            return 4;
        case DeltaFormat.LOCAL_2_BIT_DELTAS:
            return 2;
        default:
            return 0;
    }
}
exports.DeviceDeltaBits = {
    ...(0, bin_util_1.Read)((bp, ppemMin, ppemMax, deltaFormat) => {
        const deltaBits = deviceDeltaSizeFromFormat(deltaFormat);
        if (!deltaBits)
            return { variation: 0 };
        const deltasPerWord = 16 / deltaBits;
        let ppem = ppemMin, word = 0, hasDelta = false;
        const deltas = [];
        do {
            if ((ppem - ppemMin) % deltasPerWord === 0)
                word = bp.uint16();
            deltas[ppem] = (word << 16) >> (32 - deltaBits);
            if (deltas[ppem])
                hasDelta = true;
            word = (word << deltaBits) & 0xffff;
            ppem++;
        } while (ppem <= ppemMax);
        if (hasDelta) {
            return { variation: 0, deviceDeltas: deltas };
        }
        else {
            return { variation: 0 };
        }
    }),
    ...(0, bin_util_1.Write)((bb, deltas, ppemMin, ppemMax, deltaFormat) => {
        const deltaBits = deviceDeltaSizeFromFormat(deltaFormat);
        const deltasPerWord = 16 / deltaBits;
        let word = 0, flushed = false;
        // Pack deltas into 16-bit words
        for (let ppem = ppemMin; ppem <= ppemMax; ppem++) {
            const seg = deltas[ppem] & ((1 << deltaBits) - 1);
            const shift = 16 - ((ppem - ppemMin) % deltasPerWord) * deltaBits - deltaBits;
            word = word | (seg << shift);
            flushed = false;
            if ((ppem + 1 - ppemMin) % deltasPerWord === 0) {
                bb.uint16(word);
                word = 0;
                flushed = true;
            }
        }
        if (!flushed)
            bb.uint16(word);
    })
};
exports.EmptyDeviceTable = (0, bin_util_1.Write)(frag => {
    frag.uint16(1);
    frag.uint16(1);
    frag.uint16(DeltaFormat.LOCAL_8_BIT_DELTAS);
    frag.uint16(0);
});
function decideDeltaFormat(deltas) {
    let format = 0;
    let ppemMin = -1, ppemMax = -1;
    for (let ppem = 0; ppem < deltas.length; ppem++) {
        const d = deltas[ppem] || 0;
        if (d !== 0) {
            if (format < DeltaFormat.LOCAL_2_BIT_DELTAS) {
                format = DeltaFormat.LOCAL_2_BIT_DELTAS;
            }
            if (ppemMin < 0)
                ppemMin = ppem;
            ppemMax = ppem;
        }
        if ((d < -2 || d > 1) && format < DeltaFormat.LOCAL_4_BIT_DELTAS) {
            format = DeltaFormat.LOCAL_4_BIT_DELTAS;
        }
        if ((d < -8 || d > 7) && format < DeltaFormat.LOCAL_8_BIT_DELTAS) {
            format = DeltaFormat.LOCAL_8_BIT_DELTAS;
        }
    }
    return { format, ppemMin, ppemMax };
}
exports.DeviceTable = {
    ...(0, bin_util_1.Read)((bp, ivs) => {
        const arg0 = bp.uint16();
        const arg1 = bp.uint16();
        const deltaFormat = bp.uint16();
        if (deltaFormat & DeltaFormat.VARIATION_INDEX) {
            if (!ivs)
                return { variation: 0 };
            else
                return { variation: ivs.queryValue(arg0, arg1) };
        }
        else {
            return bp.next(exports.DeviceDeltaBits, arg0, arg1, deltaFormat);
        }
    }),
    ...(0, bin_util_1.Write)((bb, v, ivs) => {
        if (v.deviceDeltas) {
            const { format, ppemMin, ppemMax } = decideDeltaFormat(v.deviceDeltas);
            if (!format || ppemMin < 0 || ppemMax < 0 || ppemMin > ppemMax) {
                bb.push(exports.EmptyDeviceTable, undefined);
            }
            else {
                // write header and format
                bb.uint16(ppemMin);
                bb.uint16(ppemMax);
                bb.uint16(format);
                bb.push(exports.DeviceDeltaBits, v.deviceDeltas, ppemMin, ppemMax, format);
            }
        }
        else if (ivs) {
            const a = ivs.valueToInnerOuterID(v.variation);
            if (!a) {
                bb.push(exports.EmptyDeviceTable, undefined);
            }
            else {
                bb.uint16(a.outer);
                bb.uint16(a.inner);
                bb.uint16(DeltaFormat.VARIATION_INDEX);
            }
        }
        else {
            bb.push(exports.EmptyDeviceTable, undefined);
        }
    })
};
exports.Ptr16DeviceTable = (0, bin_composite_types_1.NullablePtr16)(exports.DeviceTable);
//# sourceMappingURL=device-table.js.map