var messageController = new MessageController();

function MessageController() {
    this.queue = new Array();
    this.listeners = new Array();
    this.processing = false;
}

MessageController.prototype.addListener = msgctrl_addListener;
MessageController.prototype.removeListener = msgctrl_removeListener;
MessageController.prototype.send = msgctrl_send;
MessageController.prototype._processQueue = msgctrl_processQueue;
MessageController.prototype._getStackTrace = msgctrl_getStackTrace;
MessageController.prototype._parseErrorStack = msgctrl_parseErrorStack;

// you can add either a function or a object as an listener.
// For a function, it must take in a single param which is the msg
// For an object, it must have a function called notify(msg);
function msgctrl_addListener(listener) {
	this.listeners.push(listener);
}

// if you want support for removing the listener, your listener object
// must support the name attribute
function msgctrl_removeListener(listener) {
	for (var i = 0; i < this.listeners.length; i++) {
		if (this.listeners[i] == listener) {
			// take out this listener from the array
			this.listeners.splice(i, 1);
			return;
		}
	}
}

function msgctrl_send(msg) {
    this.queue.push(msg);
    this._processQueue();
}

function msgctrl_processQueue() {
    if(this.processing) {
        return;
    }

    this.processing = true;

    var count = 0;
    while(this.queue.length > 0) {
        var msg = this.queue.shift();

        for(var i = 0; i < this.listeners.length; i++) {
            try {
                if((typeof this.listeners[i]) == "function") {
                    this.listeners[i](msg);
                } else {
                    this.listeners[i].notify(msg);
                }
            } catch(e) {            
                Console.println("message handling error: " + e.name + ": " + e.message);
                Console.println("message: " + msg.type);
                if((typeof this.listeners[i]) == "function") {
                    Console.println("message handler: " + this.listeners[i]);
                } else {
                    Console.println("message handler: " + this.listeners[i].notify);
                }
                Console.println(commonUtil.stackTrace());
            }
        }
        count++;
        if(count > 50) { // infinite loop detection
            alert('detecting a possible infinite event loop, breaking');
            break;
        }
    }

    this.processing = false;
}

function msgctrl_getStackTrace() {
  var result = '';

  if (typeof(arguments.caller) != 'undefined') { // IE, not ECMA
    for (var a = arguments.caller; a != null; a = a.caller) {
      result += '> ' + getFunctionName(a.callee) + '\n';
      if (a.caller == a) {
        result += '*';
        break;
      }
    }
  }
  else { // Mozilla, not ECMA
    // fake an exception so we can get Mozilla's error stack
    var testExcp;
    try
    {
      foo.bar;
    }
    catch(testExcp)
    {
      var stack = this._parseErrorStack(testExcp);
      for (var i = 1; i < stack.length; i++)
      {
        result += '> ' + stack[i] + '\n';
      }
    }
  }

  return result;
}

function msgctrl_parseErrorStack(excp)
{
  var stack = [];
  var name;

  if (!excp || !excp.stack)
  {
    return stack;
  }

  var stacklist = excp.stack.split('\n');

  for (var i = 0; i < stacklist.length - 1; i++)
  {
    var framedata = stacklist[i];

    name = framedata.match(/^(\w*)/)[1];
    if (!name) {
      name = 'anonymous';
    }

    stack[stack.length] = name;
  }

  return stack;
}



var MessageType = {
    ROW_SELECTED : "row selection event",
    DATA_UPDATED : "data update event",
    DATA_INSERTED : "data insert event",
    DATA_DELETED : "data delete event",
    ONLOAD : "document load event",
    RESIZE : "resize event",
	CELL_SELECTED : "cell selection event"
}

function OnloadEvent() {
    this.type = MessageType.ONLOAD;
}

