/**
 * Copyright (c) 2009, Yakabod, Inc.
 * File: Interface.js
 *
 * $Revision: 1.2 $
 * $Date: 2009/12/11 21:34:10 $
 * Last edited by: $Author: yli $
 *
 * Last Error-Free JSLint: 19 Nov 2009 14:46
 */

/**
 * Object.Interface - Instanciate an Interface with which objects can be checked
 *
 * @access public
 * @param sName STRING intefrace name
 * @param aMethods ARRAY list of STRING method names
 * @static
 * @throws Error
 * @returns Interface instance
 */
Object.Interface = function( sName, aMethods )
{
  var iArgumentsLength = arguments.length, iMethodLength = aMethods.length, iMethodIndex, sMethod;

  if( iArgumentsLength !== 2 )
  {
    throw new Error( 'Object.Interface constructor expects exactly 2 parameters.  Received ' + iArgumentsLength );
  }

  if( typeof name !== 'string' )
  {
    throw new Error( 'Object.Interface constructor expects first parameter, name, to be a string' );
  }

  if( ! ( aMethods instanceof Array ) )
  {
    throw new Error( 'Object.Interface constructor expects second parameter, methods, to be an Array' );
  }

  this.sName = sName;
  this.aMethods = [];

  for( iMethodIndex = 0; iMethodIndex < iMethodLength; iMethodIndex++ )
  {
    sMethod = aMethods[ iMethodIndex ];
    if( typeof sMethod !== 'string' )
    {
      throw new Error( 'Object.Interface constructor expects method names to be strings.' );
    }

    this.aMethods.push( sMethod );
  }
};
/**
 * Object.Interface.prototype.assertImplementation - requires that a given object implements this interface (via Duck typing check)
 *
 * @access public
 * @param oObject OBJECT Object which the interface should check against itself
 * @throws Error
 * @returns void
 */
Object.Interface.prototype.assertImplementation = function( oObject )
{
  var iMethodsLength = this.aMethods.length, iMethodsIndex, sMethodName;

  if( typeof oObject !== 'object' || oObject === null )
  {
    throw new Error( 'assertImplemention expects first parameter to be object' );
  }

  for( iMethodsIndex = 0; iMethodsIndex < iMethodsLength; iMethodsIndex++)
  {
    sMethodName = this.aMethods[ iMethodsIndex ];

    if( typeof sMethodName !== 'string' )
    {
      throw new Error( 'assertImplemention expects Method Names in Interface Definitions to be strings' );
    }

    if( typeof oObject[ sMethodName ] !== 'function')
    {
      throw new Error( 'assertImplemention: object does not implement the ' + this.sName + ' interface. Method, ' + sMethodName + ', is missing' );
    }
  }

};
/**
 * Object.Interface.prototype.checkImplementation - checks if a given object implements this interface (via Duck typing check)
 *
 * @access public
 * @param oObject OBJECT Object which the intefrace should check against itself
 * @returns BOOL true if all interfaces are implemented, false upon first missing Interface method
 */
Object.Interface.prototype.checkImplementation = function( oObject )
{
  var bReturnValue = false;

  try
  {
    this.assertImplementation( oObject );
    bReturnValue = true;
  }
  catch( e ){}

  return bReturnValue;
};



/* EXAMPLE 

var iFoo, iBar, ImplementsFoo, fUsesFooImplementation;


iFoo = new Object.Interface( 'iFoo', ['func1', 'func2'] );
iBar = new Object.Interface( 'iBar', ['method1', 'method2'] );


ImplementsFoo = function()
{
  iFoo.assertImplementation( this );
};
ImplementsFoo.prototype.func1 = function(){};
ImplementsFoo.prototype.func2 = function(){};


fUsesFooImplementation = function( oFooImplementation )
{
  //Require our first param to implement foo
  iFoo.assertImplementation( oFooImplementation );

  alert( 'First parameter is good because we made it to this line (last line did not throw an exception)' );

  //Check if our first param to implemented iFoo
  alert( 'oFooImplementation does' +
         ( iFoo.checkImplementation( oFooImplementation ) ?
           '' :
           ' not'
         ) +
         ' implement iFoo'
       );

  //Check if our first param to implemented iBar
  alert( 'oFooImplementation does' +
         ( iBar.checkImplementation( oFooImplementation ) ?
           '' :
           ' not'
         ) +
         ' implement iBar'
       );
};


fUsesFooImplementation( new ImplementsFoo() );
*/