// All methods in here are just for the browser. This file will not be loaded
// by the "server side" opal tool. A lot of methods defined here need to have
// duplicate definitions for use by the server side tool.

// =======================
// = Opal loading system =
// =======================

// "Opals" are similar to gems in vanilla ruby. An opal is like a framework of
// code and other resources.

// Register an opal with the given specification, which is a json literal with
// name, description, dependencies etc.
// 
// Example
// =======
// 
// opal.register({
//  name: "browser",
//  version: "0.1.0",
//  files: {
//    "bin/browser", function() { ... bin imp ... },
//    "lib/browser.rb": function() { ... browser.rb imp ... },
//    "lib/browser/element.rb": function() { ... element.rb imp ... }
//  }
// });
// 
// Notes
// =====
// 
// We then add the lib/ path in browser to our load path, so require('browser')
// will load lib/browser.rb, and require('browser/element') will load
// lib/browser/element.rb
// 
// All opals are stores with their name as a prefix, so lib/browser.rb as above
// will actually have a full path url of "/browser/lib/browser.rb"
// 
// Applications are initialized by calling their "bin" file, which by default is
// named identically to their opal name, so to start our "sample_controls"
// application, we initialize "/sample_controls/bin/sample_controls" which will
// probably require "/sample_controls/lib/sample_controls.rb" which will itself
// load cherry_kit etc etc. the main bin file most often than not will simply
// call something like CKApplication.start()
// 
// Resources like css could be added here, as well as auto loading for them, so
// when the main lib file is loaded, then they are automatically required.. 
// might work.
// 
// require('browser') will first search all opals, so we can carry out potential
// autoloading of css etc
// 
exports.register = function(specification) {
  // console.log("registering new opal: " + specification.name);
  opal_list[specification.name] = specification;
  
  load_paths.push(specification.name + "/lib/");
  
  for (var file_name in specification.files) {
    file_list[specification.name + "/" + file_name] = specification.files[file_name];
  }
};

// same as above but register this as the default application sequence. will
// look in this opal for a bin file with same name to be used for running
// exports.register_application = function(specification) {
//   exports.register(specification);
//   bin_file = '/' + specification.name + '/bin/' + specification.name;
// };

// array of loadpaths used in "require" .. each opal listed etc
// by default has root of filesystem, but each added opal also adds its libpath
var load_paths = [""];

// to load on window.load
var bin_file = null;

// cwd for application
var opal_cwd = null;

// list of all opals: name to specification json object
var opal_list = {};

// dictionary of all files:
// 
// /path/to_file.1: function() { ... imp ... },
// /path/to_file.2: function() { ... imp ... }
// 
// If a file has been included, then its function will be marked with an 
// property ".opal_required" and set to true
var file_list = exports.files = {};

// =======================================================
// = Temp launching - should be done via window.onload.. =
// =======================================================
// Run is our main file to run. usually app path, sometimes spec path etc
// 
// @param [String] path - the main executable file
// @param [String] path - the working directory
// @param [String] lib_path - the lib dir for the main target (default is "lib"), but could well be "ruby" or "opal" or indeed "" 
exports.run = function(path, cwd, lib_path) {
  bin_file = path;
  exports.getwd = opal_cwd = cwd;
  var require_path;
  
  if (!bin_file)
    throw "Opal: no bin file defined."
    
  var bin_path = bin_file + "/bin/" + bin_file + ".rb";
  
  if (exports.files[bin_path])
    require_path = bin_path;
    // exports.require(bin_path);
  else if (exports.files[bin_path = path + '/lib/' + path + '.rb']) {
    // bin_path = bin_file + "/lib/" + bin_file + ".rb";
    // exports.require(bin_path);
    require_path = bin_path;
  }
  else if (exports.files[bin_path = path + '/' + path + '.rb']) {
    // bin_path = bin_file + "/lib/" + bin_file + ".rb";
    // exports.require(bin_path);
    require_path = bin_path;
  }
  else {
    throw "cannot find bin file"
  }
  
  opal.entry_point(function() {
    // require our main "browser" spec as well - seems silly making the user
    // have to do this when we know for a fact we are in the browser.
    exports.require('browser');
    return exports.require(require_path);
  });
};

// require the file at the given path: we have already checked it exists - mark
// as being required - execute in context of top_self
// 
// params function(__FILE__) { .. }
var file_require_path = function(path) {
  // console.log("requiring " + path);
  var f = file_list[path];
  f.opal_required = true;
  return f.apply(exports.top_self, [path]);
};

// require the js string path.. might come from ruby, might come from js
exports.require = function(orig_path) {
  // console.log("native require: " + orig_path);
  // console.log(load_paths);
  var path = orig_path;
  // basically loop through each of the load paths looking for a match
  if ((path.substr(path.length - 3) != '.rb') && (path.substr(path.length -3) != '.js')) {
    // console.log("need to add .rb");
    path += '.rb'
  }
  
  for (var i = 0; i < load_paths.length; i++) {
    var try_path = load_paths[i] + path;
    // console.log("does exist? " + try_path);
    if (file_list.hasOwnProperty(try_path)) {
      if (file_list[try_path].opal_required) {
        // console.log("already required " + path);
        return;
      }
      // console.log("shit son!!!!");
      // console.log(file_list[try_path]);
      return file_require_path(try_path);
    }
  }
  
  throw "could not find require: " + orig_path;
};

// load the raw file, given as a function imolementation as the given filename
// 
// @param [String] filename
// @param [Function] implementation
exports.load_raw_file = function(filename, implementation) {
  return implementation.apply(exports.top_self);
};


// =========================
// = Browser bits and bobs =
// =========================

var browser = exports.browser = (function() {
  var agent = navigator.userAgent.toLowerCase();
  var version = 1;
  var browser = {
    version: 0,
    safari: (/webkit/).test(agent) ? version : 0,
    opera: (/opera/).test(agent) ? version : 0,
    msie: (/msie/).test(agent) && !(/opera/).test(agent) ? version : 0
  };
  
  return browser;
})();

// set callback for when opal/document is ready to go!
exports.setDocumentReadyListener = function(callback) {
  // run it in the context of top self
  var on_ready = function() {
    opal.entry_point(function() {
      callback.apply(opal.top_self);
    });
  };
  // attach ready function
  (function(){
    // w3c - firefox, safari, opera
    if (document.addEventListener) {
      document.addEventListener("DOMContentLoaded", on_ready, false);
    }
    // internet explorer
    if (exports.browser.msie) {
      (function() {
        try {
          document.documentElement.doScroll('left');
        }
        catch (e) {
          setTimeout(arguments.callee, 0);
          return;
        }
        on_ready();
      })();
    }

  })();
};


exports.glob_files = function(glob) {
  var working = glob.replace(/\*\*\//g, '.*').replace(/\*\*/g, '.*').replace(/\//g, '\\/');
  var result = [];
  var reg = new RegExp('^' + working + '$');
  for (var prop in opal.files) {
    if (reg.exec(prop)) {
      result.push(prop);
    }
  }
  return result;
};

// ================
// = On ready etc =
// ================
// var on_ready = function() {
  // console.log("===== on_ready");

// };


exports.ruby_platform = "browser";



// native xml http request
exports.request = (function() {
  try {
    new XMLHttpRequest();
    return function() {
      return new XMLHttpRequest();
    };
  }
  catch (e) {
    try {
      new ActiveXObject('MSXML2.XMLHTTP');
      return function() {
        return new ActiveXObject('MSXML2.XMLHTTP');
      };
    }
    catch (e) {
      try {
        new ActiveXObject('Microsoft.XMLHTTP');
        return function() {
          return new ActiveXObject('Microsoft.XMLHTTP');
        };
      }
      catch (e) {
        return function() {
          console.log("cannot create a native XMLHttpRequest");
        }
      }
    }
  }
})();