"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseUrlParameters = exports.appendParameters = exports.stringifyQuery = exports.parseUrl = exports.ensureParameters = exports.parseComponentUri = exports.isComponentUri = exports.areUrlsSame = void 0;
var tslib_1 = require("tslib");
var qs_1 = tslib_1.__importDefault(require("qs"));
var string_1 = require("./string");
var parse = require("url-parse");
var DEFAULT_PARSE_OPTIONS = { ignoreQueryPrefix: true };
/**
 * Indicates if the given arrays are equal
 *
 * @param {(string[]|null)} a
 * @param {(string[]|null)} b
 * @returns {boolean}
 */
function arraysEqual(a, b) {
    if (a === b)
        return true;
    if (a == null || b == null)
        return false;
    if (a.length != b.length)
        return false;
    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.
    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) {
            return false;
        }
    }
    return true;
}
/**
 * Indicates if the given sets of parameterse are the same
 *
 * @param {*} params1
 * @param {*} params2
 * @returns {boolean}
 */
function areParamsEqual(params1, params2) {
    //HACK: locale is an optional part of the mapname/session/locale triplet
    //For the purpose of the same url test, the presence (or lack thereof) of
    //this parameter should not break the url equality test
    var keys1 = Object.keys(params1).filter(function (k) { return k.toLowerCase() != "locale"; }).sort();
    var keys2 = Object.keys(params2).filter(function (k) { return k.toLowerCase() != "locale"; }).sort();
    if (arraysEqual(keys1, keys2)) {
        for (var _i = 0, keys1_1 = keys1; _i < keys1_1.length; _i++) {
            var key = keys1_1[_i];
            if (params1[key] != params2[key]) {
                return false;
            }
        }
        return true;
    }
    return false;
}
/**
 * Indicates if the given URLs are the same
 *
 * @export
 * @param {string} url1
 * @param {string} url2
 * @returns {boolean}
 */
function areUrlsSame(url1, url2) {
    var parsed1 = parse(url1);
    var parsed2 = parse(url2);
    var params1 = qs_1.default.parse(parsed1.query, DEFAULT_PARSE_OPTIONS);
    var params2 = qs_1.default.parse(parsed2.query, DEFAULT_PARSE_OPTIONS);
    var same = parsed1.protocol == parsed2.protocol
        && parsed1.slashes == parsed2.slashes
        && parsed1.auth == parsed2.auth
        && parsed1.username == parsed2.username
        && parsed1.password == parsed2.password
        && parsed1.host == parsed2.host
        && parsed1.hostname == parsed2.hostname
        && parsed1.port == parsed2.port
        && parsed1.pathname == parsed2.pathname
        && parsed1.hash == parsed2.hash
        && parsed1.host == parsed2.host
        && areParamsEqual(params1, params2);
    return same;
}
exports.areUrlsSame = areUrlsSame;
/**
 * Indicates if the given URI is a component URI
 *
 * @export
 * @param {string} uri
 * @returns {boolean}
 */
function isComponentUri(uri) {
    return uri.indexOf("component://") >= 0;
}
exports.isComponentUri = isComponentUri;
/**
 * Parses the given component URI. If it not a valid component URI returns undefined
 *
 * @export
 * @param {string} uri
 * @returns {(ParsedComponentUri | undefined)}
 */
function parseComponentUri(uri) {
    if (isComponentUri(uri)) {
        var qi = uri.lastIndexOf("?");
        var name_1 = qi < 0 ? uri.substring(12) : uri.substring(12, qi);
        var props = qi < 0 ? {} : qs_1.default.parse(uri.substring(qi), DEFAULT_PARSE_OPTIONS);
        return {
            name: name_1,
            props: props
        };
    }
}
exports.parseComponentUri = parseComponentUri;
/**
 * Normalizes the given URL to ensure it has the baseline set of required parameters for invoking any server-side script that uses the MapGuide Web API
 *
 * @export
 * @param {string} url The url to normalize
 * @param {(string | undefined)} mapName The name of the current runtime map
 * @param {(string | undefined)} session The current session id
 * @param {string} [locale] An optional locale
 * @param {boolean} [uppercase=true] If true, will uppercase all parameter names
 * @param {IInvokeUrlCommandParameter[]} [extraParameters=[]] Any extra parameters to append to the URL
 * @returns {string}
 */
function ensureParameters(url, mapName, session, locale, uppercase, extraParameters) {
    if (uppercase === void 0) { uppercase = true; }
    if (extraParameters === void 0) { extraParameters = []; }
    //If this is a component URL, let it be
    if (isComponentUri(url)) {
        return url;
    }
    var parsed = parseUrl(url);
    var params = parsed.query != null ? qs_1.default.parse(parsed.query, DEFAULT_PARSE_OPTIONS) : {};
    var bNeedMapName = true;
    var bNeedSession = true;
    var bNeedLocale = true;
    for (var key in params) {
        var name_2 = key.toLowerCase();
        switch (name_2) {
            case "session":
                bNeedSession = false;
                break;
            case "mapname":
                bNeedMapName = false;
                break;
            case "locale":
                bNeedLocale = false;
                break;
        }
    }
    if (bNeedMapName && !(0, string_1.strIsNullOrEmpty)(mapName)) {
        if (uppercase) {
            params.MAPNAME = mapName;
        }
        else {
            params.mapname = mapName;
        }
    }
    if (bNeedSession && !(0, string_1.strIsNullOrEmpty)(session)) {
        if (uppercase) {
            params.SESSION = session;
        }
        else {
            params.session = session;
        }
    }
    if (bNeedLocale) {
        if (uppercase) {
            params.LOCALE = locale;
        }
        else {
            params.locale = locale;
        }
    }
    for (var _i = 0, extraParameters_1 = extraParameters; _i < extraParameters_1.length; _i++) {
        var p = extraParameters_1[_i];
        params[p.name] = p.value;
    }
    /*
    parsed.query = queryString.stringify(params);
    const result = parsed.toString();

    if (url.indexOf(parsed.protocol) >= 0 || url.indexOf("/") == 0) {
        return result;
    }

    return result;
    */
    //Don't uppercase the parameters here if true, uppercasing is only for filling in
    //missing parameters
    return appendParameters(url, params, true, false /*uppercase*/);
}
exports.ensureParameters = ensureParameters;
/**
 * Parses the given URL and separates out the query string parameters
 *
 * @export
 * @param {string} url The URL to parse
 * @returns {IParsedUrl}
 * @since 0.12
 */
function parseUrl(url) {
    //return queryString.parseUrl(url);
    var qi = url.lastIndexOf("?");
    var parsedUrl = qi < 0 ? url : url.substring(0, qi);
    var query = qi < 0 ? {} : qs_1.default.parse(url.substring(qi), DEFAULT_PARSE_OPTIONS);
    return {
        url: parsedUrl,
        query: query
    };
}
exports.parseUrl = parseUrl;
/**
 * Converts the given object to a query string fragment
 *
 * @export
 * @param {*} parameters The object to stringify
 * @returns {string} The query string fragment
 */
function stringifyQuery(parameters) {
    return qs_1.default.stringify(parameters);
}
exports.stringifyQuery = stringifyQuery;
/**
 * Appends the specified parameters to the given URL
 *
 * @export
 * @param {string} url The URL to append parameters to
 * @param {*} parameters The parameters to append
 * @param {boolean} [bOverwriteExisting=true] If true, will overwrite any existing parameters if the URL already has them
 * @param {boolean} [bConvertToUppercase=false] If true, will ensure all parameter names are uppercase
 * @param {boolean} [bDiscardExistingParams=false] If true, will discard existing query string params before appending
 * @since 0.12
 */
function appendParameters(url, parameters, bOverwriteExisting, bConvertToUppercase, bDiscardExistingParams) {
    if (bOverwriteExisting === void 0) { bOverwriteExisting = true; }
    if (bConvertToUppercase === void 0) { bConvertToUppercase = false; }
    if (bDiscardExistingParams === void 0) { bDiscardExistingParams = false; }
    var parsed = parse(url);
    var currentParams;
    if (!bDiscardExistingParams) {
        currentParams = parsed.query != null ? qs_1.default.parse(parsed.query, DEFAULT_PARSE_OPTIONS) : {};
    }
    else {
        currentParams = {};
    }
    var paramNames = {};
    for (var key in currentParams) {
        paramNames[key.toUpperCase()] = key;
    }
    for (var name_3 in parameters) {
        //See if this parameter name was normalized
        var key = paramNames[name_3.toUpperCase()] || name_3;
        //If it was and we've got an existing value there, skip if not overwriting
        if (key && currentParams[key] && !bOverwriteExisting) {
            continue;
        }
        //Put the parameter value
        currentParams[key] = parameters[name_3];
    }
    if (bConvertToUppercase) {
        var params2 = {};
        for (var name_4 in currentParams) {
            params2[name_4.toUpperCase()] = currentParams[name_4];
        }
        currentParams = params2;
    }
    parsed.query = qs_1.default.stringify(currentParams);
    var result = parsed.toString();
    if (url.indexOf(parsed.protocol) >= 0 || url.indexOf("/") == 0) {
        return result;
    }
    return result;
}
exports.appendParameters = appendParameters;
/**
 * Parses the query string section of the given URL and returns the parsed
 * parameters as an object
 * @param url The URL to parse
 * @since 0.13
 */
function parseUrlParameters(url) {
    var parsed = parse(url);
    var currentParams = parsed.query != null ? qs_1.default.parse(parsed.query, DEFAULT_PARSE_OPTIONS) : {};
    return currentParams;
}
exports.parseUrlParameters = parseUrlParameters;
//# sourceMappingURL=url.js.map