/* -*- Mode: js; js-indent-level: 2; -*- */
/*
 * Copyright 2011 Mozilla Foundation and contributors
 * Licensed under the New BSD license. See LICENSE or:
 * http://opensource.org/licenses/BSD-3-Clause
 */

/**
  * This is a helper function for getting values from parameter/options
  * objects.
  *
  * @param args The object we are extracting values from
  * @param name The name of the property we are getting.
  * @param defaultValue An optional value to return if the property is missing
  * from the object. If this is not specified and the property is missing, an
  * error will be thrown.
  */
function getArg(aArgs, aName, aDefaultValue) {
  if (aName in aArgs) {
    return aArgs[aName];
  } else if (arguments.length === 3) {
    return aDefaultValue;
  } else {
    throw new Error('"' + aName + '" is a required argument.');
  }
}
exports.getArg = getArg;

var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
var dataUrlRegexp = /^data:.+\,.+/;

function urlParse(aUrl) {
  var match = aUrl.match(urlRegexp);
  if (!match) {
    return null;
  }
  return {
    scheme: match[1],
    auth: match[3],
    host: match[4],
    port: match[6],
    path: match[7]
  };
}
exports.urlParse = urlParse;

function urlGenerate(aParsedUrl) {
  var url = aParsedUrl.scheme + "://";
  if (aParsedUrl.auth) {
    url += aParsedUrl.auth + "@"
  }
  if (aParsedUrl.host) {
    url += aParsedUrl.host;
  }
  if (aParsedUrl.port) {
    url += ":" + aParsedUrl.port
  }
  if (aParsedUrl.path) {
    url += aParsedUrl.path;
  }
  return url;
}
exports.urlGenerate = urlGenerate;

function join(aRoot, aPath) {
  var url;

  if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
    return aPath;
  }

  if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
    url.path = aPath;
    return urlGenerate(url);
  }

  return aRoot.replace(/\/$/, '') + '/' + aPath;
}
exports.join = join;

/**
  * Because behavior goes wacky when you set `__proto__` on objects, we
  * have to prefix all the strings in our set with an arbitrary character.
  *
  * See https://github.com/mozilla/source-map/pull/31 and
  * https://github.com/mozilla/source-map/issues/30
  *
  * @param String aStr
  */
function toSetString(aStr) {
  return '$' + aStr;
}
exports.toSetString = toSetString;

function fromSetString(aStr) {
  return aStr.substr(1);
}
exports.fromSetString = fromSetString;

function relative(aRoot, aPath) {
  aRoot = aRoot.replace(/\/$/, '');

  var url = urlParse(aRoot);
  if (aPath.charAt(0) == "/" && url && url.path == "/") {
    return aPath.slice(1);
  }

  return aPath.indexOf(aRoot + '/') === 0
    ? aPath.substr(aRoot.length + 1)
    : aPath;
}
exports.relative = relative;

function strcmp(aStr1, aStr2) {
  var s1 = aStr1 || "";
  var s2 = aStr2 || "";
  return (s1 > s2) - (s1 < s2);
}

/**
  * Comparator between two mappings where the original positions are compared.
  *
  * Optionally pass in `true` as `onlyCompareGenerated` to consider two
  * mappings with the same original source/line/column, but different generated
  * line and column the same. Useful when searching for a mapping with a
  * stubbed out mapping.
  */
function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
  var cmp;

  cmp = strcmp(mappingA.source, mappingB.source);
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.originalLine - mappingB.originalLine;
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.originalColumn - mappingB.originalColumn;
  if (cmp || onlyCompareOriginal) {
    return cmp;
  }

  cmp = strcmp(mappingA.name, mappingB.name);
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.generatedLine - mappingB.generatedLine;
  if (cmp) {
    return cmp;
  }

  return mappingA.generatedColumn - mappingB.generatedColumn;
};
exports.compareByOriginalPositions = compareByOriginalPositions;

/**
  * Comparator between two mappings where the generated positions are
  * compared.
  *
  * Optionally pass in `true` as `onlyCompareGenerated` to consider two
  * mappings with the same generated line and column, but different
  * source/name/original line and column the same. Useful when searching for a
  * mapping with a stubbed out mapping.
  */
function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
  var cmp;

  cmp = mappingA.generatedLine - mappingB.generatedLine;
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.generatedColumn - mappingB.generatedColumn;
  if (cmp || onlyCompareGenerated) {
    return cmp;
  }

  cmp = strcmp(mappingA.source, mappingB.source);
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.originalLine - mappingB.originalLine;
  if (cmp) {
    return cmp;
  }

  cmp = mappingA.originalColumn - mappingB.originalColumn;
  if (cmp) {
    return cmp;
  }

  return strcmp(mappingA.name, mappingB.name);
};
exports.compareByGeneratedPositions = compareByGeneratedPositions;
