
// refers to a specific context
var contextHandlers = {};
var leavingContextHandlers = {};

// a list of commands to be done
var commandQue = {commands:[]};

// what the hash represents (the last part of the URL #...)
var hashState = {};

var currentContext = null;

function getHashState(hash) {
  hashSplit = hash.split("?");
  hashUrl = {};
  hashUrl.path = hashSplit[0];
  var t = hashUrl.path.indexOf("/");
  if (t != -1) {
      hashUrl.context = hashUrl.path.substring(0, t);
      hashUrl.path = hashUrl.path.substring(t);
  } else {
      hashUrl.context = hashUrl.path;
      hashUrl.path = "";
  }

  hashUrl.params = {};
  if (hashSplit.length == 2) {
      pms = hashSplit[1].split("&");
      for(var x=0;x<pms.length;x++) {
          var e = pms[x].indexOf("=");
          if (e == -1)
              hashUrl.params[pms] = null;
             else hashUrl.params[pms[x].substring(0, e)] = pms[x].substring(e+1);
      }
  }
  return hashUrl;
}


// Context handler should look like {context:string, f:function(),arg:variant}
function addContextHandler(contextHandler, leavingContextHandler) {
    if ((!contextHandler.context) || (contextHandler.context == "")) {
        context = "default";
    }

    if (!contextHandlers[contextHandler.context])
       contextHandlers[contextHandler.context] = [];
    contextHandlers[contextHandler.context].push(contextHandler);

    if (leavingContextHandler) {
      if (!leavingContextHandlers[leavingContextHandler.context])
         leavingContextHandlers[leavingContextHandler.context] = [];
      leavingContextHandlers[leavingContextHandler.context].push(leavingContextHandler);
    }
}

function castCmd(cmd) {
  if (typeof cmd == 'function') {
     return {
       f: cmd,
       visits: 0
     }
  } else {
    cmd.visits = 0;
  }
  return cmd;
}

// adds a command to the general que being executed...
function addToCommandQue(command) {
    command = castCmd(command);
    commandQue.commands.push(command);
}

function insertInCommandQue(command) {
  command = castCmd(command);
  commandQue.commands.unshift(command);
}

function processContext(context) {
    if (!context || (context == ""))
       context = "default";
    if (currentContext != context) {
        if (leavingContextHandlers[currentContext] && (leavingContextHandlers[currentContext].length > 0)) {
            for(var x=0;x<leavingContextHandlers[currentContext].length;x++) {
                leavingContextHandlers[currentContext][x].oldContext = currentContext;
                leavingContextHandlers[currentContext][x].newContext = context;
                clearContextCommands(currentContext);
                addToCommandQue(leavingContextHandlers[currentContext][x]);
            }
        }
        currentContext = context;
    }
    if (contextHandlers[context] && (contextHandlers[context].length > 0)) {
        for(var x=0;x<contextHandlers[context].length;x++) {
            addToCommandQue(contextHandlers[context][x]);
        }
    }
}

function appendToArgs(args, w) {
  var a = [];
  for(var x=0;x<args.length;x++)
      a.push(args[x]);
  a.push(w);
  return a;
}

function processCommandQue() {
    while(commandQue.commands.length > 0) {
        var cmd = commandQue.commands[0];
        var args = appendToArgs(arguments, cmd);
        cmd.visits++;
        if (cmd.visits > 7) {
           commandQue.commands.shift();
           continue;
         }
        if (cmd.f.apply(this, args) == -1)
          return;
        commandQue.commands.shift();
    }
}

function clearContextCommands(context) {
    var x=0;
    while(x<commandQue.commands.length) {
        var cmd = commandQue.commands[x];
        if (cmd.context == context) {
          commandQue.commands.shift();
          continue;
        }
        x++;
    }
}

function handleContextOnLoad(hash) {
  hashState = getHashState(hash);
  processContext(hashState.context);
  processCommandQue();
}
