/**
 * LanternJS MVC Framework v0.1
 * http://lanternjs.com/
 * 
 * Copyright (c) 2011 Yaroslav Tkachenko http://sap1ens.ru
 * GNU General Public License http://www.gnu.org/licenses/gpl.html
 */
var lanternJS = (function() {
	
	// private static attrs
	
	var rootUrl = '';
	var connectWith = 'jquery';
	var debug = false;

	//private static methods
	
	function _getXHTTPTransport() {
		var result = false;
		var actions = [
	      function() {return new XMLHttpRequest()},
	      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
	      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
	    ];
	    for(var i = 0; i < actions.length; i++) {
	    	try{
	    		result = actions[i]();
	    		break;
	    	} catch (e) {}	
	    }
	    return result;
 	}
 	
 	function _load(path) {
		var transport = _getXHTTPTransport();
		transport.open('GET', path, false);
		transport.send(null);
		return transport.responseText;
 	}
 	
 	function _quoteString(string) {
		var _escapeable = /["\\\x00-\x1f\x7f-\x9f]/g;
		var _meta = {
		   	'\b': '\\b',
		    '\t': '\\t',
		    '\n': '\\n',
		    '\f': '\\f',
		    '\r': '\\r',
		    '"': '\\"',
		    '\\': '\\\\'
		};
		if (string.match(_escapeable)) {
			return '"' + string.replace(_escapeable, function (a) {
		    	var c = _meta[a];
		        if (typeof c === 'string') return c;
		      	c = a.charCodeAt();
		        return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
		    }) + '"';
		}
		return '"' + string + '"';
	}	
	
	return function() {

		this.setRootUrl = function(eRootUrl) {
			rootUrl = eRootUrl;		
		}
		
		this.getRootUrl = function() {
			return rootUrl;
		}
		
		this.setConnectWith = function(eConnectWith) {
			connectWith = eConnectWith;		
		}
		
		this.getConnectWith = function() {
			return connectWith;
		}
		
		this.setDebug = function(eDebug) {
			debug = eDebug;		
		}
		
		this.getDebug = function() {
			return debug;
		}		
		
		this._getXHTTPTransport = function() {
			return _getXHTTPTransport();
		}
		
		this._load = function(path) {
			return _load(path);
		}	
		
		this._quoteString = function(string) {
			return _quoteString(string);
		}		
	}
})();

lanternJS.prototype.init = function(settings) {
	if(settings.rootUrl) this.setRootUrl(settings.rootUrl);	 
	if(settings.connectWith) this.setConnectWith(settings.connectWith);	 
	if(settings.debug) this.setDebug(settings.debug);	 
			
	if(settings.connectWith) {
		switch(settings.connectWith) {
			case 'jquery':
				var ifc = new Interface('jQuery', ['ajaxComplete', 'delegate']);
				Interface.ensureImplements(jQuery('body'), ifc);
			break;
		}
	}
}

lanternJS.prototype.load = function(path) {
	return this._load(path);
}

lanternJS.prototype.getRootUrl = function() {
	return this.getRootUrl();
}

lanternJS.prototype.getConnectWith = function() {
	return this.getConnectWith();
}

lanternJS.prototype.getDebug = function() {
	return this.getDebug();
}

lanternJS.prototype.JSON = {};

lanternJS.prototype.JSON.getString = function(o) {
	try {
		if (typeof(JSON) == 'object' && JSON.stringify) return JSON.stringify(o);
		var parent = __;
		var type = typeof(o);
		if (o === null) return "null";
		if (type == "undefined") return undefined;
		if (type == "number" || type == "boolean") return o + "";
		if (type == "string") return parent._quoteString(o);
		if (type == 'object') {
			if (o.constructor === Date) {
		    	var month = o.getUTCMonth() + 1;
		        if (month < 10) month = '0' + month;
		        var day = o.getUTCDate();
		        if (day < 10) day = '0' + day;
		        var year = o.getUTCFullYear();
		        var hours = o.getUTCHours();
		        if (hours < 10) hours = '0' + hours;
		        var minutes = o.getUTCMinutes();
		        if (minutes < 10) minutes = '0' + minutes;
		        var seconds = o.getUTCSeconds();
		        if (seconds < 10) seconds = '0' + seconds;
		        var milli = o.getUTCMilliseconds();
		        if (milli < 100) milli = '0' + milli;
		        if (milli < 10) milli = '0' + milli;
		        return '"' + year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + '.' + milli + 'Z"';
		   	}
		    if (o.constructor === Array) {
		    	var ret = [];
		        for (var i = 0; i < o.length; i++)
		        	ret.push(this.getString(o[i]) || "null");
		        return "[" + ret.join(",") + "]";
		    }
		    var pairs = [];
		    for (var k in o) {
		       	var name;
		        var type = typeof k;
		        if (type == "number") name = '"' + k + '"';
		        else if (type == "string") name = parent._quoteString(k);
		        else continue;
		        if (typeof o[k] == "function") continue;
		        var val = this.getString(o[k]);
		        pairs.push(name + ":" + val);
		    }
		    return "{" + pairs.join(",") + "}";
		}
	} catch(e) {
		if(__.getDebug()) {
			console.log(e);
		} 
	}
};

lanternJS.prototype.JSON.getObject = function (src) {
	try {
		if (typeof(JSON) == 'object' && JSON.parse) return JSON.parse(src);
		return eval("(" + src + ")");
	} catch(e) {
		if(__.getDebug()) {
			console.log(e);
		}
	}
};

var __ = new lanternJS();


var Interface = function(name, methods) {
    if(arguments.length != 2) {
        throw new Error('Interface constructor called with ' + arguments.length + 'arguments, but expected exactly 2.');
    }
    
    this.name = name;
    this.methods = [];
    for(var i = 0, len = methods.length; i < len; i++) {
        if(typeof methods[i] !== 'string') {
            throw new Error('Interface constructor expects method names to be passed in as a string.');
        }
        this.methods.push(methods[i]);        
    }    
};    

Interface.ensureImplements = function(object) {
    if(arguments.length < 2) {
        throw new Error('Function Interface.ensureImplements called with ' + arguments.length  + 'arguments, but expected at least 2.');
    }

    for(var i = 1, len = arguments.length; i < len; i++) {
        var interfaceObj = arguments[i];
        if(interfaceObj.constructor !== Interface) {
            throw new Error('Function Interface.ensureImplements expects arguments two and above to be instances of Interface.');
        }
        
        for(var j = 0, methodsLen = interfaceObj.methods.length; j < methodsLen; j++) {
            var method = interfaceObj.methods[j];
            if(!object[method] || typeof object[method] !== 'function') {
                throw new Error('Function Interface.ensureImplements: object does not implement the ' + interfaceObj.name + ' interface. Method ' + method + ' was not found.');
            }
        }
    } 
};
