The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * Copyright (C) 2005 by the gtk2-perl team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id$
 */

#include "gst2perl.h"

/* ------------------------------------------------------------------------- */

SV *
newSVGstIterator (const GstIterator *iter)
{
	AV *av, *dummy;
	SV *tie, *ref;
	HV *stash;

	if (!iter)
		return &PL_sv_undef;

	av = newAV ();
	dummy = newAV ();

	ref = newRV_noinc ((SV *) av);
	stash = gv_stashpv ("GStreamer::Iterator", TRUE);
	sv_bless (ref, stash);

	tie = newRV_noinc ((SV *) dummy);
	stash = gv_stashpv ("GStreamer::Iterator::Tie", TRUE);
	sv_bless (tie, stash);

	/* Both the dummy and the real array need to have the path stored in
	 * the ext slot.  SvGstIterator looks for it in the real array.
	 * FETCHSIZE and FETCH look for it in the dummy. */
	sv_magic ((SV *) dummy, 0, PERL_MAGIC_ext, (const char *) iter, 0);
	sv_magic ((SV *) av, 0, PERL_MAGIC_ext, (const char *) iter, 0);
	sv_magic ((SV *) av, tie, PERL_MAGIC_tied, Nullch, 0);

	return ref;
}

GstIterator *
SvGstIterator (SV *sv)
{
	MAGIC *mg;
	if (!sv || !SvROK (sv) || !(mg = mg_find (SvRV (sv), PERL_MAGIC_ext)))
		return NULL;
	return (GstIterator *) mg->mg_ptr;
}

/* ------------------------------------------------------------------------- */

SV * sv_from_pointer (gpointer pointer, GType gtype, gboolean own)
{
	GType fundamental = G_TYPE_FUNDAMENTAL (gtype);
	switch (fundamental) {
    		case G_TYPE_INTERFACE:
		case G_TYPE_OBJECT:
			return gperl_new_object (G_OBJECT (pointer), own);

		case G_TYPE_BOXED:
			/* special case for SVs, which are stored directly
			 * rather than inside blessed wrappers. */
			if (gtype == GPERL_TYPE_SV) {
				SV * sv = pointer;
				return sv ? g_boxed_copy (GPERL_TYPE_SV, pointer)
				          : &PL_sv_undef;
			}

			return gperl_new_boxed (pointer, gtype,	own);

		case G_TYPE_PARAM:
			return newSVGParamSpec (pointer);

		case G_TYPE_POINTER:
			return newSViv (PTR2IV (pointer));

		default:
			croak ("FIXME: unhandled type - %d (%s fundamental for %s)\n",
			       fundamental, g_type_name (fundamental), g_type_name (gtype));
	}
}

/* ------------------------------------------------------------------------- */

MODULE = GStreamer::Iterator	PACKAGE = GStreamer::Iterator	PREFIX = gst_iterator_

=for position SYNOPSIS

=head1 SYNOPSIS

  foreach ($bin -> iterate_elements()) {
    do_something($_);
  }

  my $iter = $bin -> iterate_elements();
  while ($_ = $iter -> next()) {
    do_something($_);
  }

=cut

=for position DESCRIPTION

=head1 DESCRIPTION

There are two ways to use a I<GStreamer::Iterator>.  The first is to use normal
Perl looping stuff:

  foreach ($bin -> iterate_elements()) {
    do_something($_);
  }

This is very elegant and Perl-ish, but may also be a bit slower.

The alternative is to use the I<next> method:

  my $iter = $bin -> iterate_elements();
  while ($_ = $iter -> next()) {
    do_something($_);
  }

This is hardly beautiful but avoids looping over the elements unnecessarily and
is thus faster.

=cut

void
DESTROY (GstIterator *iter)
    CODE:
	gst_iterator_free (iter);

# GstIteratorResult gst_iterator_next (GstIterator *it, gpointer *elem);
SV *
gst_iterator_next (iter)
	GstIterator *iter
    PREINIT:
	gboolean done = FALSE;
	gpointer item;
    CODE:
	while (!done) {
		switch (gst_iterator_next (iter, &item)) {
		case GST_ITERATOR_OK:
			RETVAL = sv_from_pointer (item, iter->type, TRUE);
			done = TRUE;
			break;

		case GST_ITERATOR_RESYNC:
			gst_iterator_resync (iter);
			break;

		case GST_ITERATOR_DONE:
			RETVAL = &PL_sv_undef;
			done = TRUE;
			break;

		case GST_ITERATOR_ERROR:
			croak ("An error occured while iterating");
		}
	}
    OUTPUT:
	RETVAL

# FIXME: Needed?
# void gst_iterator_push (GstIterator *it, GstIterator *other);

# FIXME?
# GstIterator * gst_iterator_filter (GstIterator *it, GCompareFunc func, gpointer user_data);
# GstIteratorResult gst_iterator_fold (GstIterator *iter, GstIteratorFoldFunction func, GValue *ret, gpointer user_data);
# GstIteratorResult gst_iterator_foreach (GstIterator *iter, GFunc func, gpointer user_data);
# gpointer gst_iterator_find_custom (GstIterator *it, GCompareFunc func, gpointer user_data);

MODULE = GStreamer::Iterator	PACKAGE = GStreamer::Iterator::Tie

IV
FETCHSIZE (GstIterator *iter)
    PREINIT:
	gboolean done = FALSE;
	gpointer item;
    CODE:
	RETVAL = 0;
	gst_iterator_resync (iter);

	while (!done) {
		switch (gst_iterator_next (iter, &item)) {
		case GST_ITERATOR_OK:
			RETVAL++;
			break;

		case GST_ITERATOR_RESYNC:
			RETVAL = 0;
			gst_iterator_resync (iter);
			break;

		 /* FIXME: Is it ok to handle ERROR like this? */
		case GST_ITERATOR_ERROR:
		case GST_ITERATOR_DONE:
			done = TRUE;
			break;
		}
	}
    OUTPUT:
	RETVAL

SV *
FETCH (GstIterator *iter, IV index)
    PREINIT:
	gboolean done = FALSE;
	gpointer item;
	IV counter = -1;
    CODE:
	RETVAL = &PL_sv_undef;
	gst_iterator_resync (iter);

	while (!done) {
		switch (gst_iterator_next (iter, &item)) {
		case GST_ITERATOR_OK:
			counter++;
			break;

		case GST_ITERATOR_RESYNC:
			counter = -1;
			gst_iterator_resync (iter);
			break;

		 /* FIXME: Is it ok to handle ERROR like this? */
		case GST_ITERATOR_ERROR:
		case GST_ITERATOR_DONE:
			done = TRUE;
			break;
		}

		if (counter == index) {
			RETVAL = sv_from_pointer (item, iter->type, TRUE);
			done = TRUE;
		}
	}
    OUTPUT:
	RETVAL