/* -----------------------------------------------------------------------------
 * rubycontainer_extended.swg
 *
 * This file contains additional functions that make containers
 * behave closer to ruby primitive types.
 * However, some of these functions place some restrictions on
 * the underlying object inside of the container and the iterator
 * (that it has to have an == comparison function, that it has to have 
 * an = assignment operator, etc).
 * ----------------------------------------------------------------------------- */

/** 
 * Macro used to add extend functions that require operator== in object.
 * 
 * @param Container    STL container
 * @param Type         class inside container
 *
 */
%define %swig_container_with_equal_operator( Container, Type )

  VALUE __delete__( const Type& val ) {
    VALUE r = Qnil;
    Container<Type >::iterator e = $self->end();
    Container<Type >::iterator i = std::remove( $self->begin(), e, val );
    // remove dangling elements now
    $self->erase( i, e );
    
    if ( i != e )
      r = swig::from< Type >( val );
    else if ( rb_block_given_p() )
      r = rb_yield(Qnil);
    return r;
  }

%enddef  // end of  %swig_container_with_equal_operator




/** 
 * Macro used to add extend functions that require the assignment 
 * operator (ie. = ) of contained class
 * 
 * @param Container    STL container
 * @param Type         class inside container
 * 
 */

%define %swig_container_with_assignment( Container, Type )


  //
  // map!  -- the equivalent of std::transform
  //
  Container< Type >* map_bang() {

    if ( !rb_block_given_p() )
      rb_raise( rb_eArgError, "No block given" );

    VALUE r = Qnil;
    Container< Type >::iterator i = $self->begin();
    Container< Type >::iterator e = $self->end();

    try {
      for ( ; i != e; ++i )
	{
	  r = swig::from< Type >( *i );
	  r = rb_yield( r );
	  *i = swig::as< Type >( r );
	}
    }
    catch ( const std::invalid_argument& )
      {
	rb_raise(rb_eTypeError,
		 "Yield block did not return a valid element for " "Container");
      }
    
    return $self;
  }


%enddef  // end of  %swig_container_with_assignment





/** 
 * Macro used to add all extended functions to a container
 * 
 * @param Container    STL container
 * @param Type         class inside container
 * 
 */
%define %swig_container_extend( Container, Type )

%extend Container< Type > {

  %swig_container_with_assignment( %arg(Container), Type );
  %swig_container_with_equal_operator( %arg(Container), Type );

}

%enddef


/** 
 * Private macro used to add all extended functions to C/C++
 * primitive types
 * 
 * @param Container an STL container, like std::vector (with no class template)
 *
 */
%define %__swig_container_extend_primtypes( Container )

%swig_container_extend( %arg( Container ), bool );
%swig_container_extend( %arg( Container ), char );
%swig_container_extend( %arg( Container ), short );
%swig_container_extend( %arg( Container ), int );
%swig_container_extend( %arg( Container ), unsigned short );
%swig_container_extend( %arg( Container ), unsigned int );
%swig_container_extend( %arg( Container ), float );
%swig_container_extend( %arg( Container ), double );
%swig_container_extend( %arg( Container ), std::complex );
%swig_container_extend( %arg( Container ), std::string );
%swig_container_extend( %arg( Container ), swig::GC_VALUE );

%enddef


%__swig_container_extend_primtypes( std::vector );
%__swig_container_extend_primtypes( std::deque );
%__swig_container_extend_primtypes( std::list );