"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StreamUpload = void 0;
var tslib_1 = require("tslib");
var GraphClientError_1 = require("../../../GraphClientError");
/**
 * @class
 * FileObject class for Readable Stream upload
 */
var StreamUpload = /** @class */ (function () {
    function StreamUpload(content, name, size) {
        this.content = content;
        this.name = name;
        this.size = size;
        if (!content || !name || !size) {
            throw new GraphClientError_1.GraphClientError("Please provide the Readable Stream content, name of the file and size of the file");
        }
    }
    /**
     * @public
     * Slices the file content to the given range
     * @param {Range} range - The range value
     * @returns The sliced file part
     */
    StreamUpload.prototype.sliceFile = function (range) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var rangeSize, bufs, previousRangeMin, previousRangeMax, _a, _b, slicedChunk;
            return tslib_1.__generator(this, function (_c) {
                switch (_c.label) {
                    case 0:
                        rangeSize = range.maxValue - range.minValue + 1;
                        bufs = [];
                        /**
                         * The sliceFile reads the first `rangeSize` number of bytes from the stream.
                         * The previousSlice property is used to seek the range of bytes in the previous slice.
                         * Suppose, the sliceFile reads bytes from `10 - 20` from the stream but the upload of this slice fails.
                         * When the user resumes, the stream will have bytes from position 21.
                         * The previousSlice.Range is used to compare if the requested range is cached in the previousSlice property or present in the Readable Stream.
                         */
                        if (this.previousSlice) {
                            if (range.minValue < this.previousSlice.range.minValue) {
                                throw new GraphClientError_1.GraphClientError("An error occurred while uploading the stream. Please restart the stream upload from the first byte of the file.");
                            }
                            if (range.minValue < this.previousSlice.range.maxValue) {
                                previousRangeMin = this.previousSlice.range.minValue;
                                previousRangeMax = this.previousSlice.range.maxValue;
                                // Check if the requested range is same as previously sliced range
                                if (range.minValue === previousRangeMin && range.maxValue === previousRangeMax) {
                                    return [2 /*return*/, this.previousSlice.fileSlice];
                                }
                                /**
                                 * The following check considers a possibility
                                 * of an upload failing after some of the bytes of the previous slice
                                 * were successfully uploaded.
                                 * Example - Previous slice range - `10 - 20`. Current requested range is `15 - 20`.
                                 */
                                if (range.maxValue === previousRangeMax) {
                                    return [2 /*return*/, this.previousSlice.fileSlice.slice(range.minValue, range.maxValue + 1)];
                                }
                                /**
                                 * If an upload fails after some of the bytes of the previous slice
                                 * were successfully uploaded and the new Range.Maximum is greater than the previous Range.Maximum
                                 * Example - Previous slice range - `10 - 20`. Current requested range is `15 - 25`,
                                 * then read the bytes from position 15 to 20 from previousSlice.fileSlice and read bytes from position 21 to 25 from the Readable Stream
                                 */
                                bufs.push(this.previousSlice.fileSlice.slice(range.minValue, previousRangeMax + 1));
                                rangeSize = range.maxValue - previousRangeMax;
                            }
                        }
                        if (!(this.content && this.content.readable)) return [3 /*break*/, 4];
                        if (!(this.content.readableLength >= rangeSize)) return [3 /*break*/, 1];
                        bufs.push(this.content.read(rangeSize));
                        return [3 /*break*/, 3];
                    case 1:
                        _b = (_a = bufs).push;
                        return [4 /*yield*/, this.readNBytesFromStream(rangeSize)];
                    case 2:
                        _b.apply(_a, [_c.sent()]);
                        _c.label = 3;
                    case 3: return [3 /*break*/, 5];
                    case 4: throw new GraphClientError_1.GraphClientError("Stream is not readable.");
                    case 5:
                        slicedChunk = Buffer.concat(bufs);
                        this.previousSlice = { fileSlice: slicedChunk, range: range };
                        return [2 /*return*/, slicedChunk];
                }
            });
        });
    };
    /**
     * @private
     * Reads the specified byte size from the stream
     * @param {number} size - The size of bytes to be read
     * @returns Buffer with the given length of data.
     */
    StreamUpload.prototype.readNBytesFromStream = function (size) {
        var _this = this;
        return new Promise(function (resolve, reject) {
            var chunks = [];
            var remainder = size;
            var length = 0;
            _this.content.on("end", function () {
                if (remainder > 0) {
                    return reject(new GraphClientError_1.GraphClientError("Stream ended before reading required range size"));
                }
            });
            _this.content.on("readable", function () {
                /**
                 * (chunk = this.content.read(size)) can return null if size of stream is less than 'size' parameter.
                 * Read the remainder number of bytes from the stream iteratively as they are available.
                 */
                var chunk;
                while (length < size && (chunk = _this.content.read(remainder)) !== null) {
                    length += chunk.length;
                    chunks.push(chunk);
                    if (remainder > 0) {
                        remainder = size - length;
                    }
                }
                if (length === size) {
                    return resolve(Buffer.concat(chunks));
                }
                if (!_this.content || !_this.content.readable) {
                    return reject(new GraphClientError_1.GraphClientError("Error encountered while reading the stream during the upload"));
                }
            });
        });
    };
    return StreamUpload;
}());
exports.StreamUpload = StreamUpload;
//# sourceMappingURL=StreamUpload.js.map