"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ptr16GidCoverage = exports.NullablePtr16GidCoverage = exports.GidCoverage = exports.Ptr16GlyphCoverage = exports.NullablePtr16GlyphCoverage = exports.GlyphCoverage = exports.CovUtils = exports.MaxCovItemWords = void 0;
const bin_composite_types_1 = require("@ot-builder/bin-composite-types");
const bin_util_1 = require("@ot-builder/bin-util");
const errors_1 = require("@ot-builder/errors");
const cfg_1 = require("../cfg");
exports.MaxCovItemWords = 3;
var CovUtils;
(function (CovUtils) {
    function byGID(a, b) {
        return a[0] - b[0];
    }
    function byNum(a, b) {
        return a - b;
    }
    function sortAuxMap(axm) {
        return [...axm].sort(byGID);
    }
    CovUtils.sortAuxMap = sortAuxMap;
    function sortGidList(gids) {
        return gids.sort(byNum);
    }
    CovUtils.sortGidList = sortGidList;
    function gidListFromAuxMap(axm) {
        const ans = [];
        for (const [gid, ax] of axm)
            ans.push(gid);
        return ans;
    }
    CovUtils.gidListFromAuxMap = gidListFromAuxMap;
    function valueListFromAuxMap(axm) {
        const ans = [];
        for (const [gid, ax] of axm)
            ans.push(ax);
        return ans;
    }
    CovUtils.valueListFromAuxMap = valueListFromAuxMap;
    function auxMapFromMap(mapping, gOrd) {
        const answer = [];
        for (const [g, t] of mapping)
            answer.push([gOrd.reverse(g), t]);
        answer.sort(byGID);
        return answer;
    }
    CovUtils.auxMapFromMap = auxMapFromMap;
    function auxMapFromMapExcl(mapping, gOrd, exclude) {
        const answer = [];
        for (const [g, t] of mapping)
            if (!exclude.has(g))
                answer.push([gOrd.reverse(g), t]);
        answer.sort(byGID);
        return answer;
    }
    CovUtils.auxMapFromMapExcl = auxMapFromMapExcl;
    function auxMapFromExtractor(mapping, gOrd, extract) {
        const answer = [];
        for (const t of mapping)
            answer.push([gOrd.reverse(extract(t)), t]);
        answer.sort(byGID);
        return answer;
    }
    CovUtils.auxMapFromExtractor = auxMapFromExtractor;
    function* mapFromNumbers(gids, values, gOrd) {
        errors_1.Assert.SizeMatch("cov-map length", values.length, gids.length);
        for (let item = 0; item < gids.length; item++) {
            yield [gOrd.at(gids[item]), values[item]];
        }
    }
    CovUtils.mapFromNumbers = mapFromNumbers;
    function splitListFromMap(mapping, gOrd) {
        const gidList = [];
        const values = [];
        const axm = auxMapFromMap(mapping, gOrd);
        for (const [gid, t] of axm) {
            gidList.push(gid);
            values.push(t);
        }
        return { gidList, values };
    }
    CovUtils.splitListFromMap = splitListFromMap;
    function* glyphsFromGidList(gids, gOrd) {
        for (const gid of gids)
            yield gOrd.at(gid);
    }
    CovUtils.glyphsFromGidList = glyphsFromGidList;
    function glyphSetFromGidList(gids, gOrd) {
        const s = new Set();
        for (const gid of gids)
            s.add(gOrd.at(gid));
        return s;
    }
    CovUtils.glyphSetFromGidList = glyphSetFromGidList;
    function gidListFromGlyphSet(glyphs, gOrd) {
        const gidSet = new Set();
        for (const glyph of glyphs)
            gidSet.add(gOrd.reverse(glyph));
        return [...gidSet].sort((a, b) => a - b);
    }
    CovUtils.gidListFromGlyphSet = gidListFromGlyphSet;
})(CovUtils || (exports.CovUtils = CovUtils = {}));
exports.GlyphCoverage = {
    read(view, gOrd) {
        const cov = view.next(exports.GidCoverage);
        return CovUtils.glyphSetFromGidList(cov, gOrd);
    },
    write(frag, gs, gOrd, trick = 0) {
        const gl = CovUtils.gidListFromGlyphSet(gs, gOrd);
        frag.push(exports.GidCoverage, gl, trick);
    }
};
exports.NullablePtr16GlyphCoverage = (0, bin_composite_types_1.NullablePtr16)(exports.GlyphCoverage);
exports.Ptr16GlyphCoverage = (0, bin_composite_types_1.NonNullablePtr16)(exports.GlyphCoverage);
exports.GidCoverage = {
    ...(0, bin_util_1.Read)(view => {
        const format = view.lift(0).uint16();
        switch (format) {
            case 1:
                return view.next(OtGidCoverageFormat1);
            case 2: {
                return view.next(OtGidCoverageFormat2);
            }
            default:
                throw errors_1.Errors.FormatNotSupported("coverage", format);
        }
    }),
    ...(0, bin_util_1.Write)((frag, gidList, trick = 0) => {
        if (trick & cfg_1.LookupWriteTrick.UseFlatCoverage) {
            frag.push(OtGidCoverageFormat1, gidList);
        }
        else if (trick & cfg_1.LookupWriteTrick.UseFastCoverage) {
            const collector = new CoverageRunCollector();
            for (let item = 0; item < gidList.length; item++) {
                collector.update(gidList[item], item);
            }
            collector.end();
            if (collector.runs.length < gidList.length) {
                frag.push(OtGidCoverageFormat2FromCollector, collector);
            }
            else {
                frag.push(OtGidCoverageFormat1, gidList);
            }
        }
        else {
            const format1 = bin_util_1.Frag.from(OtGidCoverageFormat1, gidList);
            const format2 = bin_util_1.Frag.from(OtGidCoverageFormat2, gidList);
            if (format2.size < format1.size) {
                frag.embed(format2);
            }
            else {
                frag.embed(format1);
            }
        }
    })
};
exports.NullablePtr16GidCoverage = (0, bin_composite_types_1.NullablePtr16)(exports.GidCoverage);
exports.Ptr16GidCoverage = (0, bin_composite_types_1.NonNullablePtr16)(exports.GidCoverage);
const OtGidCoverageFormat1 = {
    ...(0, bin_util_1.Read)(view => {
        const format = view.uint16();
        if (format !== 1)
            throw errors_1.Errors.Unreachable();
        const gids = [];
        const glyphCount = view.uint16();
        for (let index = 0; index < glyphCount; index++) {
            const gid = view.uint16();
            gids[index] = gid;
        }
        return gids;
    }),
    ...(0, bin_util_1.Write)((frag, gidList) => {
        frag.uint16(1);
        frag.uint16(gidList.length);
        let lastGID = -1;
        for (let item = 0; item < gidList.length; item++) {
            const gid = gidList[item];
            // Check the results -- should not happen
            if (gid === undefined)
                throw errors_1.Errors.Unreachable();
            if (gid < lastGID)
                throw errors_1.Errors.Unreachable();
            frag.uint16(gid);
            lastGID = gid;
        }
    })
};
class CoverageRunCollector {
    constructor() {
        this.runs = [];
        this.last = null;
    }
    start(gid, item) {
        this.last = { startGlyphID: gid, endGlyphID: gid, startCoverageIndex: item };
    }
    flush() {
        if (this.last)
            this.runs.push(this.last);
    }
    update(gid, item) {
        if (!this.last)
            this.start(gid, item);
        else if (gid !== this.last.endGlyphID + 1
            || item !== this.last.startCoverageIndex + (gid - this.last.startGlyphID)) {
            if (gid <= this.last.endGlyphID)
                throw errors_1.Errors.Unreachable();
            this.flush();
            this.start(gid, item);
        }
        else {
            this.last.endGlyphID = gid;
        }
    }
    end() {
        this.flush();
    }
}
const OtGidCoverageFormat2 = {
    ...(0, bin_util_1.Read)(view => {
        const format = view.uint16();
        if (format !== 2)
            throw errors_1.Errors.Unreachable();
        const gids = [];
        const classRangeCount = view.uint16();
        for (let ixRange = 0; ixRange < classRangeCount; ixRange++) {
            const startGlyphID = view.uint16();
            const endGlyphID = view.uint16();
            const startCoverageIndex = view.uint16();
            for (let ixGlyph = startGlyphID; ixGlyph <= endGlyphID; ixGlyph++) {
                gids[startCoverageIndex + ixGlyph - startGlyphID] = ixGlyph;
            }
        }
        return gids;
    }),
    ...(0, bin_util_1.Write)((frag, gidList) => {
        const collector = new CoverageRunCollector();
        for (let item = 0; item < gidList.length; item++) {
            collector.update(gidList[item], item);
        }
        collector.end();
        frag.push(OtGidCoverageFormat2FromCollector, collector);
    })
};
const OtGidCoverageFormat2FromCollector = {
    ...(0, bin_util_1.Write)((frag, collector) => {
        frag.uint16(2).uint16(collector.runs.length);
        for (const run of collector.runs) {
            frag.uint16(run.startGlyphID).uint16(run.endGlyphID).uint16(run.startCoverageIndex);
        }
    })
};
//# sourceMappingURL=coverage.js.map