"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.inPlaceRectifyGlyphStore = inPlaceRectifyGlyphStore;
const Ot = require("@ot-builder/ot");
const interface_1 = require("../interface");
const shared_1 = require("../shared");
var TraverseProgress;
(function (TraverseProgress) {
    TraverseProgress[TraverseProgress["PROCESSING"] = 1] = "PROCESSING";
    TraverseProgress[TraverseProgress["PROCESSED"] = 2] = "PROCESSED";
})(TraverseProgress || (TraverseProgress = {}));
class GeometryProcessor {
    constructor(recGlyphRef, recCoord, recPA, globalState) {
        this.recGlyphRef = recGlyphRef;
        this.recCoord = recCoord;
        this.recPA = recPA;
        this.globalState = globalState;
    }
    process(geom, st) {
        switch (geom.type) {
            case Ot.Glyph.GeometryType.ContourSet:
                return this.processContourSet(geom, st);
            case Ot.Glyph.GeometryType.GeometryList:
                return this.processGeometryList(geom.items, st);
            case Ot.Glyph.GeometryType.TtReference:
                return this.processTtReference(geom, st);
        }
    }
    processContourSet(cs, st) {
        const cs1 = [];
        for (const c of cs.contours) {
            const c1 = [];
            for (let zid = 0; zid < c.length; zid++) {
                const z = c[zid];
                c1[zid] = Ot.Glyph.Point.create(this.recCoord.coord(z.x), this.recCoord.coord(z.y), z.kind);
            }
            cs1.push(c1);
        }
        const g = new Ot.Glyph.ContourSet(cs1);
        for (const c of g.contours)
            for (const z of c)
                st.points.push(z);
        return g;
    }
    processGeometryList(items, st) {
        const sink = [];
        for (const item of items) {
            const processed = this.process(item, st);
            if (processed)
                sink.push(processed);
        }
        if (sink.length)
            return new Ot.Glyph.GeometryList(sink);
        else
            return null;
    }
    processTtReference(ref, st) {
        const to1 = this.recGlyphRef.glyphRef(ref.to);
        if (!to1)
            return null;
        processGlyph(this.recGlyphRef, this.recCoord, this.recPA, to1, this.globalState);
        const ref1 = new Ot.Glyph.TtReference(to1, {
            ...ref.transform,
            dx: this.recCoord.coord(ref.transform.dx),
            dy: this.recCoord.coord(ref.transform.dy)
        });
        ref1.roundXyToGrid = ref.roundXyToGrid;
        ref1.useMyMetrics = ref.useMyMetrics;
        ref1.pointAttachment = ref.pointAttachment;
        const innerPoints = shared_1.RectifyImpl.getGlyphPoints(to1);
        this.processTtReferenceImpl(innerPoints, st, ref1);
        for (const z of innerPoints) {
            st.points.push(Ot.Glyph.PointOps.applyTransform(Ot.Glyph.Point.create(z.x, z.y), ref1.transform));
        }
        return ref1;
    }
    processTtReferenceImpl(innerPoints, st, ref1) {
        if (!ref1.pointAttachment)
            return;
        const pOut = st.points[ref1.pointAttachment.outer.pointIndex];
        const pIn = innerPoints[ref1.pointAttachment.inner.pointIndex];
        if (!pIn || !pOut) {
            ref1.pointAttachment = null;
            return;
        }
        const desiredOffset = Ot.Glyph.PointOps.minus(Ot.Glyph.Point.create(pOut.x, pOut.y), Ot.Glyph.PointOps.applyTransform(Ot.Glyph.Point.create(pIn.x, pIn.y), {
            ...ref1.transform,
            scaledOffset: false,
            dx: 0,
            dy: 0
        }));
        const accept = this.recPA.acceptOffset(desiredOffset, {
            x: ref1.transform.dx,
            y: ref1.transform.dy
        });
        if (accept.x && accept.y)
            return;
        switch (this.recPA.manner) {
            case interface_1.PointAttachmentRectifyManner.TrustAttachment:
                ref1.transform = {
                    ...ref1.transform,
                    dx: desiredOffset.x,
                    dy: desiredOffset.y,
                    scaledOffset: false
                };
                break;
            case interface_1.PointAttachmentRectifyManner.TrustCoordinate:
                ref1.pointAttachment = null;
                break;
        }
    }
}
class HintProcessor {
    constructor(rec) {
        this.rec = rec;
    }
    process(geom) {
        switch (geom.type) {
            case Ot.Glyph.HintType.TtInstruction:
                return this.processTtInstructions(geom);
            case Ot.Glyph.HintType.CffHint:
                return this.processCffHint(geom);
        }
    }
    processTtInstructions(tt) {
        return new Ot.Glyph.TtInstruction(tt.instructions);
    }
    processCffHint(ch) {
        const stemHMap = new Map();
        const stemVMap = new Map();
        for (const s of ch.hStems)
            stemHMap.set(s, this.processHintStem(s));
        for (const s of ch.vStems)
            stemVMap.set(s, this.processHintStem(s));
        const h1 = new Ot.Glyph.CffHint();
        h1.hStems = [...stemHMap.values()];
        h1.vStems = [...stemVMap.values()];
        h1.hintMasks = ch.hintMasks.map(m => this.processMask(m, stemHMap, stemVMap));
        h1.counterMasks = ch.counterMasks.map(m => this.processMask(m, stemHMap, stemVMap));
        return h1;
    }
    processHintStem(stem) {
        return Ot.Glyph.CffHint.createStem(this.rec.coord(stem.start), this.rec.coord(stem.end));
    }
    processMask(mask, stemHMap, stemVMap) {
        const maskH = new Set();
        const maskV = new Set();
        for (const s of mask.maskH) {
            const s1 = stemHMap.get(s);
            if (s1)
                maskH.add(s1);
        }
        for (const s of mask.maskV) {
            const s1 = stemVMap.get(s);
            if (s1)
                maskV.add(s1);
        }
        return Ot.Glyph.CffHint.createMask(mask.at, maskH, maskV);
    }
}
function processGlyph(recGlyphRef, recCoord, recPA, glyph, state) {
    const preProgress = state.progress.get(glyph);
    if (preProgress === TraverseProgress.PROCESSING)
        throw new Error(`Circular reference found around glyph ${glyph.name}`);
    if (preProgress === TraverseProgress.PROCESSED)
        return;
    state.progress.set(glyph, TraverseProgress.PROCESSING);
    if (glyph.geometry) {
        const alg = new GeometryProcessor(recGlyphRef, recCoord, recPA, state);
        glyph.geometry = alg.process(glyph.geometry, { points: [] });
    }
    if (glyph.hints) {
        glyph.hints = new HintProcessor(recCoord).process(glyph.hints);
    }
    glyph.horizontal = {
        start: recCoord.coord(glyph.horizontal.start),
        end: recCoord.coord(glyph.horizontal.end)
    };
    glyph.vertical = {
        start: recCoord.coord(glyph.vertical.start),
        end: recCoord.coord(glyph.vertical.end)
    };
    state.progress.set(glyph, TraverseProgress.PROCESSED);
}
function inPlaceRectifyGlyphStore(recGlyphRef, recCoord, recPA, glyphs) {
    const gOrd = glyphs.decideOrder();
    const st = { progress: new Map() };
    for (const g of gOrd)
        processGlyph(recGlyphRef, recCoord, recPA, g, st);
}
//# sourceMappingURL=index.js.map