"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GsubGposMerger = void 0;
const ImpLib = require("@ot-builder/common-impl");
const Ot = require("@ot-builder/ot");
const feature_1 = require("./consolidate/feature");
const utils_1 = require("./utils");
class GsubGposMerger {
    constructor(variationDimensions, preferred, less, preferredGdef, lessGdef) {
        this.variationDimensions = variationDimensions;
        this.preferred = preferred;
        this.less = less;
        this.preferredGdef = preferredGdef;
        this.lessGdef = lessGdef;
        this.featureMergingPlans = new Map();
        this.featureVariationCollection = new Map();
        this.fordPreferred = ImpLib.Order.fromList("Features", preferred.features);
        this.fordLess = ImpLib.Order.fromList("Features", less.features);
    }
    resolve() {
        const lookups = this.getLookups();
        const scripts = this.mergeScriptList();
        for (const plan of this.featureMergingPlans.values())
            plan.resolve();
        const features = Array.from(this.featureMergingPlans.values()).map(p => p.result);
        const featureVariations = Array.from(this.featureVariationCollection.values());
        return { scripts, features, lookups, featureVariations };
    }
    getLookups() {
        const result = [];
        for (const lookup of this.preferred.lookups) {
            result.push(lookup);
            if (this.preferredGdef && this.lessGdef) {
                this.amendIgnoreSet(lookup, this.preferredGdef, this.lessGdef);
            }
        }
        for (const lookup of this.less.lookups) {
            result.push(lookup);
            if (this.preferredGdef && this.lessGdef) {
                this.amendIgnoreSet(lookup, this.lessGdef, this.preferredGdef);
            }
        }
        return [...this.preferred.lookups, ...this.less.lookups];
    }
    amendIgnoreSet(lookup, currentGdef, counterGdef) {
        if (!lookup.ignoreGlyphs)
            return;
        if (!currentGdef.glyphClassDef || !counterGdef.glyphClassDef)
            return;
        let hasBase = false, hasLigature = false, hasMark = false;
        for (const [g, c] of currentGdef.glyphClassDef) {
            if (!lookup.ignoreGlyphs.has(g))
                continue;
            switch (c) {
                case Ot.Gdef.GlyphClass.Base:
                    hasBase = true;
                    break;
                case Ot.Gdef.GlyphClass.Ligature:
                    hasLigature = true;
                    break;
                case Ot.Gdef.GlyphClass.Mark:
                    hasMark = true;
                    break;
            }
        }
        for (const [g, c] of counterGdef.glyphClassDef) {
            switch (c) {
                case Ot.Gdef.GlyphClass.Base:
                    if (hasBase)
                        lookup.ignoreGlyphs.add(g);
                    break;
                case Ot.Gdef.GlyphClass.Ligature:
                    if (hasLigature)
                        lookup.ignoreGlyphs.add(g);
                    break;
                case Ot.Gdef.GlyphClass.Mark:
                    if (hasMark)
                        lookup.ignoreGlyphs.add(g);
                    break;
            }
        }
    }
    mergeScriptList() {
        return (0, utils_1.mergeMapAlt)(this.preferred.scripts, this.less.scripts, this.mergeScript.bind(this));
    }
    mergeScript(preferred, less) {
        if (!preferred)
            preferred = { defaultLanguage: null, languages: new Map() };
        if (!less)
            less = { defaultLanguage: null, languages: new Map() };
        return {
            defaultLanguage: this.mergeLanguage(preferred.defaultLanguage, less.defaultLanguage),
            languages: (0, utils_1.mergeMapAlt)(preferred.languages, less.languages, this.mergeLanguage.bind(this))
        };
    }
    mergeLanguage(preferred, less) {
        if (!preferred)
            preferred = { requiredFeature: null, features: [] };
        if (!less)
            less = { requiredFeature: null, features: [] };
        return {
            requiredFeature: this.mergeRequiredFeature(preferred.requiredFeature, less.requiredFeature),
            features: this.combineFeatureList([...preferred.features, ...less.features])
        };
    }
    mergeRequiredFeature(preferred, less) {
        if (!preferred) {
            if (less)
                return this.pushMergingPlans(less.tag, [less]);
            else
                return null;
        }
        if (!less)
            return this.pushMergingPlans(preferred.tag, [preferred]);
        if (preferred.tag !== less.tag) {
            throw new Error(`Required feature cannot be merged: tag ${preferred.tag} <> ${less.tag}.`);
        }
        return this.pushMergingPlans(preferred.tag, [preferred, less]);
    }
    combineFeatureList(features) {
        const tagToFeatureListMap = new Map();
        for (const feature of [...features]) {
            let tfl = tagToFeatureListMap.get(feature.tag);
            if (!tfl) {
                tfl = [];
                tagToFeatureListMap.set(feature.tag, tfl);
            }
            tfl.push(feature);
        }
        const results = [];
        for (const [tag, tfl] of tagToFeatureListMap) {
            const plan = this.pushMergingPlans(tag, tfl);
            results.push(plan);
        }
        return results;
    }
    pushMergingPlans(tag, featureList) {
        const plan = new feature_1.FeatureConsolidator(this, tag, featureList);
        const existing = this.featureMergingPlans.get(plan.hash);
        if (!existing) {
            this.featureMergingPlans.set(plan.hash, plan);
            return plan.result;
        }
        else {
            return existing.result;
        }
    }
    getFeatureHash(feature) {
        const fidPreferred = this.fordPreferred.tryReverseFallback(feature, -1);
        const fidLess = this.fordLess.tryReverseFallback(feature, -1);
        return `${fidPreferred};${fidLess}`;
    }
    *getFeatureVariations() {
        yield* this.preferred.featureVariations || [];
        yield* this.less.featureVariations || [];
    }
}
exports.GsubGposMerger = GsubGposMerger;
//# sourceMappingURL=gsub-gpos-merger.js.map