"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GsubSingleWriter = exports.GsubSingleReader = 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 SubtableFormat1 = {
    read(view, lookup, context) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported(`SingleSubstFormat1`, format, 1);
        const coverage = view.ptr16().next(coverage_1.GidCoverage);
        const deltaGlyphId = view.int16();
        for (const gid of coverage) {
            const gSource = context.gOrd.at(gid);
            if (lookup.mapping.has(gSource))
                continue;
            lookup.mapping.set(gSource, context.gOrd.at((gid + deltaGlyphId + 0x20000) % 0x10000));
        }
    },
    write(frag, gidDiff, data, ctx) {
        frag.uint16(1);
        frag.push(coverage_1.Ptr16GidCoverage, coverage_1.CovUtils.gidListFromAuxMap(data), ctx.trick);
        frag.uint16(gidDiff);
    }
};
const SubtableFormat2 = {
    read(view, lookup, context) {
        const format = view.uint16();
        errors_1.Assert.FormatSupported(`SingleSubstFormat2`, format, 2);
        const coverage = view.ptr16().next(coverage_1.GidCoverage);
        const glyphCount = view.uint16();
        errors_1.Assert.SizeMatch(`SingleSubstFormat2::glyphCount`, glyphCount, coverage.length);
        for (const gid of coverage) {
            const substituteGlyphID = view.uint16();
            const gSource = context.gOrd.at(gid);
            if (lookup.mapping.has(gSource))
                continue;
            lookup.mapping.set(gSource, context.gOrd.at(substituteGlyphID));
        }
    },
    write(frag, data, ctx) {
        frag.uint16(2);
        frag.push(coverage_1.Ptr16GidCoverage, coverage_1.CovUtils.gidListFromAuxMap(data), ctx.trick);
        frag.uint16(data.length);
        frag.array(primitive_1.UInt16, coverage_1.CovUtils.valueListFromAuxMap(data));
    }
};
class GsubSingleReader {
    createLookup() {
        return new ot_layout_1.Gsub.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 Substitution Subtable`, format);
        }
    }
}
exports.GsubSingleReader = GsubSingleReader;
class GsubSingleWriterState {
    constructor() {
        this.mappings = new Map();
    }
    addGidDiff(from, to) {
        const diff = (to - from + 0x20000) % 0x10000;
        let arr = this.mappings.get(diff);
        if (!arr) {
            arr = [];
            this.mappings.set(diff, arr);
        }
        arr.push([from, to]);
    }
    collectJagged(singleLookup) {
        const out = [];
        for (const [diff, pairs] of this.mappings) {
            if (singleLookup || pairs.length < 8) {
                this.mappings.delete(diff);
                for (const p of pairs)
                    out.push(p);
            }
        }
        return out;
    }
}
const MaxJaggedItems = Math.floor((general_1.SubtableSizeLimit - primitive_1.UInt16.size * 16) / ((coverage_1.MaxCovItemWords + 1) * primitive_1.UInt16.size));
const MaxUniformItems = Math.floor((general_1.SubtableSizeLimit - primitive_1.UInt16.size * 16) / (coverage_1.MaxCovItemWords * primitive_1.UInt16.size));
class GsubSingleWriter {
    canBeUsed(l) {
        return l.type === ot_layout_1.Gsub.LookupType.Single;
    }
    getLookupType() {
        return 1;
    }
    getLookupTypeSymbol() {
        return ot_layout_1.Gsub.LookupType.Single;
    }
    buildJagged(frags, jagged, ctx) {
        const data = coverage_1.CovUtils.sortAuxMap([...jagged].slice(0, MaxJaggedItems));
        frags.push(bin_util_1.Frag.from(SubtableFormat2, data, ctx));
        return data.length;
    }
    buildUniform(frags, gidDiff, mappings, ctx) {
        const data = coverage_1.CovUtils.sortAuxMap([...mappings].slice(0, MaxUniformItems));
        frags.push(bin_util_1.Frag.from(SubtableFormat1, gidDiff, 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.mapping) {
            st.addGidDiff(ctx.gOrd.reverse(from), ctx.gOrd.reverse(to));
        }
        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, mappings] of st.mappings) {
            if (mappings === null || mappings === void 0 ? void 0 : mappings.length) {
                while (mappings.length) {
                    const len = this.buildUniform(frags, gidDiff, mappings, ctx);
                    mappings.splice(0, len);
                }
            }
        }
        return frags;
    }
}
exports.GsubSingleWriter = GsubSingleWriter;
//# sourceMappingURL=gsub-single.js.map