/**
 * TNJSCoreLibrary 0.1a 
 * (c) 2005-2007 Till Niese <tnjslibrary@tillniese.com>
 *
 * You mustn't remove the copyright information.
 */
/**

TODO:
static methodes and filds of the partent arent used with the child
probably some other minor problems with the implementation
need to write some use cases later on. but for the beginning its ok


what is still missing:
possibility to extend objects that where alreay created
extending classes so that the class itselfe will get new features
the extend methode for normal objects and classes is really rudimentary
propably we could use the same function as of the class creation
- create a class for listeners
*****/


getScriptPath = function( scriptName, returnBase ) {
	//asking for the script tag doesn't return all scripts for some resones probably a DOM updated bug
	var headerElement = document.getElementsByTagName("head")[0];
	var elementList = headerElement.childNodes;
	var scriptPath = null;
	var tmpExpr = new RegExp(scriptName.replace(".","\\.")+"(\\?.*)?$");

	if( returnBase == null ) returnBase = false;
	for( var i=0 ; i<elementList.length ; i++ ) {
	
		if (elementList[i].nodeName == "SCRIPT") {
			if( elementList[i].src && elementList[i].src.match(tmpExpr) ) {
				scriptPath = elementList[i].src;
				break;
			}
		}
	}
	if( scriptPath == null ) {
		throw new Error("can't find library ('"+scriptPath+"')");
	}
	
	if( returnBase ) {
		return scriptPath.replace(tmpExpr,'');
	} else {
		return scriptPath;
	}
};

var elementList = document.getElementsByTagName("script");



var TNJSCoreLib = {
	version : '0.1a r106',
	
	baseInclude: ["md5string","array","event"],
	
	loadedScripts: new Object(),
	
	libraryPath: getScriptPath("tnjscorelib.js",true)+"tnjscorelib/",
	
	load : function() {
		var scriptPath = getScriptPath("tnjscorelib.js");
		if( scriptPath == null ) {
			throw new Error("can't find library ('tnjscorelib.js')");
		}
		
		var includes = scriptPath.match(/\?.*load=([a-z,]*)/);
		
		this.includeScripts(this.baseInclude,this.libraryPath);
		if( includes != null ) {
			includes = includes[1].split(',');
			this.includeScripts(includes,this.libraryPath);
		}
		
	},
	
	includeScripts : function( scriptPaths, basePath ) {
		if( typeof scriptPaths  == 'string' ) {
			if( this.loadedScripts[scriptPaths] == null ) {
				document.write('\n\t\t<script type="text/javascript" src="'+(basePath||'')+scriptPaths+'.js"></script>');
				this.loadedScripts[scriptPaths] = true;
			}
		} if(  scriptPaths instanceof Array ) {
			for( var i=0 ; i<scriptPaths.length ; i++ ) {
				this.includeScripts(scriptPaths[i], basePath);
			}
		}
	}
};


//just a wrapper to make it shorter to create instances of a class only needed once
function Instance( data ) {
	var anonymousClass  = Class(data);
	
	return new anonymousClass();
}

function Interface( methodes ) {
	return {
		$type : 'interface',
		methodes : methodes
	};
}

function Class( data ) {
	
	var __retObject =  null;
	
	var classType = (data.$type==null)?'normal':data.$type;
	
	if( classType == 'abstract' ) {
		__retObject = function()
		{
			throw new Error("This class is abstact and can't be initialized");
		};
	} else {
		__retObject = function()
		{
			this.constructor = __retObject;//fixing a bug in safari
			if( this.initialize )
			{
				this.initialize.apply(this,arguments);
			}
		};
	}
	__retObject.$type = classType;
	__retObject.$parents = [];
	
	var requiredFunctionList = {};
	var requiredFunctionCount = __getInterfacesMethodes( data.$implements, requiredFunctionList );
	
	if( data.$implements instanceof Array ) {
		for( var i=0 ; i<data.$implements.length ; i++ ) {
			__retObject.$parents.push(data.$implements[i]);
		}
	}
	
	/******* make an own function out of this if this is possilble **********/
	if( data.$extends instanceof Array ) {
		for( var i=0 ; i<data.$extends.length ; i++ ) {
			__retObject.$parents.push(data.$extends[i]);
			for( var j=0 ; j<data.$extends[i].$parents.length ; j++ ) {
				__retObject.$parents.push(data.$extends[i].$parents[j]);
			}
			if( data.$extends[i].$type == "final" ) {
				throw new Error( "A final class can't be extended" );
			} else if( data.$extends[i].$type == 'abstract' ) {
				for( prop in data.$extends[i].$requiredFunctionList ) {
					if( requiredFunctionList[prop] == null ) {
						requiredFunctionList[prop] = true;
						++requiredFunctionCount;
					}
				}	
			}
			for( var prop in data.$extends[i].prototype ) {
				var propertyInfo = __splitPrototypeName(prop);
				if( __propertyACanOverwriteB( data.$extends[i].prototype[prop] , __retObject.prototype[propertyInfo.name] ) ) {
					
					if( data.$extends[i].prototype[prop] instanceof Function ) {
						__retObject.prototype[propertyInfo.name] = data.$extends[i].prototype[prop].__bindAsSuper( __retObject.prototype[propertyInfo.name] );
						if( requiredFunctionList[propertyInfo.name] == true ) {
							requiredFunctionList[propertyInfo.name] == false;
							--requiredFunctionCount;
						}
					} else {
						__retObject.prototype[propertyInfo.name] = data.$extends[i].prototype[prop];
					}
					
				} else {
					throw new Error("the property '"+propertyInfo.name+"' tries to overwrite an existing property of another kind with the same name.");
				}
			}
		}
	}
	
	
	/******* make an own function out of this if this is possilble **********/
	for( var prop in data ) {
		//only copy propertyies that aren't used for core informations
		if( __isCopyAbleClassProperty(prop) ) {
			var propertyInfo = __splitPrototypeName(prop);
			
			if( __propertyACanOverwriteB( data[prop] , __retObject.prototype[propertyInfo.name] ) ) {
				var scopeType = 'normal';
				var tmpObject = null;
				if( data[prop] instanceof Function ) {
					
					var functionScope = null;
					
					if( propertyInfo.type["static"] ) {
						functionScope = __retObject;
						scopeType == 'static';
					} else {
						functionScope = __retObject.prototype;
					}
					
					if( (functionScope[propertyInfo.name] != null) && (functionScope[propertyInfo.name].$type == 'final') ) {
						throw new Error("can't overwrite final methode '"+propertyInfo.name+"'");
					}
					
					
					var tmpObject = data[prop].__bindAsSuper(functionScope[propertyInfo.name]);
					
					
					if( propertyInfo.type["final"] ) {
						tmpObject.$type = "final";
					} else {
						tmpObject.$type = "normal";
					}
					
					if( requiredFunctionList[propertyInfo.name] == true ) {
						requiredFunctionList[propertyInfo.name] = false;
						--requiredFunctionCount;
					}
				} else {
					if( propertyInfo.type["abstract"] ) {
						if( classType != 'abstract' ) {
							throw new Error("the abstract function '"+propertyInfo.name+"' was defined in an non abstract class");
						}
						if( requiredFunctionList[propertyInfo.name] == null ) {
							requiredFunctionList[propertyInfo.name] = true;
							++requiredFunctionCount;
						}
					} else {
					
						if( propertyInfo.type["enum"]  ) {
							tmpObject = __createEnum(data[prop]);
							scopeType = 'both';
						} else if( propertyInfo.type.mask  ) {
							tmpObject = __createMask(data[prop]);
							scopeType = 'both';
						} else {
							tmpObject = data[prop];
						}
					}
				}
				if( (scopeType == 'both') || (scopeType == 'normal') ) {
					__retObject.prototype[propertyInfo.name] = tmpObject;
				}
				
				if( (scopeType == 'both') || (scopeType == 'static') ) {
					__retObject[propertyInfo.name] = tmpObject;
				}
				
			} else {
				throw new Error("the property '"+propertyInfo.name+"' tries to overwrite an existing property of another kind with the same name.");
			}
		}
	}
	
	if( classType == 'abstract' ) {
		__retObject.$requiredFunctionList = {};
		for( prop in requiredFunctionList ) {
			if( requiredFunctionList[prop] ) __retObject.$requiredFunctionList[prop] = true;
		}
	}
	
	if( (classType != 'abstract') && requiredFunctionCount>0 ) __abstractImplementationError(requiredFunctionList);
	
	//adding base methodes
	__retObject.prototype.isInstanceOf = ___isInstanceOf;
	
	return __retObject;
}



var ___extend = function( data ) {
	if( !(data  instanceof Array) ) {
		data = [data];
		
	}
	var extendScope = this;
	for( var i=0, length=data.length ; i<length ; i++ ) {
		
		var currData = data[i];
		if( currData instanceof Function  ) currData = currData.prototype;
		if( typeof extendScope == 'function' ) extendScope = extendScope.prototype;
		for( var prop in currData ) {
			if( __isCopyAbleClassProperty(prop) ) {
				var propertyInfo = __splitPrototypeName(prop);
				
				if( extendScope[propertyInfo.name] instanceof Function ) {
					if( (extendScope[propertyInfo.name]==null) || (!propertyInfo.flags.keep) ) {
						extendScope[propertyInfo.name] = currData[prop].__bindAsSuper(extendScope[propertyInfo.name]);
					} else {
						
					}
				} else {
					extendScope[propertyInfo.name] = currData[prop];
				}
			}
		}
	}
};

Object.prototype.extend = ___extend;
Function.extend = ___extend;
var __fixArray = [document,window,Array,String];
for( var i=0 ; i<__fixArray.length ; i++) {
	
	if( __fixArray[i].extend == null ) {
		__fixArray[i].extend = ___extend;
	}
}

Function.prototype.bind = function()
{
	var __method = this,  args = $A(arguments), object = args.shift();
	return function()
	{
		return __method.apply(object,args.concat($A(arguments)));
	};
};

var Try = {
	these: function()
	{
		var __returnValue = null;
		for (var i = 0; i < arguments.length; i++)
		{
			var __methode = arguments[i];
			try {
				__returnValue = __methode();
				break;
			} catch (e) {}
		}
		
		return __returnValue;
	}
};



/*** helper methodes for creating classes *****/
/**********************************************/




function __propertyACanOverwriteB( propA, propB ) {
	if( propB == null ) {
		return true;
	} else {
		if( (propA instanceof Function) && (propB instanceof Function) ) {
			return true;
		} else if( (!propA instanceof Function) && (!propB instanceof Function) ) {
			return true;
		}
	}
	return false;
}

function __createEnum( enums ) {
	if( !enums instanceof Array ) {
		throw new Error("wrong deklaration of ENUM.");
	}
	var newEnums = {};
	for( var i=0 ; i<enums.length ; i++ ) {
		newEnums[enums[i]] = i+1;
	}
	return newEnums;
}

function __createMask( mask ) {
	if( !mask instanceof Array ) {
		throw new Error("wrong declaration of MASK.");
	}
	var tmpMask = {};
	var tmpAll = 0;
	for( var i=0 ; i<mask.length ; i++ ) {
		tmpMask[mask[i]] = Math.pow(2,i);
		tmpAll |= Math.pow(2,i);
	}
	tmpMask["NONE"] = 0;
	tmpMask["ALL"] = tmpAll;
	
	tmpMask.$type = 'mask';
	
	return tmpMask;
}

function __splitPrototypeName( inName ) {
	
	var resultName = {type:{},flags:{},name:null};
	//if a $ is in the name then we have informations about the parts
	if( inName.indexOf("$") != -1 ) {
		//split it by the $
		var tmpNameParts = inName.split("$");
		var tmpLength = tmpNameParts.length;
		for( var i=0, length = tmpLength-1 ; i<length ; i++ ) {
			var tmpPart = tmpNameParts[i];
			if( tmpPart.charAt(0) == '_' ) {
				if( inName.indexOf("k") != -1 ) resultName.flags.keep = true;
				
			} else {
				resultName.type[tmpPart] = true;
			}
		}
		resultName.name = tmpNameParts[tmpLength-1];
	} else {
		resultName.name = inName;
	}
	return resultName;
}

var ___isInstanceOf = function( object ) {
	
	try { 
		if( this instanceof object ) {
			return true;
		}
	} catch ( e ) {}
	
	for( var i=0 ; i<this.constructor.$parents.length ; i++ ) {
		if( this.constructor.$parents[i] == object ) {
			return true;
		}
	}
	return false;
	
};

function __isCopyAbleClassProperty( name ) {
	switch( name ) {
		case '$type':
		case '$implements':
		case '$extends':
		case 'extend':
		case 'isInstanceOf':
			return false;
			break;
	}
	return true;
}
function __abstractImplementationError(requiredFunctionList) {
	var errorText = "class is not definde abstract and not all methodes are implemented ( ";
	for( prop in requiredFunctionList ) {
		if( requiredFunctionList[prop] == true ) {
			errorText += prop+", ";
		}
	}
	errorText = errorText.substring(0,errorText.length-2)+" )";
	throw new Error(errorText);
}

function __getInterfacesMethodes( interfaces, requiredFunctionList ) {
	var count = 0;
	if( interfaces instanceof Array  ) {
		for( var i=0 ; i<interfaces.length ; i++ ) {
			if( interfaces[i].$type == 'interface' ) {
				for( var j=0 ; j<interfaces[i].methodes.length ; j++ ) {
					requiredFunctionList[interfaces[i].methodes[j]] = true;
					count++;
				}
			}
		}
	}
	return count;
}


/**
 * this methode is used to make it possilbe to du super calls from a methode
 */
Function.prototype.__bindAsSuper = function( parentMethode )
{
	//save the information of the methode and its parent
	var __method = this, __parentMethode = parentMethode;
	
	var __retFunction = function()
	{
		var __oldSuper = this._super;
		//to make sure that there will not happen recursive calls with super 
		//an empty methode is added if no parent methode is defined
		if( __parentMethode == null ) __parentMethode = function() {};
		this._super = __parentMethode;
		var __result = __method.apply(this,arguments);
		this._super = __oldSuper;
		return __result;
	};
	__retFunction.$type = this.$type;
	return __retFunction;
};







/*****============================== =============================****/
var firstDebug = true;
var debuggeCache = [];
function debugge( message ) {
	if(document.forms[0]) {
		if( firstDebug )
		{
			document.forms[0].elements[0].value = new Date();
			firstDebug = false;
			for( var i=0, length = debuggeCache.length ; i<length ; i++) {
				document.forms[0].elements[0].value = document.forms[0].elements[0].value+"\n"+debuggeCache[i];
			}
			document.forms[0].elements[0].value = document.forms[0].elements[0].value+"\n"+message;
		} else {
			document.forms[0].elements[0].value = document.forms[0].elements[0].value+"\n"+message;
		}
	} else {
		debuggeCache.push(message);
	}
}




TNJSCoreLib.load();
