// create some functions to allow us to specify dependencies.
// for example, if I am writing script 'foo', I might put the
// following at the top of the foo.js file:
//
//
// Dependencies.declare('foo');
// Dependencies.dependsOn('bar');
// Dependencies.conflicts('unfoo');
// Dependencies.loadBefore('fooPart2');
//
// which would mean, respectively:
//  - this file is 'foo'
//  - this file  depends on 'bar', so please show an error if it hasn't
//    already been loaded
//  - this file  conflicts with 'unfoo', so make sure that nobody loads both
//    of these files at once.
//  - this file doesn't conflict with 'fooPart2' per se, but this file should
//    be loaded before that other file is - so if that file has already been
//    loaded, generate an error.
//
// 'declare' should always be the first call present, but after that the calls
// can appear in any order.
//
// For the most part, we require all scripts that participate in this system
// to contain a "declare" call. With certain third-party libraries, that may
// be impossible. Thus the _detect() function below tests for the presence of
// third-party libraries by looking for methods they define.

var Dependencies = {
  // Methods and objects that begin with _ should be considered private
  // and should not be accessed outside of this class.

  _knownScripts : new Object(),

  _lastScriptId : null,

  _throwScriptIsAbsent :
    function(missingScriptId) {
      throw new Error('The script file ' + this._lastScriptId + ' depends on the script ' +
                      missingScriptId + ', which has not been loaded.');
    },

  _throwScriptConflicts :
    function(firstConflictingScript, secondConflictingScript) {
      throw new Error('The script file ' + firstConflictingScript +
                      ' is not compatible with or should not be loaded with the script ' +
                      secondConflictingScript + ', and this file is attempting to load both.');
    },

  _throwScriptAlreadyLoaded :
    function(loadedScriptId) {
      throw new Error('The script file ' + this._lastScriptId + ' should be loaded before the file ' +
                      loadedScriptId + ', but that file has already been loaded.');
    },

  _throwMultipleDefinition :
    function(scriptId) {
      throw new Error('The script file ' + scriptId + ' has already been loaded.');
    },

  _setDefined :
    function(scriptId) {
      this._knownScripts[scriptId] = 'defined';
    },

  _isDefined :
    function(scriptId) {
      return this._knownScripts[scriptId] == 'defined';
    },

  _getConflicts :
    function(scriptId) {
      var entry = this._knownScripts[scriptId];
      if (entry && entry.startsWith('conflicts:')) {
        return entry.substring('conflicts:'.length).split(',');
      }
      return null;
    },

  // precondition: script is not defined.
  _addConflict :
    function(conflictedScript, incompatibleScript) {
      var entry = this._knownScripts[conflictedScript];
      if (!entry) {
        this._knownScripts[conflictedScript] = 'conflicts:' + incompatibleScript;
      }
      else {
        // we know it's not "defined". Must already be conflicted.
        this._knownScripts[conflictedScript] = entry + ',' + incompatibleScript;
      }

      return null;
    },

  // Declare a script as defined, unless it conflict with an existing script.
  // Separate from declare() to avoid declare->detect->declare infinite loops.
  _declare :
    function(scriptId) {
      this._lastScriptId = scriptId;
      var conflicts = this._getConflicts(scriptId);
      if (conflicts) {
        this._throwScriptConflicts(scriptId, conflicts[0]);
      }
      else {
        this._setDefined(scriptId);
      }
    },

  // autodetect if certain important libraries exist. Note that these aren't our
  // libraries, so they can't use this library directly.
  _detect :
    function() {
      if (!this._isDefined('prototype') &&
          typeof(Prototype) != 'undefined') {

        this._declare('prototype');
      }

      if (!this._isDefined('atlascompat') &&
          typeof(Web) != 'undefined' &&
          Web.Browser &&  window.Web.Browser._isMozilla) {

        // atlas compat has been loaded - it's the firefox compatibility piece used in VE.
        this._declare('atlascompat');
      }

      if (!this._isDefined('virtualearth') &&
          typeof(Msn) != 'undefined' &&
          Msn.VE && Msn.VE.API) {

        // virtual earth has been loaded
        this._declare('virtualearth');
      }
    },

  declare :
    function(scriptId) {
      if (this._isDefined(scriptId)) {
        this._throwMultipleDefinition(scriptId);
      }

      this._detect();

      this._declare(scriptId);
    },

  dependsOn :
    function(scriptId) {
      if (!this._isDefined(scriptId)) {
        this._throwScriptIsAbsent(scriptId);
      }
    },

  conflicts :
    function(scriptId) {
      // if it's already loaded, generate error
      if (this._isDefined(scriptId)) {
        this._throwScriptConflicts(_lastScriptId, scriptId);
      }

      // set up so that errors will be thrown in the future
      this._addConflict(scriptId, this._lastScriptId);
    },

  loadBefore :
    function(scriptId) {
      if (this._isDefined(scriptId)) {
        this._throwScriptAlreadyLoaded(scriptId);
      }
    }
};

//use this here
Dependencies.declare('depend');