// Macro to construct iterable objects.
%define sb_iterator(TYPE, PREFIX, VALUE_TYPE)

#if !SWIGRUBY

#if SWIGJAVA
%typemap(javainterfaces) TYPE##Iterator "java.util.Iterator<"#VALUE_TYPE">"
%typemap(javacode) TYPE##Iterator %{
  @Override
  public void remove() {
    throw new UnsupportedOperationException();
  }
%}
#endif

// Basic types
%inline %{
typedef struct {
  PREFIX##_t *ptr;
} TYPE##Iterator;
%}

// Exception to end iteration

#if SWIGJAVA
%exception TYPE##Iterator##::next() {
  if (!arg1->ptr) {
    jclass cls = (*jenv)->FindClass(jenv, "java/util/NoSuchElementException");
    (*jenv)->ThrowNew(jenv, cls, NULL);
    return $null;
  }
  $action;
}
#elif SWIGPYTHON
%exception TYPE##Iterator##::next() {
  if (!arg1->ptr) {
    SWIG_SetErrorObj(PyExc_StopIteration, SWIG_Py_Void());
    SWIG_fail;
  }
  $action;
}
%exception TYPE##Iterator##::__next__() {
  if (!arg1->ptr) {
    SWIG_SetErrorObj(PyExc_StopIteration, SWIG_Py_Void());
    SWIG_fail;
  }
  $action;
}
#endif

// Implementation of the iterator itself

%extend TYPE##Iterator {
  TYPE##Iterator(PREFIX##_t *ptr) {
    TYPE##Iterator *iter = (TYPE##Iterator *)ckd_malloc(sizeof *iter);
    iter->ptr = ptr;
    return iter;
  }

  ~TYPE##Iterator() {
    if ($self->ptr)
	PREFIX##_free($self->ptr);
    ckd_free($self);
  }

#if SWIGJAVA
  %newobject next;
  VALUE_TYPE * next() {
    if ($self->ptr) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter($self->ptr);
      $self->ptr = ##PREFIX##_next($self->ptr);
      return value;
    }

    return NULL;
  }
  bool hasNext() {
    return $self->ptr != NULL;
  }
#elif SWIGJAVASCRIPT
  %newobject next;
  VALUE_TYPE * next() {
    if ($self->ptr) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter($self->ptr);
      $self->ptr = ##PREFIX##_next($self->ptr);
      return value;
    }

    return NULL;
  }
#elif SWIGPYTHON
  // Python2
  %newobject next;
  VALUE_TYPE * next() {
    if ($self->ptr) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter($self->ptr);
      $self->ptr = ##PREFIX##_next($self->ptr);
      return value;
    }
    return NULL;
  }

  // Python3
  %newobject __next__;
  VALUE_TYPE * __next__() {
    if ($self->ptr) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter($self->ptr);
      $self->ptr = ##PREFIX##_next($self->ptr);
      return value;
    }
    return NULL;
  }
#endif
}

#endif // SWIGRUBY

%enddef


%define sb_iterable(TYPE, ITER_TYPE, PREFIX, INIT_PREFIX, VALUE_TYPE)

// Methods to retrieve the iterator from the container

#if SWIGJAVA
%typemap(javainterfaces) TYPE "Iterable<"#VALUE_TYPE">"
#endif

%extend TYPE {
  
#if SWIGRUBY  
  void each() {
    ##PREFIX##_t *iter = INIT_PREFIX##($self);
    while (iter) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter(iter);
      rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(value), SWIGTYPE_p_##VALUE_TYPE##, 0 |  0 ));
      iter = PREFIX##_next(iter);
    }
    return;
  }

  void each(int count) {
    ##PREFIX##_t *iter = INIT_PREFIX##($self);
    int cnt = 0;
    while (iter && cnt < count) {
      VALUE_TYPE *value = ##VALUE_TYPE##_fromIter(iter);
      rb_yield(SWIG_NewPointerObj(SWIG_as_voidptr(value), SWIGTYPE_p_##VALUE_TYPE##, 0 |  0 ));
      iter = PREFIX##_next(iter);
      cnt++;
    }
    if (iter)
	PREFIX##_free(iter);
    return;
  }

#elif SWIGJAVA
  %newobject iterator;
  ITER_TYPE##Iterator * iterator() {
    return new_##ITER_TYPE##Iterator(INIT_PREFIX##($self));
  }

#else  /* PYTHON, JS */
  %newobject __iter__;
  ITER_TYPE##Iterator * __iter__() {
    return new_##ITER_TYPE##Iterator(INIT_PREFIX##($self));
  }  
#endif 

}

%enddef