"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GvarTableRead = void 0;
const bin_util_1 = require("@ot-builder/bin-util");
const errors_1 = require("@ot-builder/errors");
const ot_glyphs_1 = require("@ot-builder/ot-glyphs");
const primitive_1 = require("@ot-builder/primitive");
const var_store_1 = require("@ot-builder/var-store");
const variance_1 = require("@ot-builder/variance");
const tvd_access_1 = require("../shared/tvd-access");
const shared_1 = require("./shared");
exports.GvarTableRead = (0, bin_util_1.Read)((view, gOrd, cfg, ignore, designSpace) => {
    const header = view.next(GvarHeader, gOrd, cfg, designSpace);
    const ms = new variance_1.OtVar.MasterSet();
    for (let gid = 0; gid < gOrd.length; gid++) {
        const glyph = gOrd.at(gid);
        const dataOffset = header.glyphVariationDataOffsets[gid];
        const nextDataOffset = header.glyphVariationDataOffsets[gid + 1];
        if (dataOffset === nextDataOffset)
            continue;
        const ptr = header.glyphVariationDataArray.lift(dataOffset);
        ptr.next(var_store_1.TupleVariationRead, new GlyphTvhClient(ms, glyph, ignore), {
            designSpace,
            sharedTuples: header.sharedTuples
        });
    }
});
const GvarHeader = (0, bin_util_1.Read)((bp, gOrd, cfg, designSpace) => {
    const majorVersion = bp.uint16();
    const minorVersion = bp.uint16();
    const axisCount = bp.uint16();
    const sharedTupleCount = bp.uint16();
    const bpSharedTuples = bp.ptr32();
    const glyphCount = bp.uint16();
    const flags = bp.uint16();
    const glyphVariationDataArray = bp.ptr32();
    errors_1.Assert.SubVersionSupported("GvarHeader", majorVersion, minorVersion, [1, 0]);
    errors_1.Assert.SizeMatch("GvarHeader::axisCount", axisCount, designSpace.length);
    if (!cfg.ttf.gvarReadPermissiveGlyphCount) {
        errors_1.Assert.SizeMatch("GvarHeader::glyphCount", glyphCount, gOrd.length);
    }
    const sharedTuples = [];
    for (let tid = 0; tid < sharedTupleCount; tid++) {
        sharedTuples[tid] = bpSharedTuples.array(designSpace.length, primitive_1.F2D14);
    }
    const glyphVariationDataOffsets = [];
    if (flags & shared_1.GvarFlag.LongOffsets) {
        for (let gid = 0; gid <= glyphCount; gid++) {
            glyphVariationDataOffsets[gid] = bp.uint32();
        }
    }
    else {
        for (let gid = 0; gid <= glyphCount; gid++) {
            glyphVariationDataOffsets[gid] = bp.uint16() * 2;
        }
    }
    return { sharedTuples, glyphVariationDataArray, glyphVariationDataOffsets };
});
class GlyphTvhClient {
    constructor(ms, glyph, ignore) {
        this.glyph = glyph;
        this.dimensions = 2;
        this.glyphHolder = new VarPtrCollector().process(glyph);
        this.contours = this.glyphHolder.tvdAccesses(ms, ignore);
    }
    finish() {
        for (const c of this.contours)
            for (const z of c)
                z.finish();
        this.glyphHolder.apply(this.glyph);
    }
}
class VarPtrCollector {
    process(glyph) {
        const gh = new GlyphHolder();
        if (glyph.geometry)
            gh.geometry = new GeomVarPtrCollector().process(glyph.geometry);
        gh.hMetric = new HMetricHolder(glyph.horizontal);
        gh.vMetric = new VMetricHolder(glyph.vertical);
        return gh;
    }
}
class GeomVarPtrCollector {
    process(geom) {
        switch (geom.type) {
            case ot_glyphs_1.OtGlyph.GeometryType.ContourSet:
                return new ContourHolder(geom);
            case ot_glyphs_1.OtGlyph.GeometryType.TtReference:
                return new TtReferenceHolder(geom);
            case ot_glyphs_1.OtGlyph.GeometryType.GeometryList: {
                const parts = geom.items.map(item => this.process(item));
                return new GeometryListHolder(parts);
            }
        }
    }
}
// Holder classes
class GlyphHolder {
    apply(g) {
        if (this.geometry)
            g.geometry = this.geometry.toGeometry();
        if (this.hMetric)
            g.horizontal = this.hMetric.metric;
        if (this.vMetric)
            g.vertical = this.vMetric.metric;
    }
    tvdAccesses(ms, ignore) {
        const sink = [];
        if (this.geometry)
            this.geometry.collectTvdAccesses(sink, ms, ignore);
        if (this.hMetric)
            this.hMetric.collectTvdAccesses(sink, ms, ignore);
        if (this.vMetric)
            this.vMetric.collectTvdAccesses(sink, ms, ignore);
        return sink;
    }
}
class ContourHolder {
    constructor(cs) {
        this.contours = cs.contours.map(c => [...c]);
    }
    toGeometry() {
        return new ot_glyphs_1.OtGlyph.ContourSet(this.contours);
    }
    collectTvdAccesses(sink, ms) {
        for (let cid = 0; cid < this.contours.length; cid++) {
            const items = [];
            for (let zid = 0; zid < this.contours[cid].length; zid++) {
                items.push(new ContourTvdAccess(ms, this.contours, cid, zid, 1), new ContourTvdAccess(ms, this.contours, cid, zid, 0));
            }
            sink.push(items);
        }
    }
}
class TtReferenceHolder {
    constructor(ref) {
        this.ref = { ...ref };
    }
    toGeometry() {
        const ref1 = new ot_glyphs_1.OtGlyph.TtReference(this.ref.to, this.ref.transform);
        ref1.roundXyToGrid = this.ref.roundXyToGrid;
        ref1.useMyMetrics = this.ref.useMyMetrics;
        ref1.pointAttachment = this.ref.pointAttachment;
        return ref1;
    }
    collectTvdAccesses(sink, ms) {
        sink.push([new RefTvdAccess(ms, this.ref, 1), new RefTvdAccess(ms, this.ref, 0)]);
    }
}
class GeometryListHolder {
    constructor(children) {
        this.children = children;
    }
    toGeometry() {
        return new ot_glyphs_1.OtGlyph.GeometryList(this.children.map(c => c.toGeometry()));
    }
    collectTvdAccesses(sink, ms, ignore) {
        for (const sub of this.children) {
            sub.collectTvdAccesses(sink, ms, ignore);
        }
    }
}
class HMetricHolder {
    constructor(metric) {
        this.metric = metric;
    }
    collectTvdAccesses(sink, ms, ignore) {
        // H metric
        if (ignore.horizontalMetric) {
            sink.push([new TvdIgnore(), new TvdIgnore()], [new TvdIgnore(), new TvdIgnore()]);
        }
        else {
            sink.push([new MetricTvdAccess(ms, this, 1), new TvdIgnore()], [new MetricTvdAccess(ms, this, 0), new TvdIgnore()]);
        }
    }
}
class VMetricHolder {
    constructor(metric) {
        this.metric = metric;
    }
    collectTvdAccesses(sink, ms, ignore) {
        // V metric
        if (ignore.verticalMetric) {
            sink.push([new TvdIgnore(), new TvdIgnore()], [new TvdIgnore(), new TvdIgnore()]);
        }
        else {
            sink.push([new TvdIgnore(), new MetricTvdAccess(ms, this, 1)], [new TvdIgnore(), new MetricTvdAccess(ms, this, 0)]);
        }
    }
}
// TvdAccess implementations
class TvdIgnore {
    constructor() {
        this.original = 0;
    }
    addDelta(master, delta) { }
    finish() { }
}
class ContourTvdAccess extends tvd_access_1.CumulativeTvd {
    constructor(ms, cs, cid, zid, isX) {
        super(ms);
        this.cs = cs;
        this.cid = cid;
        this.zid = zid;
        this.isX = isX;
        const z = cs[cid][zid];
        this.original = isX ? variance_1.OtVar.Ops.originOf(z.x) : variance_1.OtVar.Ops.originOf(z.y);
    }
    finish() {
        const z = this.cs[this.cid][this.zid];
        const z1 = this.isX ? { ...z, x: this.collectTo(z.x) } : { ...z, y: this.collectTo(z.y) };
        this.cs[this.cid][this.zid] = z1;
    }
}
class RefTvdAccess extends tvd_access_1.CumulativeTvd {
    constructor(ms, ref, isX) {
        super(ms);
        this.ref = ref;
        this.isX = isX;
        const transform = ref.transform;
        this.original = isX ? variance_1.OtVar.Ops.originOf(transform.dx) : variance_1.OtVar.Ops.originOf(transform.dy);
    }
    finish() {
        const tf = this.ref.transform;
        const tf1 = {
            ...tf,
            ...(this.isX ? { dx: this.collectTo(tf.dx) } : { dy: this.collectTo(tf.dy) })
        };
        this.ref.transform = tf1;
    }
}
class MetricTvdAccess extends tvd_access_1.CumulativeTvd {
    constructor(ms, pMetric, isStart) {
        super(ms);
        this.pMetric = pMetric;
        this.isStart = isStart;
        const met = pMetric.metric;
        this.original = isStart ? variance_1.OtVar.Ops.originOf(met.start) : variance_1.OtVar.Ops.originOf(met.end);
    }
    finish() {
        const met = this.pMetric.metric;
        this.pMetric.metric = {
            ...met,
            ...(this.isStart
                ? { start: this.collectTo(met.start) }
                : { end: this.collectTo(met.end) })
        };
    }
}
//# sourceMappingURL=read.js.map