"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PointNumberRunDp = exports.DeltaRunDp = exports.PointNumberRun = exports.PointNumberRunType = exports.PointNumberFlags = exports.PointCount = exports.DeltaRun = exports.DeltaRunType = exports.DeltaRunFlags = void 0;
const errors_1 = require("@ot-builder/errors");
const primitive_1 = require("@ot-builder/primitive");
var DeltaRunFlags;
(function (DeltaRunFlags) {
    DeltaRunFlags[DeltaRunFlags["DELTAS_ARE_ZERO"] = 128] = "DELTAS_ARE_ZERO";
    DeltaRunFlags[DeltaRunFlags["DELTAS_ARE_WORDS"] = 64] = "DELTAS_ARE_WORDS";
    DeltaRunFlags[DeltaRunFlags["DELTA_RUN_COUNT_MASK"] = 63] = "DELTA_RUN_COUNT_MASK";
})(DeltaRunFlags || (exports.DeltaRunFlags = DeltaRunFlags = {}));
var DeltaRunType;
(function (DeltaRunType) {
    DeltaRunType[DeltaRunType["Zero"] = 0] = "Zero";
    DeltaRunType[DeltaRunType["Short"] = 1] = "Short";
    DeltaRunType[DeltaRunType["Long"] = 2] = "Long";
})(DeltaRunType || (exports.DeltaRunType = DeltaRunType = {}));
exports.DeltaRun = {
    read(view, deltasParsed, points, deltas) {
        const runHead = view.uint8();
        const deltasAreZero = runHead & DeltaRunFlags.DELTAS_ARE_ZERO;
        const deltasAreWords = runHead & DeltaRunFlags.DELTAS_ARE_WORDS;
        const runLength = (runHead & DeltaRunFlags.DELTA_RUN_COUNT_MASK) + 1;
        for (let ixZRun = 0; ixZRun < runLength; ixZRun++) {
            const delta = deltasAreZero ? 0 : deltasAreWords ? view.int16() : view.int8();
            if (!deltas[points[deltasParsed]])
                deltas[points[deltasParsed]] = 0;
            deltas[points[deltasParsed]] += delta;
            deltasParsed++;
        }
        return deltasParsed;
    }
};
exports.PointCount = {
    read(view) {
        const byte0 = view.uint8();
        if (byte0 === 0)
            return null;
        else
            return byte0 & 0x80 ? ((byte0 << 8) | view.uint8()) & 0x7fff : byte0;
    },
    write(b, x) {
        if (x >= 1 && x <= 127) {
            b.uint8(x);
        }
        else {
            const highHalf = ((x >> 8) & 0xff) | 0x80;
            const lowerHalf = x && 0xff;
            b.uint8(highHalf).uint8(lowerHalf);
        }
    }
};
var PointNumberFlags;
(function (PointNumberFlags) {
    PointNumberFlags[PointNumberFlags["POINTS_ARE_WORDS"] = 128] = "POINTS_ARE_WORDS";
    PointNumberFlags[PointNumberFlags["POINT_RUN_COUNT_MASK"] = 127] = "POINT_RUN_COUNT_MASK";
})(PointNumberFlags || (exports.PointNumberFlags = PointNumberFlags = {}));
var PointNumberRunType;
(function (PointNumberRunType) {
    PointNumberRunType[PointNumberRunType["Short"] = 0] = "Short";
    PointNumberRunType[PointNumberRunType["Long"] = 1] = "Long";
})(PointNumberRunType || (exports.PointNumberRunType = PointNumberRunType = {}));
exports.PointNumberRun = {
    read(view, currentPoint, points) {
        const runHead = view.uint8();
        const pointsAreWords = runHead & PointNumberFlags.POINTS_ARE_WORDS;
        const runLength = (runHead & PointNumberFlags.POINT_RUN_COUNT_MASK) + 1;
        for (let ixZRun = 0; ixZRun < runLength; ixZRun++) {
            const deltaID = pointsAreWords ? view.uint16() : view.uint8();
            currentPoint += deltaID;
            points.push(currentPoint);
        }
        return currentPoint;
    }
};
function DpMin(...runs) {
    let r = null;
    for (const run of runs) {
        if (!run)
            continue;
        if (!r)
            r = run;
        else if (run.cost < r.cost)
            r = run;
    }
    return r;
}
function DpUpdateTrack(src, track, ...runs) {
    src[track] = DpMin(src[track], ...runs);
}
var DeltaRunDp;
(function (DeltaRunDp) {
    class ZeroRun {
        constructor(link, size) {
            this.link = link;
            this.size = size;
            this.cost = 1 + (link ? link.cost : 0);
        }
        write(frag) {
            const lengthPart = (this.size - 1) & DeltaRunFlags.DELTA_RUN_COUNT_MASK;
            frag.uint8(DeltaRunFlags.DELTAS_ARE_ZERO | lengthPart);
        }
        static continue(cur, x) {
            if (!cur || x)
                return null;
            if (cur.size >= DeltaRunFlags.DELTA_RUN_COUNT_MASK)
                return null;
            else
                return new ZeroRun(cur.link, cur.size + 1);
        }
        static startOver(started, prev, x) {
            if ((started && !prev) || x)
                return null;
            return new ZeroRun(prev, 1);
        }
    }
    class ByteRun {
        constructor(link, data) {
            this.link = link;
            this.data = data;
            this.cost = 1 + data.length + (link ? link.cost : 0);
        }
        write(frag) {
            const lengthPart = (this.data.length - 1) & DeltaRunFlags.DELTA_RUN_COUNT_MASK;
            frag.uint8(0 | lengthPart);
            for (const x of this.data)
                frag.int8(x);
        }
        static continue(cur, x) {
            if (!cur || primitive_1.Int8.from(x) !== primitive_1.Int16.from(x))
                return null;
            if (cur.data.length >= DeltaRunFlags.DELTA_RUN_COUNT_MASK)
                return null;
            return new ByteRun(cur.link, [...cur.data, x]);
        }
        static startOver(started, prev, x) {
            if ((started && !prev) || primitive_1.Int8.from(x) !== primitive_1.Int16.from(x))
                return null;
            return new ByteRun(prev, [x]);
        }
    }
    class WordRun {
        constructor(link, data) {
            this.link = link;
            this.data = data;
            this.cost = 1 + 2 * data.length + (link ? link.cost : 0);
        }
        write(frag) {
            const lengthPart = (this.data.length - 1) & DeltaRunFlags.DELTA_RUN_COUNT_MASK;
            frag.uint8(DeltaRunFlags.DELTAS_ARE_WORDS | lengthPart);
            for (const x of this.data)
                frag.int16(x);
        }
        static continue(cur, x) {
            if (!cur || cur.data.length >= DeltaRunFlags.DELTA_RUN_COUNT_MASK)
                return null;
            return new WordRun(cur.link, [...cur.data, x]);
        }
        static startOver(started, prev, x) {
            if (started && !prev)
                return null;
            return new WordRun(prev, [x]);
        }
    }
    class Writer {
        constructor(trackLength) {
            // We keep track three possible "last run"s and pick the one with least
            // byte cost.
            this.started = false;
            this.zero = [];
            this.byte = [];
            this.word = [];
            this.trackingModes = 1 << trackLength;
            this.trackingRestMask = (1 << (trackLength - 1)) - 1;
        }
        update(x) {
            const zero = [...this.zero];
            const byte = [...this.byte];
            const word = [...this.word];
            for (let track = 0; track < this.trackingModes; track++) {
                this.zero[track] = this.byte[track] = this.word[track] = null;
            }
            for (let mode = 0; mode < this.trackingModes * 2; mode++) {
                const origin = mode >>> 1;
                const track = (mode & 1) | ((origin & this.trackingRestMask) << 1);
                if (mode & 1) {
                    DpUpdateTrack(this.zero, track, ZeroRun.continue(zero[origin], x));
                    DpUpdateTrack(this.byte, track, ByteRun.continue(byte[origin], x));
                    DpUpdateTrack(this.word, track, WordRun.continue(word[origin], x));
                }
                else {
                    const originMin = DpMin(zero[origin], byte[origin], word[origin]);
                    DpUpdateTrack(this.zero, track, ZeroRun.startOver(this.started, originMin, x));
                    DpUpdateTrack(this.byte, track, ByteRun.startOver(this.started, originMin, x));
                    DpUpdateTrack(this.word, track, WordRun.startOver(this.started, originMin, x));
                }
            }
            this.started = true;
        }
        write(frag) {
            const runs = [];
            let bestRun = DpMin(...this.zero, ...this.byte, ...this.word);
            if (this.started && !bestRun)
                throw errors_1.Errors.Unreachable();
            while (bestRun) {
                runs.unshift(bestRun);
                bestRun = bestRun.link;
            }
            for (const run of runs)
                run.write(frag);
        }
    }
    DeltaRunDp.Writer = Writer;
})(DeltaRunDp || (exports.DeltaRunDp = DeltaRunDp = {}));
var PointNumberRunDp;
(function (PointNumberRunDp) {
    class ByteRun {
        constructor(link, data) {
            this.link = link;
            this.data = data;
            this.cost = 1 + data.length + (link ? link.cost : 0);
        }
        write(frag) {
            const lengthPart = (this.data.length - 1) & PointNumberFlags.POINT_RUN_COUNT_MASK;
            frag.uint8(0 | lengthPart);
            for (const x of this.data)
                frag.uint8(x);
        }
        static continue(cur, x) {
            if (!cur || primitive_1.UInt8.from(x) !== primitive_1.UInt16.from(x))
                return null;
            if (cur.data.length >= PointNumberFlags.POINT_RUN_COUNT_MASK)
                return null;
            return new ByteRun(cur.link, [...cur.data, x]);
        }
        static startOver(started, prev, x) {
            if ((started && !prev) || primitive_1.UInt8.from(x) !== primitive_1.UInt16.from(x))
                return null;
            return new ByteRun(prev, [x]);
        }
    }
    class WordRun {
        constructor(link, data) {
            this.link = link;
            this.data = data;
            this.cost = 1 + 2 * data.length + (link ? link.cost : 0);
        }
        write(frag) {
            const lengthPart = (this.data.length - 1) & PointNumberFlags.POINT_RUN_COUNT_MASK;
            frag.uint8(PointNumberFlags.POINTS_ARE_WORDS | lengthPart);
            for (const x of this.data)
                frag.uint16(x);
        }
        static continue(cur, x) {
            if (!cur || cur.data.length >= PointNumberFlags.POINT_RUN_COUNT_MASK)
                return null;
            return new WordRun(cur.link, [...cur.data, x]);
        }
        static startOver(started, prev, x) {
            if (started && !prev)
                return null;
            return new WordRun(prev, [x]);
        }
    }
    class Writer {
        constructor(trackLength) {
            this.started = false;
            this.byte = [];
            this.word = [];
            this.trackingModes = 1 << trackLength;
            this.trackingRestMask = (1 << (trackLength - 1)) - 1;
        }
        update(x) {
            const byte = [...this.byte];
            const word = [...this.word];
            for (let track = 0; track < this.trackingModes; track++) {
                this.byte[track] = this.word[track] = null;
            }
            for (let mode = 0; mode < 2 * this.trackingModes; mode++) {
                const origin = mode >>> 1;
                const track = (mode & 1) | ((origin & this.trackingRestMask) << 1);
                if (mode & 1) {
                    DpUpdateTrack(this.byte, track, ByteRun.continue(byte[origin], x));
                    DpUpdateTrack(this.word, track, WordRun.continue(word[origin], x));
                }
                else {
                    const originMin = DpMin(byte[origin], word[origin]);
                    DpUpdateTrack(this.byte, track, ByteRun.startOver(this.started, originMin, x));
                    DpUpdateTrack(this.word, track, WordRun.startOver(this.started, originMin, x));
                }
            }
            this.started = true;
        }
        write(frag) {
            const runs = [];
            let bestRun = DpMin(...this.byte, ...this.word);
            if (this.started && !bestRun)
                throw errors_1.Errors.Unreachable();
            while (bestRun) {
                runs.unshift(bestRun);
                bestRun = bestRun.link;
            }
            for (const run of runs)
                run.write(frag);
        }
    }
    PointNumberRunDp.Writer = Writer;
})(PointNumberRunDp || (exports.PointNumberRunDp = PointNumberRunDp = {}));
//# sourceMappingURL=runs.js.map