var razsor = {
    definePackage : function(name) {
	    var currentPackage = window;
	    var currentPackageString = "";
	    var packages = name.split(".");
	    var packageName = packages[packages.length - 1];
	    var i;
	    for (i = 0, len = packages.length - 1; i < len; i++) {
		    currentPackageString += i === 0 ? packages[i] : "." + packages[i];

		    if (!currentPackage[packages[i]]) {
			    throw new razsor.RazorError("Cannot create \"" + name
			            + "\" because package \"" + currentPackageString
			            + "\" does not exist.");
		    }
		    currentPackage = currentPackage[packages[i]];
	    }

	    if (currentPackage[packageName]) {
		    throw new razsor.RazorError("Cannot create \"" + name
		            + "\" because \"" + packageName
		            + "\" already exists on package \"" + currentPackageString
		            + "\"");
	    } else {
		    currentPackage[packageName] = {};
		    currentPackage[packageName].toString = function() {
			    return name;
		    };
	    }
    },

    extend : function(baseObj, obj, overwrites) {
	    var item;
	    var superTest = /xyz/.test(function() {
		    return xyz;
	    }) ? /\b_super\b/ : /.*/;
	    var overrideFunction = function(item) {
		    if (!overwrites && typeof obj[item] === "function"
		            && superTest.test(obj[item])) {
			    if (baseObj[item] && typeof baseObj[item] !== "function") {
				    throw new razsor.RazorError(
				            "Can't override existing property on the base object with a function");
			    }
			    var _super = baseObj[item];
			    baseObj[item] = function() {
				    var tmp = this._super;
				    this._super = _super;

				    var ret = obj[item].apply(this, arguments);
				    this._super = tmp;
				    if (!this._super) {
					    delete this._super;
				    }

				    return ret;
			    };
		    } else {
			    baseObj[item] = obj[item];
		    }
	    };

	    for (item in obj) {
		    if (obj.hasOwnProperty(item)) {
			    overrideFunction(item);
		    }
	    }
    },

    defineClass : function(props) {
	    var _className = props._className, _package = props._package, _class = props._class, _static = props._static, _abstract = props._abstract, _implements = props._implements, _extends = props._extends;

	    if (!_className) {
		    throw new razsor.RazorError("No class name specified");
	    }
	    if (!_package) {
		    throw new razsor.RazorError(
		            "Package does not exist or package declaration missing for class "
		                    + _className);
	    }
	    if (_package[_className]) {
		    throw new razsor.RazorError(_className
		            + " already exists in package " + _package.toString());
	    }
	    if (!_class || !(_class.init || _class._init)) {
		    throw new razsor.RazorError(
		            "No class definition or no init constructor found on class "
		                    + _className);
	    }
	    if (props._extends !== undefined) {
		    if (!_extends) {
			    throw new razsor.RazorError("_super declaration for class "
			            + _className + " is undefined");
		    }
		    var superTest = /xyz/.test(function() {
			    return xyz;
		    }) ? /\b_super\b/ : /.*/;
		    if (!superTest.test(_class.init)) {
			    throw new razsor.RazorError(
			            "No call to this._super() found in constructor of class "
			                    + _className);
		    }
	    }

	    var Class = function() {
		    this.init.apply(this, arguments);
	    };

	    if (_extends) {
		    var F = function() {
		    };
		    F.prototype = _extends.prototype;
		    Class.prototype = new F();
		    Class.prototype.constructor = Class;
		    Class.prototype.__super = _extends;
	    }
	    Class.prototype.__package = _package;
	    Class.prototype.__class = Class;
	    Class.prototype.__className = _className;

	    razsor.extend(Class, _static, true);
	    razsor.extend(Class.prototype, _class);

	    if (_abstract) {
		    var item;
		    for (item in _abstract.methods) {
			    if (_abstract.methods.hasOwnProperty(item)) {
				    var methodName = _abstract.methods[item];
				    if (Class[methodName] || Class.prototype[methodName]) {
					    throw new razsor.RazorError(
					            "Concrete implementation for "
					                    + methodName
					                    + " found in abstract class "
					                    + _className
					                    + (_extends ? " or in its inheritance tree."
					                            : ""));
				    }
			    }
		    }
	    }

	    if (_abstract || _implements) {
		    var init = Class.prototype.init;
		    Class.prototype.init = function() {
			    if (_abstract) {
				    razsor.ensureImplements(this, _abstract);
			    }
			    if (_implements) {
				    var i;
				    for (i = 0; i < _implements.length; i++) {
					    razsor.ensureImplements(this, _implements[i]);
				    }
			    }

			    init.apply(this, arguments);
		    };
	    }

	    _package[_className] = Class;
    },

    Interface : function(name, methods) {
	    this.name = name;
	    var error;

	    // Interface Names
	    var regExp = /^[A-Z]{1}[\w]*$/;
	    if (arguments.length !== 2) {
		    throw new razsor.RazorError("Interface constructor called with "
		            + arguments.length + " arguments, but expected exactly 2.");
	    }
	    if (typeof name !== "string" || !regExp.test(name)) {
		    throw new razsor.RazorError(
		            "Argument 'name' must be a valid string representing a JavaScript identifier. name="
		                    + name);
	    }
	    if (!(methods instanceof Array)) {
		    throw new razsor.RazorError(
		            "Argument 'methods' must be an array. typeof methods="
		                    + typeof methods);
	    }
	    if (!methods.length) {
		    throw new razsor.RazorError(
		            "At least 1 method must be defined in Methods");
	    }

	    // Method Names
	    this.name = name;
	    this.methods = [];
	    regExp = /^[a-zA-Z$_]{1}[\w\$]*$/;
	    var i;
	    for (i = 0, len = methods.length; i < len; i++) {
		    if (typeof methods[i] !== 'string' || !regExp.test(methods[i])) {
			    throw new razsor.RazorError(
			            "Interface method names must be strings representing valid JavaScript identifiers. methods["
			                    + i + "]=" + methods[i]);
		    }
		    this.methods.push(methods[i]);
	    }
    },

    ensureImplements : function(object, interfaceObj) {
	    var error;

	    if (arguments.length < 2) {
		    throw new razsor.RazorError("ensureImplements called with "
		            + arguments.length + " arguments, but expected at least 2.");
	    }
	    var i;
	    var j;
	    for (i = 1, len = arguments.length; i < len; i++) {
		    interfaceObj = arguments[i];
		    if (interfaceObj.constructor !== razsor.Interface) {
			    throw new razsor.RazorError(
			            "ensureImplements expects arguments two and above to be instances of razsor.Interface.");
		    }
		    for (j = 0, methodsLen = interfaceObj.methods.length; j < methodsLen; j++) {
			    var method = interfaceObj.methods[j];
			    if (!object[method] || typeof object[method] !== 'function') {
				    throw new razsor.RazorError(
				            "Object does not implement the "
				                    + interfaceObj.name
				                    + " interface correctly: Method "
				                    + method
				                    + " was not found. This is either because the class does not define the methods in the interface; or you are calling 'new' "
				                    + " on an abstract class; or you are calling 'new' on a class that does not provide concrete implementations for each of the"
				                    + " abstract methods in its superclass.");
			    }
		    }
	    }
    },

    bind : function(source, target) {
	    return function() {
		    if (typeof source === "string") {
			    return target[source].apply(target, arguments);
		    } else {
			    return source.apply(target, arguments);
		    }
	    };
    },

    RazorError : function(msg) {
	    var e = new Error();
	    e.message = e.stack || e.stackTrace || msg;
	    return e;
    },

    capitalise : function(toCapitalise) {
	    return toCapitalise.substring(0, 1).toUpperCase()
	            + toCapitalise.substring(1).toLowerCase();
    }
};

razsor.definePackage("razsor.event");

