"use strict";
/**
 * -------------------------------------------------------------------------------------------
 * Copyright (c) Microsoft Corporation.  All Rights Reserved.  Licensed under the MIT License.
 * See License in the project root for license information.
 * -------------------------------------------------------------------------------------------
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.LargeFileUploadTask = void 0;
var tslib_1 = require("tslib");
/**
 * @module LargeFileUploadTask
 */
var GraphClientError_1 = require("../GraphClientError");
var GraphResponseHandler_1 = require("../GraphResponseHandler");
var ResponseType_1 = require("../ResponseType");
var Range_1 = require("./FileUploadTask/Range");
var UploadResult_1 = require("./FileUploadTask/UploadResult");
/**
 * @class
 * Class representing LargeFileUploadTask
 */
var LargeFileUploadTask = /** @class */ (function () {
    /**
     * @public
     * @constructor
     * Constructs a LargeFileUploadTask
     * @param {Client} client - The GraphClient instance
     * @param {FileObject} file - The FileObject holding details of a file that needs to be uploaded
     * @param {LargeFileUploadSession} uploadSession - The upload session to which the upload has to be done
     * @param {LargeFileUploadTaskOptions} options - The upload task options
     * @returns An instance of LargeFileUploadTask
     */
    function LargeFileUploadTask(client, file, uploadSession, options) {
        if (options === void 0) { options = {}; }
        /**
         * @private
         * Default value for the rangeSize
         */
        this.DEFAULT_FILE_SIZE = 5 * 1024 * 1024;
        this.client = client;
        if (!file.sliceFile) {
            throw new GraphClientError_1.GraphClientError("Please pass the FileUpload object, StreamUpload object or any custom implementation of the FileObject interface");
        }
        else {
            this.file = file;
        }
        this.file = file;
        if (!options.rangeSize) {
            options.rangeSize = this.DEFAULT_FILE_SIZE;
        }
        this.options = options;
        this.uploadSession = uploadSession;
        this.nextRange = new Range_1.Range(0, this.options.rangeSize - 1);
    }
    /**
     * @public
     * @static
     * @async
     * Makes request to the server to create an upload session
     * @param {Client} client - The GraphClient instance
     * @param {string} requestUrl - The URL to create the upload session
     * @param {any} payload - The payload that needs to be sent
     * @param {KeyValuePairObjectStringNumber} headers - The headers that needs to be sent
     * @returns The promise that resolves to LargeFileUploadSession
     */
    LargeFileUploadTask.createUploadSession = function (client, requestUrl, payload, headers) {
        if (headers === void 0) { headers = {}; }
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var session, largeFileUploadSession;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, client
                            .api(requestUrl)
                            .headers(headers)
                            .post(payload)];
                    case 1:
                        session = _a.sent();
                        largeFileUploadSession = {
                            url: session.uploadUrl,
                            expiry: new Date(session.expirationDateTime),
                            isCancelled: false,
                        };
                        return [2 /*return*/, largeFileUploadSession];
                }
            });
        });
    };
    /**
     * @private
     * Parses given range string to the Range instance
     * @param {string[]} ranges - The ranges value
     * @returns The range instance
     */
    LargeFileUploadTask.prototype.parseRange = function (ranges) {
        var rangeStr = ranges[0];
        if (typeof rangeStr === "undefined" || rangeStr === "") {
            return new Range_1.Range();
        }
        var firstRange = rangeStr.split("-");
        var minVal = parseInt(firstRange[0], 10);
        var maxVal = parseInt(firstRange[1], 10);
        if (Number.isNaN(maxVal)) {
            maxVal = this.file.size - 1;
        }
        return new Range_1.Range(minVal, maxVal);
    };
    /**
     * @private
     * Updates the expiration date and the next range
     * @param {UploadStatusResponse} response - The response of the upload status
     * @returns Nothing
     */
    LargeFileUploadTask.prototype.updateTaskStatus = function (response) {
        this.uploadSession.expiry = new Date(response.expirationDateTime);
        this.nextRange = this.parseRange(response.nextExpectedRanges);
    };
    /**
     * @public
     * Gets next range that needs to be uploaded
     * @returns The range instance
     */
    LargeFileUploadTask.prototype.getNextRange = function () {
        if (this.nextRange.minValue === -1) {
            return this.nextRange;
        }
        var minVal = this.nextRange.minValue;
        var maxValue = minVal + this.options.rangeSize - 1;
        if (maxValue >= this.file.size) {
            maxValue = this.file.size - 1;
        }
        return new Range_1.Range(minVal, maxValue);
    };
    /**
     * @deprecated This function has been moved into FileObject interface.
     * @public
     * Slices the file content to the given range
     * @param {Range} range - The range value
     * @returns The sliced ArrayBuffer or Blob
     */
    LargeFileUploadTask.prototype.sliceFile = function (range) {
        console.warn("The LargeFileUploadTask.sliceFile() function has been deprecated and moved into the FileObject interface.");
        if (this.file.content instanceof ArrayBuffer || this.file.content instanceof Blob || this.file.content instanceof Uint8Array) {
            return this.file.content.slice(range.minValue, range.maxValue + 1);
        }
        throw new GraphClientError_1.GraphClientError("The LargeFileUploadTask.sliceFile() function expects only Blob, ArrayBuffer or Uint8Array file content. Please note that the sliceFile() function is deprecated.");
    };
    /**
     * @public
     * @async
     * Uploads file to the server in a sequential order by slicing the file
     * @returns The promise resolves to uploaded response
     */
    LargeFileUploadTask.prototype.upload = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var uploadEventHandlers, nextRange, err, fileSlice, rawResponse, responseBody, res;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        uploadEventHandlers = this.options && this.options.uploadEventHandlers;
                        _a.label = 1;
                    case 1:
                        if (!!this.uploadSession.isCancelled) return [3 /*break*/, 5];
                        nextRange = this.getNextRange();
                        if (nextRange.maxValue === -1) {
                            err = new Error("Task with which you are trying to upload is already completed, Please check for your uploaded file");
                            err.name = "Invalid Session";
                            throw err;
                        }
                        return [4 /*yield*/, this.file.sliceFile(nextRange)];
                    case 2:
                        fileSlice = _a.sent();
                        return [4 /*yield*/, this.uploadSliceGetRawResponse(fileSlice, nextRange, this.file.size)];
                    case 3:
                        rawResponse = _a.sent();
                        if (!rawResponse) {
                            throw new GraphClientError_1.GraphClientError("Something went wrong! Large file upload slice response is null.");
                        }
                        return [4 /*yield*/, GraphResponseHandler_1.GraphResponseHandler.getResponse(rawResponse)];
                    case 4:
                        responseBody = _a.sent();
                        /**
                         * (rawResponse.status === 201) -> This condition is applicable for OneDrive, PrintDocument and Outlook APIs.
                         * (rawResponse.status === 200 && responseBody.id) -> This additional condition is applicable only for OneDrive API.
                         */
                        if (rawResponse.status === 201 || (rawResponse.status === 200 && responseBody.id)) {
                            this.reportProgress(uploadEventHandlers, nextRange);
                            return [2 /*return*/, UploadResult_1.UploadResult.CreateUploadResult(responseBody, rawResponse.headers)];
                        }
                        res = {
                            expirationDateTime: responseBody.expirationDateTime || responseBody.ExpirationDateTime,
                            nextExpectedRanges: responseBody.NextExpectedRanges || responseBody.nextExpectedRanges,
                        };
                        this.updateTaskStatus(res);
                        this.reportProgress(uploadEventHandlers, nextRange);
                        return [3 /*break*/, 1];
                    case 5: return [2 /*return*/];
                }
            });
        });
    };
    LargeFileUploadTask.prototype.reportProgress = function (uploadEventHandlers, nextRange) {
        if (uploadEventHandlers && uploadEventHandlers.progress) {
            uploadEventHandlers.progress(nextRange, uploadEventHandlers.extraCallbackParam);
        }
    };
    /**
     * @public
     * @async
     * Uploads given slice to the server
     * @param {ArrayBuffer | Blob | File} fileSlice - The file slice
     * @param {Range} range - The range value
     * @param {number} totalSize - The total size of a complete file
     * @returns The response body of the upload slice result
     */
    LargeFileUploadTask.prototype.uploadSlice = function (fileSlice, range, totalSize) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.client
                            .api(this.uploadSession.url)
                            .headers({
                            "Content-Length": "".concat(range.maxValue - range.minValue + 1),
                            "Content-Range": "bytes ".concat(range.minValue, "-").concat(range.maxValue, "/").concat(totalSize),
                            "Content-Type": "application/octet-stream",
                        })
                            .put(fileSlice)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    /**
     * @public
     * @async
     * Uploads given slice to the server
     * @param {unknown} fileSlice - The file slice
     * @param {Range} range - The range value
     * @param {number} totalSize - The total size of a complete file
     * @returns The raw response of the upload slice result
     */
    LargeFileUploadTask.prototype.uploadSliceGetRawResponse = function (fileSlice, range, totalSize) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.client
                            .api(this.uploadSession.url)
                            .headers({
                            "Content-Length": "".concat(range.maxValue - range.minValue + 1),
                            "Content-Range": "bytes ".concat(range.minValue, "-").concat(range.maxValue, "/").concat(totalSize),
                            "Content-Type": "application/octet-stream",
                        })
                            .responseType(ResponseType_1.ResponseType.RAW)
                            .put(fileSlice)];
                    case 1: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    /**
     * @public
     * @async
     * Deletes upload session in the server
     * @returns The promise resolves to cancelled response
     */
    LargeFileUploadTask.prototype.cancel = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var cancelResponse;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.client
                            .api(this.uploadSession.url)
                            .responseType(ResponseType_1.ResponseType.RAW)
                            .delete()];
                    case 1:
                        cancelResponse = _a.sent();
                        if (cancelResponse.status === 204) {
                            this.uploadSession.isCancelled = true;
                        }
                        return [2 /*return*/, cancelResponse];
                }
            });
        });
    };
    /**
     * @public
     * @async
     * Gets status for the upload session
     * @returns The promise resolves to the status enquiry response
     */
    LargeFileUploadTask.prototype.getStatus = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var response;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.client.api(this.uploadSession.url).get()];
                    case 1:
                        response = _a.sent();
                        this.updateTaskStatus(response);
                        return [2 /*return*/, response];
                }
            });
        });
    };
    /**
     * @public
     * @async
     * Resumes upload session and continue uploading the file from the last sent range
     * @returns The promise resolves to the uploaded response
     */
    LargeFileUploadTask.prototype.resume = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this.getStatus()];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, this.upload()];
                    case 2: return [2 /*return*/, _a.sent()];
                }
            });
        });
    };
    /**
     * @public
     * @async
     * Get the upload session information
     * @returns The large file upload session
     */
    LargeFileUploadTask.prototype.getUploadSession = function () {
        return this.uploadSession;
    };
    return LargeFileUploadTask;
}());
exports.LargeFileUploadTask = LargeFileUploadTask;
//# sourceMappingURL=LargeFileUploadTask.js.map