/** 
 * Enables you to create interfaces that define methods and have classes that do implement them.
 * @author Ariel Flesler
 */

/**
 * @contructor
 * @param {String, Array} methods List of method names that need to be implemented (strings are splitted by comma or whitespace)
 */
var Interface = Class(function( methods ){
	this.methods = methods.split ? methods.split(/[,\s]+/) : methods;
	this.uid = ++this.clazz._uid_;
});

Interface.statics('_uid_', 0);

/**
 * @method
 * @memberOf Interface
 * @param {Class, Instance} obj Class or instance that needs to implement this interface.
 * @returns true if all the methods are implemented or the name of the first non-implemented method.
 */
Interface.members( 'isImplementedBy', function( obj ){
	var i = this.methods.length;
	obj = obj.prototype || obj;// Class or instance
	
	if( !obj.implemented[this.uid] ){	
		while( i-- )
			if( this.methods[i] in obj == false )
				return this.methods[i];
		
		obj.implemented[this.uid] = true;
	}	
	return true;
});

// ifaces can be one interface object or an array of objects
Class.extensions( 'implement', function( data, ifaces ){
	data.m.ifaces = ifaces.concat ? ifaces : [ ifaces ];
	
	var old = data.c, ok = false;
	data.c = function(){
		if( !ok ){
			var method, i = 0;
			while( this.ifaces[i] )
				if( (method = this.implement(this.ifaces[i++]) ) !== true )
					throw new Error('A class failed to implement the method "'+method+'".');
			ok = true;
		}
		old.apply(this,arguments);
	};
});

/**
 * @id SomeClass.implement
 * @id someInstance.implement
 * @memberOf Class
 * @param {Instance} iface Interface instance to check for implementation against the calling class.
 * @returns true if all the methods are implemented or the name of the first non-implemented method.
 */
Class.staticsList.implement = Class.prototype.implement = function( iface ){
	return iface.isImplementedBy( this );
};

Class.after.push(function( data ){
	data.m.implemented = {};
});