
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <glib-object.h>


/* *************** */

typedef void (*scope_destroy_func) (void* scope);

typedef volatile struct {
	gint ref_count;
	scope_destroy_func destroy_func; 
	void* parent;   // parent scope or object
	
	char* name; // just for debugging
} scope_info;

static scope_info* ref_scope (scope_info* scope)
{
	g_atomic_int_inc (&(scope->ref_count));
	printf ("Referenced a scope %s, now has: %d references\n", scope->name, scope->ref_count);
	return scope;
}

static void unref_scope (scope_info* scope)
{
	printf ("Unreferenced a scope %s, now has: %d references\n", scope->name, scope->ref_count - 1);
	if (g_atomic_int_dec_and_test (&(scope->ref_count))) scope->destroy_func (scope);
}

static void closure_notify_func (gpointer data, GClosure* closure)
{
	printf ("Destroying a closure with scope %s\n", ((scope_info*) data)->name);
	unref_scope ((scope_info*) data);
}



/* *************** */


typedef volatile struct
{
	scope_info _info_;

	int inner_var_2;
	int param2;
	void* dynamic_data;
	
} __parent_method_scope_1;

static void __parent_method_scope_1_destroy (__parent_method_scope_1* scope)
{
	printf ("Destroying scope 1\n");

	if (scope->dynamic_data)
		free (scope->dynamic_data);
	free (scope);
	
	printf ("Scope 1 destroyed.\n");
}

typedef volatile struct
{
	scope_info _info_;

	char* some_text;
	GObject* some_object;
	
} __parent_method_scope_1_1;

static void __parent_method_scope_1_1_destroy (__parent_method_scope_1_1* scope)
{
	printf ("Destroying scope 1.1\n");

	if (scope->some_object)
		g_object_unref (scope->some_object);
	unref_scope ((scope_info*) (scope->_info_.parent));
	free (scope);
	
	printf ("Scope 1.1 destroyed.\n");
}

typedef volatile struct
{
	scope_info _info_;

	long number;
	
} __parent_method_scope_1_2;

static void __parent_method_scope_1_2_destroy (__parent_method_scope_1_2* scope)
{
	printf ("Destroying scope 1.2\n");

	unref_scope ((scope_info*) (scope->_info_.parent));
	free (scope);
	
	printf ("Scope 1.2 destroyed.\n");
}


static void __parent_method_lambda_1 (gpointer whatever, __parent_method_scope_1_1* scope /* passed as user_data */)
{
	/* content of first lambda */
	
	scope->some_text = g_strconcat (scope->some_text, " world\n");	
}

static void __parent_method_lambda_2 (gpointer whatever, __parent_method_scope_1_2* scope)
{
	/* content of second lambda */
	
	printf ("%d\n", scope->number ++);
}

GClosure* parent_method (int param1, int param2, char* text)
{
	__parent_method_scope_1* _scope_1_ = calloc (1, sizeof (__parent_method_scope_1));
	_scope_1_->_info_.name = "Scope1";
	_scope_1_->_info_.ref_count = 1;
	_scope_1_->_info_.destroy_func = __parent_method_scope_1_destroy;
	
	int inner_var_1 = 0;
	_scope_1_->inner_var_2 = 2;
	_scope_1_->param2 = param2; // copy the value of used parameters into the scope struct and use just the copy from now on

	{
		__parent_method_scope_1_1* _scope_1_1_ = calloc (1, sizeof (__parent_method_scope_1_1));
		_scope_1_1_->_info_.name = "Scope1.1";
		_scope_1_1_->_info_.ref_count = 1;
		_scope_1_1_->_info_.destroy_func = __parent_method_scope_1_1_destroy;
		_scope_1_1_->_info_.parent = ref_scope ((scope_info *) _scope_1_);
		
		_scope_1_1_->some_text = "Hello";
		
		GClosure* closure = g_cclosure_new (G_CALLBACK (__parent_method_lambda_1), ref_scope ((scope_info *) _scope_1_1_), closure_notify_func);
		g_closure_set_marshal (closure, g_cclosure_marshal_VOID__VOID);
		/* g_object_watch_closure (self, closure);  would be needed if this was an instance method and the lambda accesses the parent object */
		
		g_closure_invoke (closure, NULL, 1, NULL, NULL);
		
		g_closure_sink (closure);
		
		printf (_scope_1_1_->some_text);
		
		unref_scope ((scope_info *) _scope_1_1_);
	}
	
	GClosure* result;
	
	{
		__parent_method_scope_1_2* _scope_1_2_ = calloc (1, sizeof (__parent_method_scope_1_2));
		_scope_1_2_->_info_.name = "Scope1.2";
		_scope_1_2_->_info_.ref_count = 1;
		_scope_1_2_->_info_.destroy_func = __parent_method_scope_1_2_destroy;
		_scope_1_2_->_info_.parent = ref_scope ((scope_info *) _scope_1_);
		_scope_1_2_->number = 5;
		
		result = g_cclosure_new (G_CALLBACK(__parent_method_lambda_2), ref_scope ((scope_info *) _scope_1_2_), closure_notify_func);
		g_closure_set_marshal (result, g_cclosure_marshal_VOID__VOID);
		
		unref_scope ((scope_info *) _scope_1_2_);
	}
	
	unref_scope ((scope_info *) _scope_1_);
	
	return result;
}

void main ()
{
	GClosure* closure = parent_method (1, 2, "3");
	
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	g_closure_invoke (closure, NULL, 1, NULL, NULL);
	
	g_closure_sink (closure);
}


