#include <glib.h>
#include <glib-object.h>
#include <gobject/gvaluecollector.h>
#include <stdio.h>

GType testtest_signaler_get_type (void);

#define TESTTEST_TYPE_SIGNALER                  (testtest_signaler_get_type ())
#define TESTTEST_SIGNALER(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), TESTTEST_TYPE_SIGNALER, TestTestSignaler))
#define TESTTEST_IS_SIGNALER(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TESTTEST_TYPE_SIGNALER))
#define TESTTEST_SIGNALER_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), TESTTEST_TYPE_SIGNALER, TestTestSignalerClass))
#define TESTTEST_IS_SIGNALER_CLASS(klass)       (G_TYPE_CHECK_CLASS_TYPE ((klass), TESTTEST_TYPE_SIGNALER))
#define TESTTEST_SIGNALER_GET_CLASS(obj)        (G_TYPE_INSTANCE_GET_CLASS ((obj), TESTTEST_TYPE_SIGNALER, TestTestSignalerClass))

typedef struct _TestTestSignaler        TestTestSignaler;
typedef struct _TestTestSignalerClass   TestTestSignalerClass;

static int count = 0;

struct _TestTestSignaler {
	GTypeInstance parent_instance;
	volatile int ref_count;
	int id;
};

struct _TestTestSignalerClass {
	GTypeClass parent_class;
	void (*finalize) (TestTestSignaler *self);
};

static gpointer testtest_signaler_parent_class = NULL;

gpointer testtest_signaler_ref (gpointer instance) {
	TestTestSignaler* self;
	self = instance;
	g_atomic_int_inc (&self->ref_count);
	return instance;
}

void testtest_signaler_unref (gpointer instance) {
	TestTestSignaler* self;
	self = instance;
	if (g_atomic_int_dec_and_test (&self->ref_count)) {
		TESTTEST_SIGNALER_GET_CLASS (self)->finalize (self);
		g_type_free_instance ((GTypeInstance *) self);
	}
}

static void testtest_signaler_finalize (TestTestSignaler* obj) {
	TestTestSignaler * self;
	self = G_TYPE_CHECK_INSTANCE_CAST (obj, TESTTEST_TYPE_SIGNALER, TestTestSignaler);
}

static void testtest_signaler_class_init (TestTestSignalerClass *klass) {
	testtest_signaler_parent_class = g_type_class_peek_parent (klass);
	TESTTEST_SIGNALER_CLASS (klass)->finalize = testtest_signaler_finalize;
	g_signal_new ("test-a-signal", TESTTEST_TYPE_SIGNALER, G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
}

static void testtest_signaler_instance_init (TestTestSignaler *self) {
	self->id = ++count;
}

static void value_testtest_signaler_init (GValue* value) {
	value->data[0].v_pointer = NULL;
}

static void value_testtest_signaler_free_value (GValue* value) {
	if (value->data[0].v_pointer) {
		testtest_signaler_unref (value->data[0].v_pointer);
	}
}

static void value_testtest_signaler_copy_value (const GValue* src_value, GValue* dest_value) {
	if (src_value->data[0].v_pointer) {
		dest_value->data[0].v_pointer = testtest_signaler_ref (src_value->data[0].v_pointer);
	} else {
		dest_value->data[0].v_pointer = NULL;
	}
}

static gpointer value_testtest_signaler_peek_pointer (const GValue* value) {
	return value->data[0].v_pointer;
}

static gchar* value_testtest_signaler_collect_value (GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
	if (collect_values[0].v_pointer) {
		TestTestSignaler* object;
		object = collect_values[0].v_pointer;
		if (object->parent_instance.g_class == NULL) {
			return g_strconcat ("invalid unclassed object pointer for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
		} else if (!g_value_type_compatible (G_TYPE_FROM_INSTANCE (object), G_VALUE_TYPE (value))) {
			return g_strconcat ("invalid object type `", g_type_name (G_TYPE_FROM_INSTANCE (object)), "' for value type `", G_VALUE_TYPE_NAME (value), "'", NULL);
		}
		value->data[0].v_pointer = testtest_signaler_ref (object);
	} else {
		value->data[0].v_pointer = NULL;
	}
	return NULL;
}

static gchar* value_testtest_signaler_lcopy_value (const GValue* value, guint n_collect_values, GTypeCValue* collect_values, guint collect_flags) {
	TestTestSignaler** object_p;
	object_p = collect_values[0].v_pointer;
	if (!object_p) {
		return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
	}
	if (!value->data[0].v_pointer) {
		*object_p = NULL;
	} else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) {
		*object_p = value->data[0].v_pointer;
	} else {
		*object_p = testtest_signaler_ref (value->data[0].v_pointer);
	}
	return NULL;
}

GType testtest_signaler_get_type (void) {
	static volatile gsize testtest_signaler_type_id__volatile = 0;
	if (g_once_init_enter (&testtest_signaler_type_id__volatile)) {
		static const GTypeValueTable g_define_type_value_table = { value_testtest_signaler_init, value_testtest_signaler_free_value, value_testtest_signaler_copy_value, value_testtest_signaler_peek_pointer, "p", value_testtest_signaler_collect_value, "p", value_testtest_signaler_lcopy_value };
		static const GTypeInfo g_define_type_info = { sizeof (TestTestSignalerClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) testtest_signaler_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (TestTestSignaler), 0, (GInstanceInitFunc) testtest_signaler_instance_init, &g_define_type_value_table };
		static const GTypeFundamentalInfo g_define_type_fundamental_info = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE) };
		GType testtest_signaler_type_id;
		testtest_signaler_type_id = g_type_register_fundamental (g_type_fundamental_next (), "TestTestSignaler", &g_define_type_info, &g_define_type_fundamental_info, 0);
		g_once_init_leave (&testtest_signaler_type_id__volatile, testtest_signaler_type_id);
	}
	return testtest_signaler_type_id__volatile;
}

TestTestSignaler *testtest_signaler_new () {
	TestTestSignaler *self;
	self = (TestTestSignaler *) g_type_create_instance (TESTTEST_TYPE_SIGNALER);
	return self;
}

void on_signal_received (TestTestSignaler *signaler, gpointer data) {
	printf ("%d\n", signaler->id);
	fflush (stdout);
}

int main (int argc, char *argv[]) {
	TestTestSignaler *signaler;
	int i;

	signaler = testtest_signaler_new ();
	g_signal_connect (signaler, "test-a-signal", (GCallback) on_signal_received, NULL);
	g_signal_emit_by_name (signaler, "test-a-signal");

	for (i = 0; i < 1000; i++) {
		signaler = testtest_signaler_new ();
		g_signal_emit_by_name (signaler, "test-a-signal");
	}

	return 0;
}
