/*
 * A BonoboObject to display audio/ulaw.
 *
 * Author:
 *   Chris Lahey <clahey@umich.edu>
 *
 * Based on bonobo-text-plain.c by
 *   Nat Friedman (nat@gnome-support.com)
 *
 */
#include <config.h>
#include "bonobo-audio-ulaw.h"
#include "item-audio.h"
#include "color.h"

/*
 * Utility functions.
 */
static void
free_sound (bonobo_object_data_t *bonobo_object_data)
{
	if (bonobo_object_data->sound != NULL)
		g_free (bonobo_object_data->sound);
	bonobo_object_data->sound = NULL;
	bonobo_object_data->sound_len = 0;
} /* free_sound */

static void
update_control (bonobo_object_data_t *bonobo_object_data)
{
	gnome_canvas_set_scroll_region (GNOME_CANVAS (bonobo_object_data->canvas),
					0, 0, bonobo_object_data->sound_len, 256);
	
	gnome_canvas_item_set (bonobo_object_data->canvas_item,
			       "BonoboObjectData", bonobo_object_data, NULL);
} /* update_view_foreach */
		  
static gboolean
update_control_callback (gpointer data)
{
	bonobo_object_data_t *bonobo_object_data = (bonobo_object_data_t *) data;

	update_control (bonobo_object_data);

	bonobo_object_data->update_callback_id = 0;

	return FALSE;
}

static void
progressive_update (bonobo_object_data_t *bonobo_object_data,
		    char *buff, size_t count)
{
	/*
	 * 1. Append the new sound data to the control's sound buffer.
	 */
	bonobo_object_data->sound = g_realloc (bonobo_object_data->sound,
					       bonobo_object_data->sound_len
					       + count);

	memcpy (bonobo_object_data->sound + bonobo_object_data->sound_len,
		buff, count);

	bonobo_object_data->sound_len += count;

	if (bonobo_object_data->update_callback_id == 0) {
		bonobo_object_data->update_callback_id = gtk_timeout_add
			(10, update_control_callback, (gpointer) bonobo_object_data);
	}

} /* progressive_update */

/*
 * Bonobo::PersistStream
 *
 * These two functions implement the Bonobo::PersistStream load and
 * save methods which allow data to be loaded into and out of the
 * BonoboObject.
 */
static int
stream_read (Bonobo_Stream stream, bonobo_object_data_t *bonobo_object_data)
{
	Bonobo_Stream_iobuf *buffer;
	CORBA_Environment    ev;

	CORBA_exception_init (&ev);
	do {
#define READ_CHUNK_SIZE 65536
		Bonobo_Stream_read (stream, READ_CHUNK_SIZE,
				    &buffer, &ev);

		if (ev._major != CORBA_NO_EXCEPTION) {
			CORBA_exception_free (&ev);
			return -1;
		}

		bonobo_object_data->sound = g_realloc (bonobo_object_data->sound,
						       bonobo_object_data->sound_len +
						       buffer->_length);

		memcpy (bonobo_object_data->sound + bonobo_object_data->sound_len,
			buffer->_buffer, buffer->_length);

		bonobo_object_data->sound_len += buffer->_length;
		if (buffer->_length <= 0)
			break;

		CORBA_free (buffer);
	} while (1);

	CORBA_free (buffer);
	CORBA_exception_free (&ev);

	return 0;
} /* stream_read */

/*
 * This function implements the Bonobo::PersistStream:load method.
 */
static void
pstream_load (BonoboPersistStream *ps, const Bonobo_Stream stream,
	      Bonobo_Persist_ContentType type, void *data,
	      CORBA_Environment *ev)
{
	bonobo_object_data_t *bonobo_object_data = data;

	if (*type && g_strcasecmp (type, "audio/ulaw") != 0) {	    
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Persist_WrongDataType, NULL);
		return;
	}

	/*
	 * 1. Free the old sound data.
	 */
	free_sound (bonobo_object_data);

	/*
	 * 2. Read the new sound data.
	 */
	if (stream_read (stream, bonobo_object_data) < 0) {
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Persist_FileNotFound, NULL);
		return;
	}

	/*
	 * 3. Update the displays.
	 */
	update_control (bonobo_object_data);
} /* pstream_load */

/*
 * This function implements the Bonobo::PersistStream:save method.
 */
static void
pstream_save (BonoboPersistStream *ps, const Bonobo_Stream stream,
	      Bonobo_Persist_ContentType type, void *data,
	      CORBA_Environment *ev)
{
	bonobo_object_data_t *bonobo_object_data = data;
	Bonobo_Stream_iobuf  *buffer;
	size_t                pos;

	if (*type && g_strcasecmp (type, "audio/ulaw") != 0) {	    
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Persist_WrongDataType, NULL);
		return;
	}

	/*
	 * Write the sound data into the stream.
	 *
	 * FIXME: Do we really _have_ to double-buffer the sound?
	 */
	buffer = Bonobo_Stream_iobuf__alloc ();

	data = CORBA_sequence_CORBA_octet_allocbuf (bonobo_object_data->sound_len);
	memcpy (data, bonobo_object_data->sound, bonobo_object_data->sound_len);

	buffer->_buffer = data;
	buffer->_length = bonobo_object_data->sound_len;

	pos = 0;
	
	while (pos < bonobo_object_data->sound_len) {
   
		Bonobo_Stream_write (stream, buffer, ev);

		if (ev->_major != CORBA_NO_EXCEPTION) {
			CORBA_free (buffer);
			CORBA_free (data);
			return;
		}

		pos += buffer->_length;
	}

	CORBA_free (buffer);
	CORBA_free (data);
} /* pstream_save */

static CORBA_long
pstream_get_max_size (BonoboPersistStream *ps, void *data,
		      CORBA_Environment *ev)
{
	bonobo_object_data_t *bonobo_object_data = data;

	return bonobo_object_data->sound_len;
}

static Bonobo_Persist_ContentTypeList *
pstream_get_content_types (BonoboPersistStream *ps, void *closure,
			   CORBA_Environment *ev)
{
	return bonobo_persist_generate_content_types (1, "audio/ulaw");
}

/*
 * Bonobo::ProgressiveDataSink
 *
 * These functions implement the ProgressiveDataSink interface
 * methods, which are used to send a slow stream of data to the widget
 * and have it update its views progressively.
 */

static int
progressive_start (BonoboProgressiveDataSink *psink, void *data)
{
	bonobo_object_data_t * bonobo_object_data = (bonobo_object_data_t *) data;

	free_sound (bonobo_object_data);
	update_control (bonobo_object_data);

	return 0;
} /* progressive_start */

static int
progressive_add_data (BonoboProgressiveDataSink *psink,
		      const Bonobo_ProgressiveDataSink_iobuf *buffer,
		      void *data)
{
	bonobo_object_data_t *bonobo_object_data = (bonobo_object_data_t *) data;

	progressive_update (bonobo_object_data, (char *) buffer->_buffer,
			    (size_t) buffer->_length);

	return 0;
} /* progressive_add_data */

/*
 * This callback is invoked when the BonoboControl object
 * encounters a fatal CORBA exception.
 */
static void
bonobo_object_system_exception_cb (BonoboObject *bonobo_object, CORBA_Object corba_object,
				   CORBA_Environment *ev, gpointer data)
{
	bonobo_object_unref (bonobo_object);
}

static void
bonobo_object_destroy_cb (BonoboObject *bonobo_object, bonobo_object_data_t *bonobo_object_data)
{
	if (bonobo_object_data->sound)
		g_free (bonobo_object_data->sound);
	bonobo_object_data->sound     = NULL;
	bonobo_object_data->sound_len = 0;

	gtk_object_unref (GTK_OBJECT (bonobo_object_data->vbox));

	g_free (bonobo_object_data); 
}

static BonoboObject *
generic_factory (BonoboGenericFactory *this,  void *data)
{
	BonoboControl *bonobo_object;
	BonoboPersistStream *stream;
	BonoboProgressiveDataSink *psink;
	bonobo_object_data_t *bonobo_object_data = data;

	bonobo_object_data = g_new0 (bonobo_object_data_t, 1);
	if (!bonobo_object_data)
		return NULL;
	
	bonobo_object_data->sound = NULL;
	bonobo_object_data->sound_len = 0;
	bonobo_object_data->update_callback_id = 0;

	bonobo_object_data->canvas = gnome_canvas_new ();
	gtk_widget_set_usize (GTK_WIDGET (bonobo_object_data->canvas), 256, 256);

	bonobo_object_data->canvas_item =
		gnome_canvas_item_new (gnome_canvas_root (GNOME_CANVAS (bonobo_object_data->canvas)),
				       item_audio_get_type (),
				       "BonoboObjectData", bonobo_object_data,
				       /* "height", 100.0, */
				       NULL);
	gnome_canvas_set_scroll_region (GNOME_CANVAS (bonobo_object_data->canvas),
					0, 0,
					bonobo_object_data->sound_len, 256);
	bonobo_object_data->hscroll = gtk_hscrollbar_new (gtk_layout_get_hadjustment
							  (GTK_LAYOUT (bonobo_object_data->canvas)));

	bonobo_object_data->vbox = gtk_vbox_new (0, FALSE);
	gtk_box_pack_start (GTK_BOX (bonobo_object_data->vbox), bonobo_object_data->canvas, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (bonobo_object_data->vbox), bonobo_object_data->hscroll, FALSE, FALSE, 0);
        gtk_widget_show_all (bonobo_object_data->vbox);

	/*
	 * Creates the Control.
	 */
	bonobo_object = bonobo_control_new (bonobo_object_data->vbox);
	if (bonobo_object == NULL) {
		gtk_object_unref (GTK_OBJECT (bonobo_object_data->vbox));
		g_free (bonobo_object_data);
		return NULL;
	}

	bonobo_object_data->bonobo_object = bonobo_object;

	/*
	 * Register the Bonobo::PersistStream interface.
	 */
	stream = bonobo_persist_stream_new (pstream_load, pstream_save,
					    pstream_get_max_size,
					    pstream_get_content_types,
					    bonobo_object_data);
	if (stream == NULL) {
		bonobo_object_unref (BONOBO_OBJECT (bonobo_object));
		g_free (bonobo_object_data);
		return NULL;
	}

	bonobo_object_add_interface (BONOBO_OBJECT (bonobo_object),
				     BONOBO_OBJECT (stream));

	/*
	 * Register the Bonobo::ProgressiveDataSink interface.
	 */
	psink = bonobo_progressive_data_sink_new (progressive_start,
						  NULL, /* progressive_end */
						  progressive_add_data,
						  NULL, /* progressive_set_size */
						  bonobo_object_data);
	
	if (psink == NULL) {
		bonobo_object_unref (BONOBO_OBJECT (bonobo_object));
		g_free (bonobo_object_data);
		return NULL;
	}

	bonobo_object_add_interface (BONOBO_OBJECT (bonobo_object),
				     BONOBO_OBJECT (psink));

	gtk_signal_connect (GTK_OBJECT (bonobo_object), "destroy",
			    GTK_SIGNAL_FUNC (bonobo_object_destroy_cb),
			    bonobo_object_data);

	gtk_signal_connect (GTK_OBJECT (bonobo_object), "system_exception",
			    GTK_SIGNAL_FUNC (bonobo_object_system_exception_cb),
			    bonobo_object_data);

	return BONOBO_OBJECT (bonobo_object);
} /* generic_factory */

BONOBO_OAF_FACTORY ("OAFIID:Bonobo_Sample_Audio_ulaw_Factory",
		    "bonobo-audio-ulaw", VERSION,
		    generic_factory,
		    NULL)


syntax highlighted by Code2HTML, v. 0.9.1