import { __extends } from "tslib";
import PDFName from "../objects/PDFName";
import PDFRef from "../objects/PDFRef";
import PDFFlateStream from "./PDFFlateStream";
import { bytesFor, Cache, reverseArray, sizeInBytes, sum } from "../../utils";
export var EntryType;
(function (EntryType) {
    EntryType[EntryType["Deleted"] = 0] = "Deleted";
    EntryType[EntryType["Uncompressed"] = 1] = "Uncompressed";
    EntryType[EntryType["Compressed"] = 2] = "Compressed";
})(EntryType || (EntryType = {}));
/**
 * Entries should be added using the [[addDeletedEntry]],
 * [[addUncompressedEntry]], and [[addCompressedEntry]] methods
 * **in order of ascending object number**.
 */
var PDFCrossRefStream = /** @class */ (function (_super) {
    __extends(PDFCrossRefStream, _super);
    function PDFCrossRefStream(dict, entries, encode) {
        if (encode === void 0) { encode = true; }
        var _this = _super.call(this, dict, encode) || this;
        // Returns an array of integer pairs for each subsection of the cross ref
        // section, where each integer pair represents:
        //   firstObjectNumber(OfSection), length(OfSection)
        _this.computeIndex = function () {
            var subsections = [];
            var subsectionLength = 0;
            for (var idx = 0, len = _this.entries.length; idx < len; idx++) {
                var currEntry = _this.entries[idx];
                var prevEntry = _this.entries[idx - 1];
                if (idx === 0) {
                    subsections.push(currEntry.ref.objectNumber);
                }
                else if (currEntry.ref.objectNumber - prevEntry.ref.objectNumber > 1) {
                    subsections.push(subsectionLength);
                    subsections.push(currEntry.ref.objectNumber);
                    subsectionLength = 0;
                }
                subsectionLength += 1;
            }
            subsections.push(subsectionLength);
            return subsections;
        };
        _this.computeEntryTuples = function () {
            var entryTuples = new Array(_this.entries.length);
            for (var idx = 0, len = _this.entries.length; idx < len; idx++) {
                var entry = _this.entries[idx];
                if (entry.type === EntryType.Deleted) {
                    var type = entry.type, nextFreeObjectNumber = entry.nextFreeObjectNumber, ref = entry.ref;
                    entryTuples[idx] = [type, nextFreeObjectNumber, ref.generationNumber];
                }
                if (entry.type === EntryType.Uncompressed) {
                    var type = entry.type, offset = entry.offset, ref = entry.ref;
                    entryTuples[idx] = [type, offset, ref.generationNumber];
                }
                if (entry.type === EntryType.Compressed) {
                    var type = entry.type, objectStreamRef = entry.objectStreamRef, index = entry.index;
                    entryTuples[idx] = [type, objectStreamRef.objectNumber, index];
                }
            }
            return entryTuples;
        };
        _this.computeMaxEntryByteWidths = function () {
            var entryTuples = _this.entryTuplesCache.access();
            var widths = [0, 0, 0];
            for (var idx = 0, len = entryTuples.length; idx < len; idx++) {
                var _a = entryTuples[idx], first = _a[0], second = _a[1], third = _a[2];
                var firstSize = sizeInBytes(first);
                var secondSize = sizeInBytes(second);
                var thirdSize = sizeInBytes(third);
                if (firstSize > widths[0])
                    widths[0] = firstSize;
                if (secondSize > widths[1])
                    widths[1] = secondSize;
                if (thirdSize > widths[2])
                    widths[2] = thirdSize;
            }
            return widths;
        };
        _this.entries = entries || [];
        _this.entryTuplesCache = Cache.populatedBy(_this.computeEntryTuples);
        _this.maxByteWidthsCache = Cache.populatedBy(_this.computeMaxEntryByteWidths);
        _this.indexCache = Cache.populatedBy(_this.computeIndex);
        dict.set(PDFName.of('Type'), PDFName.of('XRef'));
        return _this;
    }
    PDFCrossRefStream.prototype.addDeletedEntry = function (ref, nextFreeObjectNumber) {
        var type = EntryType.Deleted;
        this.entries.push({ type: type, ref: ref, nextFreeObjectNumber: nextFreeObjectNumber });
        this.entryTuplesCache.invalidate();
        this.maxByteWidthsCache.invalidate();
        this.indexCache.invalidate();
        this.contentsCache.invalidate();
    };
    PDFCrossRefStream.prototype.addUncompressedEntry = function (ref, offset) {
        var type = EntryType.Uncompressed;
        this.entries.push({ type: type, ref: ref, offset: offset });
        this.entryTuplesCache.invalidate();
        this.maxByteWidthsCache.invalidate();
        this.indexCache.invalidate();
        this.contentsCache.invalidate();
    };
    PDFCrossRefStream.prototype.addCompressedEntry = function (ref, objectStreamRef, index) {
        var type = EntryType.Compressed;
        this.entries.push({ type: type, ref: ref, objectStreamRef: objectStreamRef, index: index });
        this.entryTuplesCache.invalidate();
        this.maxByteWidthsCache.invalidate();
        this.indexCache.invalidate();
        this.contentsCache.invalidate();
    };
    PDFCrossRefStream.prototype.clone = function (context) {
        var _a = this, dict = _a.dict, entries = _a.entries, encode = _a.encode;
        return PDFCrossRefStream.of(dict.clone(context), entries.slice(), encode);
    };
    PDFCrossRefStream.prototype.getContentsString = function () {
        var entryTuples = this.entryTuplesCache.access();
        var byteWidths = this.maxByteWidthsCache.access();
        var value = '';
        for (var entryIdx = 0, entriesLen = entryTuples.length; entryIdx < entriesLen; entryIdx++) {
            var _a = entryTuples[entryIdx], first = _a[0], second = _a[1], third = _a[2];
            var firstBytes = reverseArray(bytesFor(first));
            var secondBytes = reverseArray(bytesFor(second));
            var thirdBytes = reverseArray(bytesFor(third));
            for (var idx = byteWidths[0] - 1; idx >= 0; idx--) {
                value += (firstBytes[idx] || 0).toString(2);
            }
            for (var idx = byteWidths[1] - 1; idx >= 0; idx--) {
                value += (secondBytes[idx] || 0).toString(2);
            }
            for (var idx = byteWidths[2] - 1; idx >= 0; idx--) {
                value += (thirdBytes[idx] || 0).toString(2);
            }
        }
        return value;
    };
    PDFCrossRefStream.prototype.getUnencodedContents = function () {
        var entryTuples = this.entryTuplesCache.access();
        var byteWidths = this.maxByteWidthsCache.access();
        var buffer = new Uint8Array(this.getUnencodedContentsSize());
        var offset = 0;
        for (var entryIdx = 0, entriesLen = entryTuples.length; entryIdx < entriesLen; entryIdx++) {
            var _a = entryTuples[entryIdx], first = _a[0], second = _a[1], third = _a[2];
            var firstBytes = reverseArray(bytesFor(first));
            var secondBytes = reverseArray(bytesFor(second));
            var thirdBytes = reverseArray(bytesFor(third));
            for (var idx = byteWidths[0] - 1; idx >= 0; idx--) {
                buffer[offset++] = firstBytes[idx] || 0;
            }
            for (var idx = byteWidths[1] - 1; idx >= 0; idx--) {
                buffer[offset++] = secondBytes[idx] || 0;
            }
            for (var idx = byteWidths[2] - 1; idx >= 0; idx--) {
                buffer[offset++] = thirdBytes[idx] || 0;
            }
        }
        return buffer;
    };
    PDFCrossRefStream.prototype.getUnencodedContentsSize = function () {
        var byteWidths = this.maxByteWidthsCache.access();
        var entryWidth = sum(byteWidths);
        return entryWidth * this.entries.length;
    };
    PDFCrossRefStream.prototype.updateDict = function () {
        _super.prototype.updateDict.call(this);
        var byteWidths = this.maxByteWidthsCache.access();
        var index = this.indexCache.access();
        var context = this.dict.context;
        this.dict.set(PDFName.of('W'), context.obj(byteWidths));
        this.dict.set(PDFName.of('Index'), context.obj(index));
    };
    PDFCrossRefStream.create = function (dict, encode) {
        if (encode === void 0) { encode = true; }
        var stream = new PDFCrossRefStream(dict, [], encode);
        stream.addDeletedEntry(PDFRef.of(0, 65535), 0);
        return stream;
    };
    PDFCrossRefStream.of = function (dict, entries, encode) {
        if (encode === void 0) { encode = true; }
        return new PDFCrossRefStream(dict, entries, encode);
    };
    return PDFCrossRefStream;
}(PDFFlateStream));
export default PDFCrossRefStream;
//# sourceMappingURL=PDFCrossRefStream.js.map