/*
 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
 */
#include "e.h"

/* yes - i know. glibc specific... but i like being able to do my own */
/* backtraces! NB: you need CFLAGS="-rdynamic -g" LDFLAGS="-rdynamic -g" */
#ifdef OBJECT_PARANOIA_CHECK

/* local subsystem functions */
static void _e_object_segv(int sig);

/* local subsystem globals */
static sigjmp_buf _e_object_segv_buf;
#endif

/* externally accessible functions */
EAPI void *
e_object_alloc(int size, int type, E_Object_Cleanup_Func cleanup_func)
{
   E_Object *obj;
   
   obj = calloc(1, size);
   if (!obj) return NULL;
   obj->magic = E_OBJECT_MAGIC;
   obj->type = type;
   obj->references   = 1;
   obj->cleanup_func = cleanup_func;
   return obj;
}

EAPI void
e_object_del(E_Object *obj)
{
   E_OBJECT_CHECK(obj);
   if (obj->deleted) return;
   if (obj->del_att_func) obj->del_att_func(obj);
   if (obj->del_func) obj->del_func(obj);
   obj->deleted = 1;
   e_object_unref(obj);
}

EAPI int
e_object_is_del(E_Object *obj)
{
   E_OBJECT_CHECK_RETURN(obj, 1);
   return obj->deleted;
}

EAPI void
e_object_del_func_set(E_Object *obj, E_Object_Cleanup_Func del_func)
{
   E_OBJECT_CHECK(obj);
   obj->del_func = del_func;
}

EAPI void
e_object_type_set(E_Object *obj, int type)
{
   E_OBJECT_CHECK(obj);
   obj->type = type;
}

EAPI void
e_object_free(E_Object *obj)
{
   E_OBJECT_CHECK(obj);
/*   
   if (obj->crumbs)
     {
	printf("EEEK obj type %x has crumbs still! ->\n", obj->type);
	e_object_breadcrumb_debug(obj);
     }
 */
   if (obj->free_att_func) obj->free_att_func(obj);
// FIXME: although this is good - if during cleanup the cleanup func calls
// other generic funcs to do cleanups on the same object... we get bitching.
// disable for now (the final free of the struct should probably happen after
// the cleanup func and be done byt he object system - set the magic after
// cleanup :)  
//   obj->magic = E_OBJECT_MAGIC_FREED;
   obj->cleanup_func(obj);
}

EAPI int
e_object_ref(E_Object *obj)
{
   E_OBJECT_CHECK_RETURN(obj, -1);
   obj->references++;
   return obj->references;
}

EAPI int
e_object_unref(E_Object *obj)
{
   int ref;
   
   E_OBJECT_CHECK_RETURN(obj, -1);
   obj->references--;
   ref = obj->references;
   if (obj->references == 0) e_object_free(obj);
   return ref;
}

EAPI int
e_object_ref_get(E_Object *obj)
{
   E_OBJECT_CHECK_RETURN(obj, 0);
   return obj->references;
}

EAPI int
e_object_error(E_Object *obj)
{
#ifdef OBJECT_PARANOIA_CHECK   
   char buf[4096];
   char bt[8192];
   void *trace[128];
   char **messages = NULL;
   int i, trace_num;

   /* fetch stacktrace */
   trace_num = backtrace(trace, 128);
   messages = backtrace_symbols(trace, trace_num);

   /* build stacktrace */
   bt[0] = 0;
   if (messages)
     {
	for (i = 1; i < trace_num; i++)
	  {
	     strcat(bt, messages[i]);
	     strcat(bt, "\n");
	  }
	free(messages);
     }
   /* if NULL obj then dump stacktrace */
   if (!obj)
     {
	snprintf(buf, sizeof(buf),
		 "Object is NULL.\n"
		 "%s",
		 bt);
     }
   /* if obj pointer is non NULL, actually try an access and see if we segv */
   else if (obj)
     {
	struct sigaction act, oact;
	int magic = 0, segv = 0;
	
	/* setup segv handler */
	act.sa_handler = _e_object_segv;
	act.sa_flags   = SA_RESETHAND;
	sigemptyset(&act.sa_mask);
	sigaction(SIGSEGV, &act, &oact);
	/* set a longjump to be within this if statement. only called if we */
	/* segfault */
	if (sigsetjmp(_e_object_segv_buf, 1))
	  {
	     sigaction(SIGSEGV, &oact, NULL);
	     segv = 1;
	  }
	else
	  {
	     /* try access magic value */
	     magic = obj->magic;
	     /* if pointer is bogus we'd segv and so jump to the if() above */
	     /* contents, and thus reset segv handler and set segv flag. */
	     /* if not we just continue moving along here and reset handler */
	     sigaction(SIGSEGV, &oact, NULL);
	  }
	/* if we segfaulted above... */
	if (segv)
	  snprintf(buf, sizeof(buf),
		   "Object [%p] is an invalid/garbage pointer.\n"
		   "Segfault successfully avoided.\n"
		   "%s",
		   obj,
		   bt);
	else
	  {
	     /* valid ram then... if we freed this object before */
	     if (magic == E_OBJECT_MAGIC_FREED)
	       snprintf(buf, sizeof(buf),
			"Object [%p] is already freed.\n"
			"%s",
			obj,
			bt);
	     /* garbage magic value - pointer to non object */
	     else if (magic != E_OBJECT_MAGIC)
	       snprintf(buf, sizeof(buf),
			"Object [%p] has garbage magic (%x).\n"
			"%s",
			obj, magic,
			bt);
	     else if (obj->references < 0)
	       snprintf(buf, sizeof(buf),
			"Object [%p] has negative references (%i).\n"
			"%s",
			obj, obj->references,
			bt);
	     else if (obj->references > 100)
	       snprintf(buf, sizeof(buf),
			"Object [%p] has unusually high reference count (%i).\n"
			"%s",
			obj, obj->references,
			bt);
	     /* it's all ok! */
	     else
	       {
		  return 0;
	       }
	  }
     }
   /* display actual error message */
   e_error_message_show("%s", buf);
//   abort();
   return 1;
#else
   return 0;
#endif   
}

EAPI void
e_object_data_set(E_Object *obj, void *data)
{
   E_OBJECT_CHECK(obj);
   obj->data = data;
}

EAPI void *
e_object_data_get(E_Object *obj)
{
   E_OBJECT_CHECK_RETURN(obj, NULL);
   return obj->data;
}

EAPI void
e_object_free_attach_func_set(E_Object *obj, void (*func) (void *obj))
{
   E_OBJECT_CHECK(obj);
   obj->free_att_func = func;
}

EAPI void
e_object_del_attach_func_set(E_Object *obj, void (*func) (void *obj))
{
   E_OBJECT_CHECK(obj);
   obj->del_att_func = func;
}

/*
void
e_object_breadcrumb_add(E_Object *obj, char *crumb)
{
   E_OBJECT_CHECK(obj);
   obj->crumbs = evas_list_append(obj->crumbs, strdup(crumb));
}

void
e_object_breadcrumb_del(E_Object *obj, char *crumb)
{
   Evas_List *l;
   
   E_OBJECT_CHECK(obj);
   for (l = obj->crumbs; l; l = l->next)
     {
	if (!strcmp(crumb, l->data))
	  {
	     free(l->data);
	     obj->crumbs = evas_list_remove_list(obj->crumbs, l);
	     return;
	  }
     }
}

void
e_object_breadcrumb_debug(E_Object *obj)
{
   Evas_List *l;
   
   E_OBJECT_CHECK(obj);
   for (l = obj->crumbs; l; l = l->next)
     printf("CRUMB: %s\n", l->data);
}
*/

#ifdef OBJECT_PARANOIA_CHECK
/* local subsystem functions */
static void
_e_object_segv(int sig)
{
   siglongjmp(_e_object_segv_buf, 1);
}
#endif
