/**
 * Request Analyzer
 * Belongs to Decentraleyes.
 *
 * @author      Thomas Rientjes
 * @since       2014-05-30
 * @license     MPL 2.0
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 */

'use strict';

/**
 * Imports
 */

/**
 * Resource version mappings.
 * @var {object} mappings
 */
var mappings = require('./mappings');

/**
 * Gets and sets add-on specific preferences.
 * @var {object} simplePreferences
 */
var simplePreferences = require('sdk/simple-prefs');

/**
 * Constants
 */

const MAPPING_FILE_EXPRESSION = new RegExp('.map$', 'i');
const VERSION_EXPRESSION = /(?:\d{1,2}\.){1,3}\d{1,2}/;
const VERSION_PLACEHOLDER = '{version}';
const WEB_PREFIX_VALUE = 'www.';
const WEB_PREFIX_LENGTH = WEB_PREFIX_VALUE.length;
const VALUE_SEPARATOR = ';';

/**
 * Variables
 */

var preferences = simplePreferences.prefs;
var whitelistedDomains = {};

/**
 * Initializations
 */

_applyWhitelistPreference();

/**
 * Event Handlers
 */

simplePreferences.on('domainWhitelist', _applyWhitelistPreference);

/**
 * Public Methods
 */

exports.isValidCandidate = function (httpChannel) {

    // See if the request is targeted at a Content Delivery Network.
    if (mappings[httpChannel.URI.host] === undefined) {
        return false;
    }

    // Attempt to determine the domain of the request initiator.
    var initiatorDomain =
        httpChannel.loadInfo && httpChannel.loadInfo.loadingDocument && httpChannel.loadInfo.loadingDocument.domain ||
        httpChannel.referrer && httpChannel.referrer.host;

    // If the request initiator could be determined and is whitelisted.
    if (initiatorDomain && whitelistedDomains[_normalizeDomain(initiatorDomain)]) {

        // Remove referer header from request.
        httpChannel.setRequestHeader('Referer', null, false);
        return false;
    }

    // Only requests of type GET can be valid candidates.
    return httpChannel.requestMethod === 'GET';
};

exports.getLocalTarget = function (channelHost, channelPath) {

    var hostMappings, basePath, resourceMappings;

    // Use the proper mappings for the targeted host.
    hostMappings = mappings[channelHost];

    // Resource mapping files are never locally available.
    if (MAPPING_FILE_EXPRESSION.test(channelPath)) {
        return false;
    }

    basePath = _matchBasePath(hostMappings, channelPath);
    resourceMappings = hostMappings[basePath];

    if (!resourceMappings) {
        return false;
    }

    // Return either the local target's path or false.
    return _findLocalTarget(resourceMappings, basePath, channelPath);
};

/**
 * Private Methods
 */

function _matchBasePath(hostMappings, channelPath) {

    for (let basePath of Object.keys(hostMappings)) {

        if (channelPath.startsWith(basePath)) {
            return basePath;
        }
    }

    return false;
}

function _findLocalTarget(resourceMappings, basePath, channelPath) {

    var resourcePath, versionNumber, resourcePattern;

    resourcePath = channelPath.replace(basePath, '');

    versionNumber = resourcePath.match(VERSION_EXPRESSION);
    resourcePattern = resourcePath.replace(versionNumber, VERSION_PLACEHOLDER);

    for (let resourceMold of Object.keys(resourceMappings)) {

        if (resourcePattern.startsWith(resourceMold)) {

            // Prepare and return a local target.
            return {
                path: resourceMappings[resourceMold].path.replace(VERSION_PLACEHOLDER, versionNumber),
                type: resourceMappings[resourceMold].type
            };
        }
    }

    return false;
}

function _normalizeDomain(domain) {

    domain = domain.toLowerCase().trim();

    if (domain.startsWith(WEB_PREFIX_VALUE)) {
        domain = domain.slice(WEB_PREFIX_LENGTH);
    }

    return domain;
}

function _applyWhitelistPreference() {

    whitelistedDomains = {};

    //noinspection JSUnresolvedVariable
    preferences.domainWhitelist.split(VALUE_SEPARATOR).forEach(function (domain) {
        whitelistedDomains[_normalizeDomain(domain)] = true;
    });
}