"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RectifyGposGlyphCoordAlg = exports.RectifyGsubGlyphCoordAlg = void 0;
exports.RStub = RStub;
exports.rectifyLookupList = rectifyLookupList;
const tslib_1 = require("tslib");
const Ot = tslib_1.__importStar(require("@ot-builder/ot"));
const interface_1 = require("../../interface");
const shared_1 = require("../../shared");
function RStub(demand, fill) {
    return { demand, fill };
}
function rectifyLookupList(lookups, alg, app) {
    const replicateProcedures = [];
    for (let lid = 0; lid < lookups.length; lid++) {
        const lookup = lookups[lid];
        replicateProcedures[lid] = app(lookup, alg);
    }
    for (let lid = 0; lid < lookups.length; lid++) {
        replicateProcedures[lid].fill(replicateProcedures[lid].demand);
    }
    const result = new Map();
    for (let lid = 0; lid < lookups.length; lid++) {
        result.set(lookups[lid], replicateProcedures[lid].demand);
    }
    return result;
}
class RectifyGlyphCoordAlgBase {
    constructor(rg, rc, rap) {
        this.rg = rg;
        this.rc = rc;
        this.rap = rap;
        this._cache = new Map();
    }
    setMeta(props, ret) {
        ret.rightToLeft = props.rightToLeft;
        ret.ignoreGlyphs = shared_1.RectifyImpl.Glyph.setSome(this.rg, props.ignoreGlyphs || new Set());
    }
    isValidMatchingSequence(match) {
        if (!match.length)
            return false;
        for (const gs of match)
            if (!gs.size)
                return false;
        return true;
    }
    processChainingRules(props, ret) {
        ret.rules = [];
        for (const rule of props.rules) {
            const match1 = shared_1.RectifyImpl.listAllT(this.rg, rule.match, shared_1.RectifyImpl.Glyph.setSome);
            if (!match1 || !this.isValidMatchingSequence(match1))
                continue;
            const applications1 = [];
            for (const app of rule.applications) {
                const stub = this._cache.get(app.apply);
                if (stub)
                    applications1.push({ at: app.at, apply: stub.demand });
            }
            ret.rules.push({
                match: match1,
                applications: applications1,
                inputBegins: rule.inputBegins,
                inputEnds: rule.inputEnds
            });
        }
    }
    process(lookup) {
        const result = this.processImpl(lookup);
        this._cache.set(lookup, result);
        return result;
    }
}
class RectifyGsubGlyphCoordAlg extends RectifyGlyphCoordAlgBase {
    processImpl(lookup) {
        switch (lookup.type) {
            case Ot.Gsub.LookupType.Single:
                return this.gsubSingle(lookup);
            case Ot.Gsub.LookupType.Multi:
                return this.gsubMulti(lookup);
            case Ot.Gsub.LookupType.Alternate:
                return this.gsubAlternate(lookup);
            case Ot.Gsub.LookupType.Ligature:
                return this.gsubLigature(lookup);
            case Ot.Gsub.LookupType.Chaining:
                return this.gsubChaining(lookup);
            case Ot.Gsub.LookupType.Reverse:
                return this.gsubReverse(lookup);
        }
    }
    gsubSingle(props) {
        return RStub(new Ot.Gsub.Single(), ret => {
            this.setMeta(props, ret);
            ret.mapping = shared_1.RectifyImpl.Glyph.bimapSome(this.rg, props.mapping);
        });
    }
    gsubMulti(props) {
        return RStub(new Ot.Gsub.Multiple(), ret => {
            this.setMeta(props, ret);
            ret.mapping = shared_1.RectifyImpl.Glyph.mapSomeTX(this.rg, props.mapping, shared_1.RectifyImpl.Glyph.listAll);
        });
    }
    gsubAlternate(props) {
        return RStub(new Ot.Gsub.Alternate(), ret => {
            this.setMeta(props, ret);
            ret.mapping = shared_1.RectifyImpl.Glyph.mapSomeTX(this.rg, props.mapping, shared_1.RectifyImpl.Glyph.listAll);
        });
    }
    gsubLigature(props) {
        return RStub(new Ot.Gsub.Ligature(), ret => {
            this.setMeta(props, ret);
            const mapping1 = [];
            for (const { from, to } of props.mapping) {
                const dst1 = this.rg.glyphRef(to);
                if (!dst1)
                    continue;
                const src1 = shared_1.RectifyImpl.Glyph.listAll(this.rg, from);
                if (!src1)
                    continue;
                mapping1.push({ from: src1, to: dst1 });
            }
            ret.mapping = mapping1;
        });
    }
    gsubReverse(props) {
        return RStub(new Ot.Gsub.ReverseSub(), ret => {
            this.setMeta(props, ret);
            ret.rules = shared_1.RectifyImpl.listSomeT(this.rg, props.rules, (rec, rule) => {
                const match1 = shared_1.RectifyImpl.listAllT(rec, rule.match, shared_1.RectifyImpl.Glyph.setSome);
                const replace1 = shared_1.RectifyImpl.Glyph.bimapSome(rec, rule.replacement);
                if (!match1 || !this.isValidMatchingSequence(match1) || !replace1)
                    return null;
                else
                    return { ...rule, match: match1, replacement: replace1 };
            });
        });
    }
    gsubChaining(props) {
        return RStub(new Ot.Gsub.Chaining(), ret => {
            this.setMeta(props, ret);
            this.processChainingRules(props, ret);
        });
    }
}
exports.RectifyGsubGlyphCoordAlg = RectifyGsubGlyphCoordAlg;
class RectifyGposGlyphCoordAlg extends RectifyGlyphCoordAlgBase {
    processImpl(lookup) {
        switch (lookup.type) {
            case Ot.Gpos.LookupType.Single:
                return this.gposSingle(lookup);
            case Ot.Gpos.LookupType.Pair:
                return this.gposPair(lookup);
            case Ot.Gpos.LookupType.Cursive:
                return this.gposCursive(lookup);
            case Ot.Gpos.LookupType.MarkToBase:
                return this.gposMarkToBase(lookup);
            case Ot.Gpos.LookupType.MarkToLigature:
                return this.gposMarkToLigature(lookup);
            case Ot.Gpos.LookupType.MarkToMark:
                return this.gposMarkToMark(lookup);
            case Ot.Gpos.LookupType.Chaining:
                return this.gposChaining(lookup);
        }
    }
    gposSingle(props) {
        return RStub(new Ot.Gpos.Single(), ret => {
            this.setMeta(props, ret);
            ret.adjustments = shared_1.RectifyImpl.Glyph.mapSomeTX(this.rg, props.adjustments, (rec, x) => rectifyAdjustment(this.rc, x));
        });
    }
    gposPair(props) {
        return RStub(new Ot.Gpos.Pair(), ret => {
            this.setMeta(props, ret);
            const rep = props.adjustments.toRep();
            for (let c1 = 0; c1 < rep.xClasses.length; c1++) {
                rep.xClasses[c1] = shared_1.RectifyImpl.Glyph.listSome(this.rg, rep.xClasses[c1]);
            }
            for (let c2 = 0; c2 < rep.yClasses.length; c2++) {
                rep.yClasses[c2] = shared_1.RectifyImpl.Glyph.listSome(this.rg, rep.yClasses[c2]);
            }
            for (let c1 = 0; c1 < rep.data.length; c1++) {
                const row = rep.data[c1];
                for (let c2 = 0; c2 < row.length; c2++) {
                    const adj = row[c2];
                    if (adj == null)
                        continue;
                    row[c2] = [
                        rectifyAdjustment(this.rc, adj[0]),
                        rectifyAdjustment(this.rc, adj[1])
                    ];
                }
            }
            ret.adjustments = Ot.DicingStore.create(rep);
        });
    }
    gposCursive(props) {
        return RStub(new Ot.Gpos.Cursive(), ret => {
            this.setMeta(props, ret);
            ret.attachments = shared_1.RectifyImpl.mapSomeT2(this.rg, props.attachments, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyAnchorPairAP(this.rap, g, rectifyAnchorPair(this.rc, x)));
        });
    }
    gposMarkToBase(props) {
        return RStub(new Ot.Gpos.MarkToBase(), ret => {
            this.setMeta(props, ret);
            ret.marks = shared_1.RectifyImpl.mapSomeT2(this.rg, props.marks, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyMarkRecordAP(this.rap, g, rectifyMarkRecord(this.rc, x)));
            ret.bases = shared_1.RectifyImpl.mapSomeT2(this.rg, props.bases, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyBaseRecordAP(this.rap, g, rectifyBaseRecord(this.rc, x)));
        });
    }
    gposMarkToMark(props) {
        return RStub(new Ot.Gpos.MarkToMark(), ret => {
            this.setMeta(props, ret);
            ret.marks = shared_1.RectifyImpl.mapSomeT2(this.rg, props.marks, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyMarkRecordAP(this.rap, g, rectifyMarkRecord(this.rc, x)));
            ret.baseMarks = shared_1.RectifyImpl.mapSomeT2(this.rg, props.baseMarks, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyBaseRecordAP(this.rap, g, rectifyBaseRecord(this.rc, x)));
        });
    }
    gposMarkToLigature(props) {
        return RStub(new Ot.Gpos.MarkToLigature(), ret => {
            this.setMeta(props, ret);
            ret.marks = shared_1.RectifyImpl.mapSomeT2(this.rg, props.marks, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyMarkRecordAP(this.rap, g, rectifyMarkRecord(this.rc, x)));
            ret.bases = shared_1.RectifyImpl.mapSomeT2(this.rg, props.bases, (rg, g) => rg.glyphRef(g), (rg, g, x) => rectifyLigatureRecordAP(this.rap, g, rectifyLigatureRecord(this.rc, x)));
        });
    }
    gposChaining(props) {
        return RStub(new Ot.Gpos.Chaining(), ret => {
            this.setMeta(props, ret);
            this.processChainingRules(props, ret);
        });
    }
}
exports.RectifyGposGlyphCoordAlg = RectifyGposGlyphCoordAlg;
// Helper functions
function rectifyAdjustment(rec, adj) {
    return {
        ...adj,
        dX: rec.coord(adj.dX),
        dY: rec.coord(adj.dY),
        dWidth: rec.coord(adj.dWidth),
        dHeight: rec.coord(adj.dHeight)
    };
}
function rectifyAnchor(rec, anc) {
    return { ...anc, x: rec.coord(anc.x), y: rec.coord(anc.y) };
}
function rectifyAnchorAP(rectifier, context, z) {
    if (!rectifier || !z.attachToPoint)
        return z;
    const desired = shared_1.RectifyImpl.getGlyphPoints(context)[z.attachToPoint.pointIndex];
    if (!desired)
        return { ...z, attachToPoint: null };
    const accept = rectifier.acceptOffset(desired, z);
    if (accept.x && accept.y)
        return z;
    switch (rectifier.manner) {
        case interface_1.PointAttachmentRectifyManner.TrustAttachment:
            return { ...z, x: desired.x, y: desired.y };
        case interface_1.PointAttachmentRectifyManner.TrustCoordinate:
            return { ...z, attachToPoint: null };
    }
}
function rectifyAnchorPair(rec, ap) {
    return {
        entry: !ap.entry ? ap.entry : rectifyAnchor(rec, ap.entry),
        exit: !ap.exit ? ap.exit : rectifyAnchor(rec, ap.exit)
    };
}
function rectifyAnchorPairAP(rectifier, context, ap) {
    return {
        entry: !ap.entry ? ap.entry : rectifyAnchorAP(rectifier, context, ap.entry),
        exit: !ap.exit ? ap.exit : rectifyAnchorAP(rectifier, context, ap.exit)
    };
}
function rectifyMarkRecord(rec, mr) {
    const mr1 = { markAnchors: [] };
    for (let clsAnchor = 0; clsAnchor < mr.markAnchors.length; clsAnchor++) {
        const a = mr.markAnchors[clsAnchor];
        if (!a)
            mr1.markAnchors[clsAnchor] = a;
        else
            mr1.markAnchors[clsAnchor] = rectifyAnchor(rec, a);
    }
    return mr1;
}
function rectifyMarkRecordAP(rec, glyph, mr) {
    const mr1 = { markAnchors: [] };
    for (let clsAnchor = 0; clsAnchor < mr.markAnchors.length; clsAnchor++) {
        const a = mr.markAnchors[clsAnchor];
        if (!a)
            mr1.markAnchors[clsAnchor] = a;
        else {
            mr1.markAnchors[clsAnchor] = rectifyAnchorAP(rec, glyph, a);
        }
    }
    return mr1;
}
function rectifyBaseRecord(rec, mr) {
    const mr1 = { baseAnchors: [] };
    for (let clsAnchor = 0; clsAnchor < mr.baseAnchors.length; clsAnchor++) {
        const a = mr.baseAnchors[clsAnchor];
        if (!a)
            mr1.baseAnchors[clsAnchor] = a;
        else
            mr1.baseAnchors[clsAnchor] = rectifyAnchor(rec, a);
    }
    return mr1;
}
function rectifyBaseRecordAP(rec, glyph, mr) {
    const mr1 = { baseAnchors: [] };
    for (let clsAnchor = 0; clsAnchor < mr.baseAnchors.length; clsAnchor++) {
        const a = mr.baseAnchors[clsAnchor];
        if (!a)
            mr1.baseAnchors[clsAnchor] = a;
        else {
            mr1.baseAnchors[clsAnchor] = rectifyAnchorAP(rec, glyph, a);
        }
    }
    return mr1;
}
function rectifyLigatureRecord(rec, mr) {
    const mr1 = { baseAnchors: [] };
    for (let part = 0; part < mr.baseAnchors.length; part++) {
        mr1.baseAnchors[part] = [];
        for (let clsAnchor = 0; clsAnchor < mr.baseAnchors.length; clsAnchor++) {
            const a = mr.baseAnchors[part][clsAnchor];
            if (!a)
                mr1.baseAnchors[part][clsAnchor] = a;
            else
                mr1.baseAnchors[part][clsAnchor] = rectifyAnchor(rec, a);
        }
    }
    return mr1;
}
function rectifyLigatureRecordAP(rec, glyph, mr) {
    const mr1 = { baseAnchors: [] };
    for (let part = 0; part < mr.baseAnchors.length; part++) {
        mr1.baseAnchors[part] = [];
        for (let clsAnchor = 0; clsAnchor < mr.baseAnchors.length; clsAnchor++) {
            const a = mr.baseAnchors[part][clsAnchor];
            if (!a)
                mr1.baseAnchors[part][clsAnchor] = a;
            else {
                mr1.baseAnchors[part][clsAnchor] = rectifyAnchorAP(rec, glyph, a);
            }
        }
    }
    return mr1;
}
//# sourceMappingURL=rectify.js.map