"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GposSingleWriter = exports.GposSingleReader = void 0;
const bin_util_1 = require("@ot-builder/bin-util");
const errors_1 = require("@ot-builder/errors");
const ot_layout_1 = require("@ot-builder/ot-layout");
const primitive_1 = require("@ot-builder/primitive");
const cfg_1 = require("../cfg");
const general_1 = require("../gsub-gpos-shared/general");
const coverage_1 = require("../shared/coverage");
const gpos_adjust_1 = require("../shared/gpos-adjust");
const SubtableFormat1 = {
    read(view, lookup, context) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported(`SinglePosFormat1`, format, 1);
        const coverage = view.ptr16().next(coverage_1.GidCoverage);
        const valueFormat = view.uint16();
        const adj = view.next(gpos_adjust_1.GposAdjustment, valueFormat, context.ivs);
        for (const gid of coverage) {
            const gSource = context.gOrd.at(gid);
            if (lookup.adjustments.has(gSource))
                continue;
            lookup.adjustments.set(gSource, adj);
        }
    },
    write(frag, adj, data, ctx) {
        const fmt = gpos_adjust_1.GposAdjustment.decideFormat(adj);
        frag.uint16(1);
        frag.push(coverage_1.Ptr16GidCoverage, data, ctx.trick);
        frag.uint16(fmt);
        frag.push(gpos_adjust_1.GposAdjustment, adj, fmt, ctx.ivs);
    }
};
const SubtableFormat2 = {
    read(view, lookup, context) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported(`SinglePosFormat2`, format, 2);
        const coverage = view.ptr16().next(coverage_1.GidCoverage);
        const valueFormat = view.uint16();
        const glyphCount = view.uint16();
        if (!valueFormat) {
            for (const gid of coverage) {
                const gSource = context.gOrd.at(gid);
                lookup.adjustments.set(gSource, ot_layout_1.Gpos.ZeroAdjustment);
            }
        }
        else {
            errors_1.Assert.SizeMatch(`SinglePosFormat2::glyphCount`, glyphCount, coverage.length);
            for (const gid of coverage) {
                const adj = view.next(gpos_adjust_1.GposAdjustment, valueFormat, context.ivs);
                const gSource = context.gOrd.at(gid);
                if (lookup.adjustments.has(gSource))
                    continue;
                lookup.adjustments.set(gSource, adj);
            }
        }
    },
    write(frag, data, fmt, ctx) {
        frag.uint16(2);
        frag.push(coverage_1.Ptr16GidCoverage, coverage_1.CovUtils.gidListFromAuxMap(data), ctx.trick);
        frag.uint16(fmt);
        frag.uint16(data.length);
        frag.array(gpos_adjust_1.GposAdjustment, coverage_1.CovUtils.valueListFromAuxMap(data), fmt, ctx.ivs);
    }
};
class GposSingleReader {
    createLookup() {
        return new ot_layout_1.Gpos.Single();
    }
    parseSubtable(view, lookup, context) {
        const format = view.lift(0).uint16();
        switch (format) {
            case 1:
                view.next(SubtableFormat1, lookup, context);
                break;
            case 2:
                view.next(SubtableFormat2, lookup, context);
                break;
            default:
                throw errors_1.Errors.FormatNotSupported(`Single Positioning Subtable`, format);
        }
    }
}
exports.GposSingleReader = GposSingleReader;
class GsubSingleWriterState {
    constructor() {
        this.mappings = new Map();
    }
    addRecord(gid, adj, ctx) {
        const h = gpos_adjust_1.GposAdjustment.hash(adj, ctx.ivs);
        if (!h)
            return; // omit 0
        let a = this.mappings.get(h);
        if (!a) {
            a = [adj, []];
            this.mappings.set(h, a);
        }
        a[1].push(gid);
    }
    collectJagged(singleSubtable) {
        const out = [];
        for (const [h, [adj, gids]] of this.mappings) {
            if (singleSubtable || gids.length < 8) {
                this.mappings.delete(h);
                for (const gid of gids)
                    out.push([gid, adj]);
            }
        }
        return out;
    }
}
const MaxUniformItems = Math.floor((general_1.SubtableSizeLimit - primitive_1.UInt16.size * 16) / (primitive_1.UInt16.size * coverage_1.MaxCovItemWords));
class GposSingleWriter {
    canBeUsed(l) {
        return l.type === ot_layout_1.Gpos.LookupType.Single;
    }
    getLookupType() {
        return 1;
    }
    getLookupTypeSymbol() {
        return ot_layout_1.Gpos.LookupType.Single;
    }
    pickJaggedData(jagged) {
        let fmt = 0;
        for (const [gid, adj] of jagged)
            fmt |= gpos_adjust_1.GposAdjustment.decideFormat(adj);
        let size = primitive_1.UInt16.size * 4, picks = 0;
        for (const [gid, adj] of jagged) {
            const dSize = primitive_1.UInt16.size * coverage_1.MaxCovItemWords + gpos_adjust_1.GposAdjustment.measure(adj, fmt);
            if (size + dSize > general_1.SubtableSizeLimit)
                break;
            size += dSize;
            picks += 1;
        }
        const data = coverage_1.CovUtils.sortAuxMap([...jagged].slice(0, picks));
        return { fmt, data };
    }
    buildJagged(frags, jagged, ctx) {
        const { fmt, data } = this.pickJaggedData(jagged);
        frags.push(bin_util_1.Frag.from(SubtableFormat2, data, fmt, ctx));
        return data.length;
    }
    buildUniform(frags, adj, gids, ctx) {
        const data = coverage_1.CovUtils.sortGidList([...gids].slice(0, MaxUniformItems));
        frags.push(bin_util_1.Frag.from(SubtableFormat1, adj, data, ctx));
        return data.length;
    }
    createSubtableFragments(lookup, ctx) {
        const singleLookup = !!(ctx.trick & cfg_1.LookupWriteTrick.AvoidBreakSubtable);
        const st = new GsubSingleWriterState();
        for (const [from, to] of lookup.adjustments) {
            st.addRecord(ctx.gOrd.reverse(from), to, ctx);
        }
        const frags = [];
        // jagged
        const jagged = st.collectJagged(singleLookup);
        while (jagged.length) {
            const len = this.buildJagged(frags, jagged, ctx);
            jagged.splice(0, len);
        }
        // flat
        for (const [gidDiff, [adj, gids]] of st.mappings) {
            if (gids === null || gids === void 0 ? void 0 : gids.length) {
                while (gids.length) {
                    const len = this.buildUniform(frags, adj, gids, ctx);
                    gids.splice(0, len);
                }
            }
        }
        return frags;
    }
}
exports.GposSingleWriter = GposSingleWriter;
//# sourceMappingURL=gpos-single.js.map