/**
* GF_Instance(sType, jTarget[, oOptions])
*
* Base class for all GF instances. Every object should extend this class
* (using the GF.ExtendClass method which is availble as a Function extension
* named Extend).
* 
* Implements basic object creation procedures, aggregation and common methods
* for object selection.
* 
* @return bool - False on error.
* @param type string - Name of the child class.
* @param target jQuery - Target which is being used to initialize the newly
*  created object.
* @param options object - Configuration options object.
*/
function GF_Instance(sType, jTarget, oOptions) {
	
	/* Declarations */
	
	this.m_iId;             // Instance's id
	this.m_jTarget;         // Instance's DOM element
	this.m_sType;           // Final type of the object
	
	this.GF_Instance;       // GF_Instance(sType, jTarget[, oOptions])
	this.InitInstance;      // InitInstance(target)
	this.RemoveInstance;    // RemoveInstance()
	this.RefreshTarget;     // RefreshTarget()
	this.IsInstanceOf;      // IsInstanceOf(sType)
	
	/* Definitions */
	
	/**
	* GF_Instance(sType, jTarget[, oOptions])
	*
	* The GF_Instance constructor. Checks if all the necessary parameters are set
	* and invokes the object creation. If any exception raises, it skips the object
	* creation and returns false.
	* 
	* When target points to more than one DOM element, this constructor invokes
	* multiple object creation and returns an array of newly created objects.
	* 
	* @return bool - False on error.
	* @param type string - Name of the child class.
	* @param target jQuery - Target which is being used to initialize the newly
	*  created object.
	* @param options object - Configuration options object.
	*/
	this.GF_Instance = function(sType, jTarget, options) {
		try {
			
			if ((sType == undefined) || !sType) {
				GF_Debug.Error('No instance type specified. GF object creation failed.');
			}
			
			this.m_sType = sType;
			
			if (!(jTarget instanceof $)) {
				GF_Debug.Error('Unknown type of target (' + typeof jTarget + '). Expected a jQuery object. GF object creation failed.');
			}
			if (jTarget.length < 1) {
				GF_Debug.Warning('Target emerged to be an empty jQuery object. GF object creation omitted.');
				return false;
			}
			if (jTarget.length > 1) {
				var a = [];
				eval('var f = ' + sType + ';');
				for (var i = 0; i < jTarget.length; i++) {
					a.push(new f(jTarget.eq(i), oOptions));
				}
				return a;
			}
			this.InitInstance(jTarget);
			
		}
		catch (xException) {
			GF_Debug.HandleException(xException);
			this.RemoveInstance();
			return false;
		}
	};
		
	/**
	* InitInstance(jTarget)
	*
	* Initializes the DOM element passed as the argument. Prepares the name, id and
	* adds necessary CSS classes.
	* 
	* Also adds the newly created object to the instances vector.
	* 
	* @return void
	* @param jTarget jQuery - Target DOM element.
	*/
	this.InitInstance = function(jTarget) {
		this.m_iId = GF_Instance.s_aoInstances.push(this) - 1;
		this.m_jTarget = jTarget;
		this.m_jTarget.addClass('GF-instance-' + this.m_iId).addClass('GF-instance').addClass(sType);
	};
	
	/**
	* RemoveInstance()
	* 
	* Removes this instance from the instances vector.
	* 
	* @return void
	*/
	this.RemoveInstance = function() {
		if ((this.m_iId != undefined) && (GF_Instance.s_aoInstances[this.m_iId] != undefined)) {
			if (this.m_jTarget) {
				this.m_jTarget.remove();
			}
			GF.DeleteFromArray(GF_Instance.s_aoInstances, this.m_iId);
		}
	};
	
	/**
	* RefreshTarget()
	* 
	* Refreshes the m_jTarget property using the actual instance id.
	*/
	this.RefreshTarget = function() {
		this.m_jTarget = $('.GF-instance-' + this.m_iId);
	};
	
	/**
	* IsInstanceOf(sType)
	* 
	* Checks whether current object is an istance of the given class.
	* 
	* @return bool
	* @param sType string - Type that we want to match
	*/
	this.IsInstanceOf = function(sType) {
		return this.m_sType == sType;
	};
	
	/* Constructor call */
	
	return this.GF_Instance(sType, jTarget, oOptions);
	
};

/**
* @var GF_Instance.s_aoInstances array - Keeps all the instances for further retrieval.
*/
GF_Instance.s_aoInstances = [];

/**
* GF_Instance.GetInstance(jNode[, sType])
* 
* Returns an object of a GF_Instance derived class associated with the DOM
* node passed as an argument. If type is specified, it looks for an object
* of that type. Otherwise it returns the first GF_Instance encountered.
* 
* It is recommended to use GetInstace() straight from derived classes.
* It's not necessary then to pass the second argument - it's being passed
* automatically.
* 
* @return GF_Instance|bool - Associated object or false if not found.
* @param jNode jQuery - A DOM node being the search subject.
* @param sType string - Optional name of the object type desired.
*/
GF_Instance.GetInstance = function(jNode, sType) {
	var iId;
	if (jNode.hasClass('GF-instance') && ((sType == undefined) || jNode.hasClass(sType))) {
		iId = GF_Instance.GetId(GF.GetClasses(jNode));
		if (iId >= 0) {
			return GF_Instance.ReturnInstance(iId);
		}
		GF_Debug.Message('Encoutered a .GF-instance node without id.');
	}
	var jParents = jNode.parents('.GF-instance' + ((sType == undefined) ? '' : '.' + sType));
	for (var i = 0; i < jParents.length; i++) {
		jNode = jParents.eq(i);
		iId = GF_Instance.GetId(GF.GetClasses(jNode));
		if (iId >= 0) {
			return GF_Instance.ReturnInstance(iId);
		}
		GF_Debug.Message('Encoutered a .GF-instance node without id.');
	}
	GF_Debug.Error('Instance not found!');
	return false;
};

/**
* GF_Instance.GetId(aClasses)
* 
* Returns the GF_Instance id from an array of CSS classes.
* 
* @return int - Id.
* @param aClasses array - CSS classname array.
*/
GF_Instance.GetId = function(aClasses) {
	for (var i in aClasses) {
		if (aClasses[i].substr(0, 12) == 'GF-instance-') {
			return aClasses[i].substr(12);
		}
	}
	return -1;
};

/**
* GF_Instance.ReturnInstance(iId)
* 
* Returns an instance with the specified id. It checks first if such an instance
* does exist. It's recommended to use GetInstace rather than call this function
* directly.
* 
* @return GF_Instance|bool - Desired instance or false on failiure.
* @param id int - Id of the desired object.
*/
GF_Instance.ReturnInstance = function(iId) {
	if (GF_Instance.s_aoInstances[iId] == undefined) {
		GF_Debug.Error('Tried to access a non-existing instance (' + iId + ').');
		return false;
	}
	return GF_Instance.s_aoInstances[iId];
};

/**
* GF_Instance.GetCurrentInstance(mContext, type)
* 
* Returns the derived class of GF_Instance instance corresponding to the context.
* It may be the context itself if called from an appropriate object or a GetInstace
* result if context is a DOM node.
* 
* It is recommended to use GetInstace() straight from derived classes.
* It's not necessary then to pass the second argument - it's being passed
* automatically.
* 
* It is commonly used in event handlers across the framework.
* 
* @return GF_Instance|bool - Desired object or false on error.
* @param mContext mixed - Pointer to object that is used to begin search.
* @param type string - Optional name of the object type desired.
*/
GF_Instance.GetCurrentInstance = function(mContext, sType) {
	eval('var bIsProperType = ((mContext instanceof ' + sType + ') ? true : false);');
	if (bIsProperType) {
		return mContext;
	}
	else if (mContext.nodeType != undefined) {
		eval('var instance = ' + sType + '.GetInstance($(mContext));');
		return instance;
	}
	else {
		GF_Debug.Error('Couldn\'t get current instance of type ' + sType + '. Unrecognized context type (' + typeof mContext + ')');
		return false;
	}
};
