"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const ts_log_debug_1 = require("ts-log-debug");
const RestClient_1 = require("typed-rest-client/RestClient");
const constants_1 = require("../constants");
const HttpClient_1 = require("typed-rest-client/HttpClient");
const klawSync = require("klaw-sync");
const mime_types_1 = require("mime-types");
const path_1 = require("path");
const fs_extra_1 = require("fs-extra");
const file_utils_1 = require("./file-utils");
const auth_1 = __importDefault(require("./auth"));
const progress_stream_1 = __importDefault(require("progress-stream"));
const multi_progress_1 = __importDefault(require("multi-progress"));
const chalk_1 = __importDefault(require("chalk"));
const zlib = __importStar(require("zlib"));
const routes = {
    version: (params) => `${constants_1.Constants.ASSET_ROOT_URL}/${params.name}/${params.version}`,
    versions: (params) => `${constants_1.Constants.ASSET_ROOT_URL}/${params.user}/${params.collection}/versions`,
    assetfile: (params) => `${constants_1.Constants.ASSETFILE_ROOT_URL}/${params.key}`
};
class Remote {
    constructor() {
        this.auth = new auth_1.default();
    }
    async getRestClient() {
        const token = await this.auth.getToken();
        const modelNestClient = new RestClient_1.RestClient('model-nest-cli', constants_1.Constants.HOST, [], {
            headers: { Authorization: `Bearer ${token}` }
        });
        return modelNestClient;
    }
    async getDownloadClient() {
        const token = await this.auth.getToken();
        const modelNestClient = new HttpClient_1.HttpClient('model-nest-cli', [], {
            headers: { Authorization: `Bearer ${token}`, 'Accept-Encoding': 'gzip' }
        });
        return modelNestClient;
    }
    async upload(assetName, localPath) {
        const client = await this.getRestClient();
        if (!fs.existsSync(localPath)) {
            ts_log_debug_1.$log.error(`${localPath} does not exist`);
            return;
        }
        let files = [path.relative(path.dirname(localPath), localPath)];
        if (fs.lstatSync(localPath).isDirectory()) {
            const fileStats = klawSync(localPath);
            for (const file of fileStats) {
                if (file_utils_1.isSourceAsset(file.path)) {
                    if (path_1.dirname(file.path) != path_1.resolve(localPath)) {
                        throw Error("Root asset file (ie. fbx or obj), must be in the root of the uploaded directory");
                    }
                }
            }
            files = fileStats.map(f => path.relative(localPath, f.path));
        }
        ts_log_debug_1.$log.debug(files);
        ts_log_debug_1.$log.debug(constants_1.Constants.HOST);
        const uploadClient = new RestClient_1.RestClient('model-nest-cli');
        try {
            const url = `assets/${encodeURIComponent(assetName)}/upload`;
            ts_log_debug_1.$log.debug(url);
            ts_log_debug_1.$log.debug(JSON.stringify({ files }, null, 2));
            const uploadUrlsResp = await client.create(url, { files });
            ts_log_debug_1.$log.debug(uploadUrlsResp);
            const uploadUrls = uploadUrlsResp.result;
            if (uploadUrls != null) {
                for (const filePath of Object.keys(uploadUrls)) {
                    const absPath = path.join(localPath, filePath);
                    if (fs.lstatSync(absPath).isFile()) {
                        const uploadResult = await uploadClient.uploadStream('PUT', uploadUrls[filePath].url, fs.createReadStream(absPath), {
                            additionalHeaders: {
                                'content-type': mime_types_1.lookup(absPath) || 'application/octet-stream'
                            }
                        });
                        ts_log_debug_1.$log.debug(uploadResult);
                    }
                }
            }
        }
        catch (err) {
            ts_log_debug_1.$log.error(`Unexpected error:\n ${JSON.stringify(err, null, 2)}`);
            return;
        }
    }
    async getAssetVersion(packageVersion) {
        const client = await this.getRestClient();
        const versionEndpoint = routes.version(packageVersion);
        const resp = await client.get(versionEndpoint);
        return resp.result;
    }
    async downloadAsset(nestDirectory, packageVersion, version) {
        const client = await this.getDownloadClient();
        const packageDir = path_1.join(nestDirectory, packageVersion.name);
        await fs_extra_1.ensureDir(packageDir);
        const format = version.formats.find(f => f.derived == false);
        const multi = new multi_progress_1.default();
        const downloads = [];
        if (format) {
            for (const assetFile of format.resources) {
                if (assetFile.embedded)
                    continue;
                const downloadPath = routes.assetfile(assetFile);
                const localPath = path_1.join(packageDir, assetFile.sourcePath);
                await fs_extra_1.ensureDir(path_1.dirname(localPath));
                let file = fs.createWriteStream(localPath);
                const downloadPromise = client.get(downloadPath);
                const promise = this.pipeToFile(assetFile, downloadPromise, file, multi);
                downloads.push(promise);
            }
        }
        await Promise.all(downloads);
        console.log('\n\nAsset download complete\n\n');
    }
    async pipeToFile(asset, downloadPromise, file, progressBar) {
        const resp = (await downloadPromise);
        const size = Number(resp.message.headers['content-length']);
        const str = progress_stream_1.default({
            length: size,
            time: 100 /* ms */
        });
        const bar = progressBar.newBar(`  Downloading :asset [:bar] :percent :etas remaining`, {
            complete: '=',
            incomplete: ' ',
            total: asset.fileSize || size,
            width: 50,
        });
        str.on('data', (chunk) => {
            bar.tick(chunk.length, {
                asset: chalk_1.default.green(asset.sourcePath).padEnd(40, ' '),
            });
        });
        function gunzipPipe(stream, headers) {
            if (headers['content-encoding'] === 'gzip') {
                return stream.pipe(zlib.createGunzip());
            }
            else {
                return stream;
            }
        }
        return new Promise((resolve, reject) => {
            gunzipPipe(resp.message.pipe(str), resp.message.headers)
                .pipe(file).on('close', () => {
                resolve(asset.sourcePath);
            });
        });
    }
}
exports.default = Remote;
//# sourceMappingURL=remote.js.map