"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemoteAdapter = void 0;
const Network_1 = require("../Utilities/Network");
const NetworkAdapter_1 = require("./NetworkAdapter");
class RemoteAdapter extends NetworkAdapter_1.NetworkAdapter {
    constructor(hosts) {
        super();
        /**
         * @internal
         */
        this.type = 'remote';
        this.sources = {};
        this.sourcesById = {};
        if (hosts) {
            this.hosts = hosts;
        }
        // Create a new CDM Http Client without base URL.
        this.httpClient = new Network_1.CdmHttpClient();
    }
    get hosts() {
        return this._hosts;
    }
    set hosts(val) {
        this._hosts = val;
        for (const pair of this._hosts) {
            this.getOrRegisterHostInfo(pair[1], pair[0]);
        }
    }
    clearCache() {
        this.sources = {};
        this.sourcesById = {};
    }
    canRead() {
        return true;
    }
    readAsync(corpusPath) {
        const _super = Object.create(null, {
            executeRequest: { get: () => super.executeRequest }
        });
        return __awaiter(this, void 0, void 0, function* () {
            const url = this.createAdapterPath(corpusPath);
            const cdmHttpRequest = this.setUpCdmRequest(url, new Map([['User-Agent', 'CDM']]), 'GET');
            const cdmHttpResponse = yield _super.executeRequest.call(this, cdmHttpRequest);
            return cdmHttpResponse.content.toString();
        });
    }
    createAdapterPath(corpusPath) {
        const urlConfig = this.getUrlConfig(corpusPath);
        return `${urlConfig.protocol}://${urlConfig.host}${urlConfig.path}`;
    }
    createCorpusPath(adapterPath) {
        if (!adapterPath) {
            return undefined;
        }
        const protocolIndex = adapterPath.indexOf('://');
        if (protocolIndex === -1) {
            return;
        }
        const pathIndex = adapterPath.indexOf('/', protocolIndex + 3);
        const path = pathIndex !== -1 ? adapterPath.slice(pathIndex) : '';
        const hostInfo = this.getOrRegisterHostInfo(adapterPath);
        return `/${hostInfo.key}${path}`;
    }
    getGuid() {
        let guid;
        // make sure that there is no collision of keys.
        do {
            // tslint:disable-next-line:insecure-random
            guid = Math.random()
                .toString(36)
                .substring(7);
        } while (!!this.sourcesById[guid]);
        return guid;
    }
    fetchConfig() {
        const resultConfig = {
            type: this.type
        };
        const hostsArray = [];
        const configObject = {};
        if (this.hosts !== undefined) {
            // Go through the Hosts dictionary and build a JObject for each item.
            for (const host of this.hosts) {
                const hostItem = {};
                hostItem[host[0]] = host[1];
                hostsArray.push(hostItem);
            }
            configObject.hosts = hostsArray;
        }
        // Try constructing network configs.
        const networkConfigList = this.fetchNetworkConfig();
        for (const prop of Object.keys(networkConfigList)) {
            configObject[prop] = networkConfigList[prop];
        }
        if (this.locationHint) {
            configObject.locationHint = this.locationHint;
        }
        resultConfig.config = configObject;
        return JSON.stringify(resultConfig);
    }
    /// <inheritdoc />
    updateConfig(config) {
        if (!config) {
            throw new Error('Remote adapter needs a config.');
        }
        this.updateNetworkConfig(config);
        const configJson = JSON.parse(config);
        if (configJson.locationHint) {
            this.locationHint = configJson.locationHint;
        }
        const hosts = configJson.hosts;
        // Create a temporary dictionary.
        const hostsDict = new Map();
        // Iterate through all of the items in the hosts array.
        for (const host of hosts) {
            // Get the property's key and value and save it to the dictionary.
            for (const hostProperty of Object.entries(host)) {
                hostsDict.set(hostProperty[0], hostProperty[1]);
            }
        }
        // Assign the temporary dictionary to the Hosts dictionary.
        this.hosts = hostsDict;
    }
    getOrRegisterHostInfo(adapterPath, key) {
        const protocolIndex = adapterPath.indexOf('://');
        if (protocolIndex === -1) {
            return;
        }
        const pathIndex = adapterPath.indexOf('/', protocolIndex + 3);
        const hostIndex = pathIndex !== -1 ? pathIndex : adapterPath.length;
        const protocol = adapterPath.slice(0, protocolIndex);
        const host = adapterPath.slice(protocolIndex + 3, hostIndex);
        const fullHost = adapterPath.slice(0, hostIndex);
        if (!this.sources[fullHost] || (key !== undefined && this.sources[fullHost] !== key)) {
            const guid = key ? key : this.getGuid();
            this.sources[fullHost] = guid;
            this.sourcesById[guid] = {
                protocol,
                host
            };
        }
        return {
            key: this.sources[fullHost],
            protocol,
            host
        };
    }
    getUrlConfig(corpusPath) {
        const hostKeyIndex = corpusPath.indexOf('/', 1);
        const hostKey = corpusPath.slice(1, hostKeyIndex);
        if (!this.sourcesById[hostKey]) {
            throw new Error('Host id not identified by remote adapter. Make sure to use makeCorpusPath to get the corpus path.');
        }
        const path = corpusPath.slice(hostKeyIndex);
        return Object.assign(Object.assign({}, this.sourcesById[hostKey]), { path });
    }
}
exports.RemoteAdapter = RemoteAdapter;
//# sourceMappingURL=RemoteAdapter.js.map