/*  
 *   MIT-style license.
 *  Copyright (c) 2008  Pedro Andujar Sanz   http://www.jsimpleclass.net/
 * 
 */


var Class = ( function(){
	
	var JSimpleClass = {};
	var DefineStaticMaP = {} ;	
	var finalClass = [];
	var declaredClasses  = [];
	var privateKlass = [];	

	Function.prototype.__wp__ = function(){
		var func =  function(){
		    var SELF = arguments.callee;
			return	SELF.__$this__.apply(SELF.__$object ||this, arguments);
		};
		func.__$object = arguments[0];
		func.__$this__ = this.__$this__  || this;
		func.__$wp__ =  true;
		return func ;
	};
	Function.prototype.$call = function(){
		var params =  Array.prototype.slice.apply(arguments, [1]);
		var obj  = arguments[0];
		if(this.__$this__ ) return this.__$this__.apply(obj,params);
		else return this.apply(obj,params);	
	};
	Function.prototype.$apply = function(obj,args){
		var params = args || [];
		if(this.__$this__) return this.__$this__.apply(obj,params);
		else return this.apply(obj,params);
	};
	//http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf
	Array.prototype.indexOf = (function(){
		if(Array.prototype.indexOf && typeof(Array.prototype.indexOf) == "function") return Array.prototype.indexOf;	
		return (function(item, from){
			var lg = this.length;
			for (var ctr = isNaN(from) ? 0 : (from < 0) ? lg + Math.ceil(from)  : Math.floor(from) ; ctr < lg; ctr++){
				if (this[ctr] === item) return ctr;
			}
			return -1;
		})		
	})();		

	function isFinal(klass){
		for(var ctr = 0,lg = finalClass.length; ctr < lg;ctr++){
			if(finalClass[ctr] == klass) return true;
		}
		return false;
	};

	
	function getInsideClasses(Klass){
		var result = [];
		if(!Klass.$$class) return [];
		var $instance = new Klass.$$class($constructorNull);
		var KlassName = Klass.__NAME__;
		var $Class = Klass.$$class;
		var $superClass = Klass.$$superClass;
		for(var prop in $instance){
			try{
				if($instance[prop].$$class){
					 if($instance[prop].$$class !=  $Class  && $instance[prop].$$class !=  $superClass){
						result[result.length] = $instance[prop].__NAME__;
					}
				}
				else if(prop == "$$static"){
					var statik =  $instance[prop];
					for( var prop2 in  statik){
						if(statik[prop2].__CLASS__) result[result.length] = statik[prop2].__NAME__;
					}
				}
			}
			catch(e){}
		}
		for(var ctr = 0,lg = privateKlass.length; ctr < lg; ctr++){
			if(privateKlass[ctr].indexOf(KlassName) == 0) result[result.length] = privateKlass[ctr];
		}
		return result;
	};
	
	function setDeclaredClasses(klassName){
		if((/\:\:/).test(klassName)){
			if((/\:\:private\./).test(klassName)){
				if(privateKlass.indexOf(klassName ) == -1) privateKlass[privateKlass.length] = klassName;
			}		
		}
		else{
			declaredClasses[declaredClasses.length] = klassName;
		}	
	};
	
	var superArray  = [];
	function getSuper(obj){
		for(var ctr = 0, lg = superArray.length;ctr < lg; ctr++){
			if(superArray[ctr][0] == obj) return superArray[ctr];	
		}
		superArray[lg] = [obj,[]];
		return superArray[lg];
	};
	function removeSuper(obj){
		for(var ctr = 0, lg = superArray.length;ctr < lg; ctr++){
			if(superArray[ctr][0] == obj) {
				superArray.splice(ctr,ctr+1);
				break
			}
		}
	};
	var $keyWords = ["$$class","$$className","$$superClass","$$static","$$super","$$DEFINE_STATIC","$$implement","$$implemented","$$finalized","$$finalize"];
	var addProperties = function(object, source){
		for(var p in source){
			object[p] = source[p];
		}
		return object;
	};
	
	function __implement(sources, object,props){
		var source = null;
		var keyWords = props && props.constructor == Array ? $keyWords.concat(props) : $keyWords;
		if(!object.$$implemented) object.$$implemented = [];
		for(var ctr = 0; source = sources[ctr]; ctr++){
			source = source.$$class ?  new source.$$class($constructorNull) : source;
			for(var i in source){
				if(keyWords.indexOf(i) == -1){
					object[i] = source[i];
				}
			}
			if(source.$$className){
				if(object.$$implemented.indexOf(source.$$className) == -1) object.$$implemented.push(source.$$className);	
			}
		}	
	};	
	var $proto = {};
	var $constructorNull = {};
	
	return ({		
	create:function(klassName,Klass){
	
		var cFinal = arguments[2] == "final" ? true :false;
  		var KLASS = function(){
				if(arguments[0] == $proto) return ;
				var SELF = arguments.callee;	
				this.$$implement = function(){
					var isArray = arguments[arguments.length - 1].constructor == Array;
					if(isArray){
						var sources = [];
						for(var ctr = 0, lg = arguments.length - 1;ctr < lg;  ctr++) sources[ctr] = arguments[ctr];
						__implement(sources, this,arguments[arguments.length - 1]);
					}
					else{
						__implement(arguments, this);	
					};
				};
				
 				this.$$class = SELF;
				this.$$static = SELF.$$static;
				this.$$className = SELF.__NAME__;
				
				SELF.__CLASS__.apply(this,[JSimpleClass]);
				
				if(arguments[0] != JSimpleClass ){
				
					if(this.$$DEFINE_STATIC &&  arguments[1]  == DefineStaticMaP){
						this.$$DEFINE_STATIC();
						var  statik =  this.$$static;
						for(var prop in statik){
							if(typeof(statik[prop]) == "function"){
								if(!statik[prop].__CLASS__){							
									statik[prop]=  statik[prop].__wp__(this);
								}
							}
						}
						delete this.$$DEFINE_STATIC;
					}	
				

					if(this.$$DEFINE_STATIC){
						delete this.$$DEFINE_STATIC;
					}
					
					if(arguments[0] != $constructorNull){
						if(typeof(this.$$constructor)!= "undefined"){
							this.$$constructor.apply(this,arguments);
							delete this.$$constructor;
							this.$$finalized = false;
							}
						else{ for(var prop in this) delete this[prop];};
					
					}
					else if(this.$$constructor) delete this.$$constructor;					
					delete this.$$implement;
				}
			};
	
		KLASS.__CLASS__= typeof(Klass) == "function" ? Klass  : (function(){}) ;
		KLASS.$$static = {};
		KLASS.$$class = KLASS;
		KLASS.__NAME__ = klassName ;	
		new KLASS($constructorNull,DefineStaticMaP);
		setDeclaredClasses(KLASS.__NAME__);
		KLASS.implement = function(){
				var source;
				for(var ctr = 0; source = arguments[ctr]; ctr++){
					source = source.$$class ?  new source.$$class($constructorNull) : source;
					for(var prop in source) KLASS.prototype[prop] = source[prop];	
				}		
		}
		if(cFinal) finalClass[finalClass.length] = KLASS;
		return KLASS;
    },
       
	extend:function(klassName, superKlass, Klass){
	
		if(isFinal(superKlass) )return null;	
		var cFinal = arguments[3] == "final" ? true :false;

		var KLASS = function(){
			if(arguments[0] == $proto) return ;
			var SELF = arguments.callee;
			if(SELF.$$superClass){
				SELF.$$superClass.apply( this, [JSimpleClass,arguments[1],arguments[2]]);
			}	
	
			var inheritFromParent  = {};			
			for(var prop in this){ 
				inheritFromParent[prop] = this[prop];				
			}
			inheritFromParent["$$constructor"] = typeof(this.$$constructor)!= "undefined" ? this.$$constructor : (function(){});

			this.$$class = SELF;
			this.$$superClass = SELF.$$superClass;
			this.$$className = SELF.__NAME__;
			this.$$static = SELF.$$static;
			inheritFromParent["$$static"] = this.$$superClass.$$static;
			delete inheritFromParent["$$super"] ;
			delete inheritFromParent["$$DEFINE_STATIC"] ;
			delete inheritFromParent["$$implement"] ;
			for(var prop in inheritFromParent){	
				if(typeof(inheritFromParent[prop]) == "function"){
						if(!inheritFromParent[prop].__CLASS__ ){
							inheritFromParent[prop] =  inheritFromParent[prop].__wp__(this);
						}
				}
			}			
			var currentSuper = getSuper(this)[1];
			currentSuper.push(inheritFromParent);
			this.$$super =  currentSuper;

			if(arguments[1] == DefineStaticMaP){
					if(this.$$DEFINE_STATIC){
						function getStatic(){
							if(Class.getIndex(this.$$superClass) == 0) this.$$static = {};
							this.$$DEFINE_STATIC();
						}						
						for(var prop in this) getStatic.prototype[prop] = this[prop];
						getStatic.prototype["$$super"] =  [].concat(currentSuper);
						arguments[2].$$static = addProperties(arguments[2].$$static, new getStatic().$$static);						
					}					
			}
			
			this.$$DEFINE_STATIC = (function(){});
			SELF.__CLASS__.apply(this);
				
			if(arguments[0] != JSimpleClass ){
				removeSuper(this);
				if(arguments[1] == DefineStaticMaP ){
					this.$$static = arguments[2].$$static;
					if(this.$$DEFINE_STATIC){
						this.$$DEFINE_STATIC();
					}

					var statik =  this.$$static;
					for(var prop in statik){	
						if(typeof(statik[prop]) == "function"){
							if(statik[prop].__$wp__ ){	
								if(!statik[prop].__CLASS__) statik[prop]= statik[prop].__$this__.__wp__(this);							
							}
							else if(!statik[prop].__CLASS__){
								statik[prop]= statik[prop].__wp__(this);
							}
						}
					}			
				}
				delete this.$$DEFINE_STATIC;				
				if(arguments[0] != $constructorNull){
					if(typeof(this.$$constructor)!= "undefined"){

						this.$$constructor.apply(this,arguments);
						delete this.$$constructor;
						this.$$finalized = false;
						}
					else{ for(var prop in this) delete this[prop]};
				}	
				else if(this.$$constructor) delete this.$$constructor;	
				delete this.$$implement;
							
			}
		};
	
		KLASS.$$superClass = superKlass;
		KLASS.__CLASS__= Klass ? Klass  : superKlass.__CLASS__;
		KLASS.$$static = {} ;
		KLASS.$$class = KLASS;
		KLASS.__NAME__ = klassName || "";
		
		new KLASS($constructorNull, DefineStaticMaP, KLASS);
		KLASS.prototype = new KLASS.$$superClass($proto);
		setDeclaredClasses(KLASS.__NAME__);
		KLASS.implement = function(){
				var source;
				for(var ctr = 0; source = arguments[ctr]; ctr++){
					source = source.$$class ?  new source.$$class($constructorNull) : source;
					for(var prop in source) KLASS.prototype[prop] = source[prop];	
				}		
		};
		if(cFinal) finalClass[finalClass.length] = KLASS;
		return KLASS;
	},
	clone:function(klassName,klass,cFinal){
	
		if(klass.$$superClass){
			return this.extend(klassName, klass.$$superClass, klass.__CLASS__, cFinal);
		}
		else if(klass.__CLASS__){
			return this.create(klassName, klass.__CLASS__,cFinal);
		}
		else{
			return null;
		}
	},
	instanceOf:function(object , constructor){
		var currentClass = object.$$class;
		while (currentClass) {
			if (currentClass == constructor) return true;
			currentClass = currentClass.$$superClass;
		}
		return false;	
	},
	exists:function(klass){
		try{
			eval("" + klass + ".$$class");
			return true;
		}
		catch(e){return false;}
	},
	getSuperClasses:function(object){
		var currentClass =   typeof(object) == "function" ? object : object.$$class;
		var historyClass = [];
		while(currentClass){	
			if(currentClass.$$superClass){
				historyClass[historyClass.length] = currentClass.$$superClass.__NAME__;
			}
			currentClass =  currentClass.$$superClass;				
		}
		return historyClass;
	},
	getIndex:function(obj){
		if(typeof(obj) == "function" ){
			var currentClass = obj.$$class;
			var level =  -1;
			while(currentClass){	
				level++;
				currentClass = currentClass.$$superClass;			
			}
			return level;
		}
		else{
			if(obj.$$superClass) return obj.$$super.length;
			else if(obj.$$class) return 0;
		}
		return -1;
	},
	getImplementedClasses:function(klass){
		  
		  if(klass.$$class){
			  var result = new klass.$$class($constructorNull).$$implemented;
			  if(typeof(result) == "undefined") return [];
			  return result;
		  }
		  return [];
		
	},
	getDeclaredClasses:function(klass){
		if(klass && klass.$$class) return getInsideClasses(klass);
		return declaredClasses;
	},
	getName:function(klass){
		if(klass.$$class) return klass.$$class.__NAME__;
		return null;
	},
	finalize:function(inst){
		if(inst.$$finalize) inst.$$finalize();
		for(var prop in inst) delete inst[prop];
		for(var prop in inst) inst[prop] = null;
		inst.$$finalized = true;
		return null;
	}
});
})();
