"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GsubLigatureWriter = exports.GsubLigatureReader = 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 general_1 = require("../gsub-gpos-shared/general");
const coverage_1 = require("../shared/coverage");
const SubtableFormat1 = {
    read(view, lookup, context) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported(`LigatureSubstFormat1`, format, 1);
        const coverage = view.next(coverage_1.Ptr16GidCoverage);
        const ligatureSetCount = view.uint16();
        errors_1.Assert.SizeMatch(`LigatureSubstFormat1::ligatureSetCount`, ligatureSetCount, coverage.length);
        for (const gidFirst of coverage) {
            const ligatureSet = view.ptr16();
            const ligatureCount = ligatureSet.uint16();
            for (let lsid = 0; lsid < ligatureCount; lsid++) {
                const ligature = ligatureSet.ptr16();
                const gidLigatureGlyph = ligature.uint16();
                const componentCount = ligature.uint16();
                const componentGlyphIDs = ligature.array(componentCount - 1, primitive_1.UInt16);
                lookup.mapping.push({
                    from: [gidFirst, ...componentGlyphIDs].map(gid => context.gOrd.at(gid)),
                    to: context.gOrd.at(gidLigatureGlyph)
                });
            }
        }
    },
    write(frag, mapping, ctx) {
        const { gidList, values } = coverage_1.CovUtils.splitListFromMap(mapping, ctx.gOrd);
        frag.uint16(1);
        frag.push(coverage_1.Ptr16GidCoverage, gidList, ctx.trick);
        frag.uint16(values.length);
        for (const ligSet of values) {
            const fLigSet = frag.ptr16New();
            fLigSet.uint16(ligSet.length);
            for (const [rests, to] of ligSet) {
                const fLig = fLigSet.ptr16New();
                fLig.uint16(ctx.gOrd.reverse(to))
                    .uint16(rests.length + 1)
                    .array(primitive_1.UInt16, rests.map(g => ctx.gOrd.reverse(g)));
            }
        }
    }
};
class GsubLigatureReader {
    createLookup() {
        return new ot_layout_1.Gsub.Ligature();
    }
    parseSubtable(view, lookup, context) {
        const format = view.lift(0).uint16();
        switch (format) {
            case 1:
                view.next(SubtableFormat1, lookup, context);
                break;
            default:
                throw errors_1.Errors.FormatNotSupported(`Ligature Substitution Subtable`, format);
        }
    }
}
exports.GsubLigatureReader = GsubLigatureReader;
class State {
    constructor() {
        this.mapping = new Map();
        this.size = primitive_1.UInt16.size * 4;
    }
    tryAddMapping(firstComponent, [restComponents, to]) {
        const deltaSize = this.mapping.has(firstComponent)
            ? primitive_1.UInt16.size * (3 + restComponents.length)
            : primitive_1.UInt16.size * (4 + coverage_1.MaxCovItemWords + restComponents.length);
        if (this.size + deltaSize > general_1.SubtableSizeLimit)
            return false;
        let arr = this.mapping.get(firstComponent);
        if (!arr) {
            arr = [];
            this.mapping.set(firstComponent, arr);
        }
        arr.push([restComponents, to]);
        this.size += deltaSize;
        return true;
    }
}
class GsubLigatureWriter {
    canBeUsed(l) {
        return l.type === ot_layout_1.Gsub.LookupType.Ligature;
    }
    getLookupType() {
        return 4;
    }
    getLookupTypeSymbol() {
        return ot_layout_1.Gsub.LookupType.Ligature;
    }
    flush(frags, state, ctx) {
        if (!state.mapping.size)
            return;
        frags.push(bin_util_1.Frag.from(SubtableFormat1, state.mapping, ctx));
    }
    createSubtableFragments(lookup, ctx) {
        let state = new State();
        const frags = [];
        // Iterate the path map using post-root order to make sure that longer ligatures
        // is processed first.
        for (const { from, to } of lookup.mapping) {
            if (from.length < 1)
                continue; // meaningless, skip
            ctx.stat.setContext(from.length); // Stat
            const [g0, ...gCont] = from;
            if (state.tryAddMapping(g0, [gCont, to]))
                continue;
            this.flush(frags, state, ctx);
            state = new State();
            if (!state.tryAddMapping(g0, [gCont, to]))
                throw errors_1.Errors.Unreachable();
        }
        this.flush(frags, state, ctx);
        return frags;
    }
}
exports.GsubLigatureWriter = GsubLigatureWriter;
//# sourceMappingURL=gsub-ligature.js.map