//#include <math.h>
/*http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29*/
/*http://en.wikipedia.org/wiki/Free_list*/

#define NULE 0
#define MULE 8888

#define NOIDEA_RT 7777
#define NOIDEA_EVENT 6666

typedef struct NOIDEA_memory
{
	int id;
	int size;
	struct NOIDEA_memory * last;
	struct NOIDEA_memory * next;
} NOIDEA_memory;

typedef struct NOIDEA_yromem
{
	NOIDEA_memory * space;
	int id;
} NOIDEA_yromem;

/*after reading in wikipedia , i found similarity with Garbage Collection by John McCarthy*/
/*so i've invented nothing*/

/*give an array or a malloc to this function */
NOIDEA_memory * NOIDEA_seg (void * noidea_array, int noidea_size)
{

	NOIDEA_memory * noidea_header = (NOIDEA_memory*)noidea_array;
	NOIDEA_memory * noidea_new = (NOIDEA_memory*)(noidea_array + sizeof(NOIDEA_memory));
	NOIDEA_yromem * noidea_ender = (NOIDEA_yromem*)(noidea_array + noidea_size - sizeof(NOIDEA_yromem));
	noidea_header->id = MULE;
	noidea_header->size = noidea_size;
	noidea_header->last = noidea_new;
	noidea_header->next = noidea_new;
	noidea_new->id = MULE;
	noidea_new->size = noidea_size - sizeof(NOIDEA_memory);
	noidea_new->last = noidea_header;
	noidea_new->next = noidea_header;
	noidea_ender->space = noidea_header;
	noidea_ender->id = MULE;
	return noidea_header;
}

void * NOIDEA_alloc (NOIDEA_memory * noidea_segment, int noidea_size)
{
	if (noidea_size < sizeof(NOIDEA_memory)+sizeof(NOIDEA_yromem))
	{
		noidea_size = sizeof(NOIDEA_memory)+sizeof(NOIDEA_yromem);
	}
	NOIDEA_memory * noidea_pivot = noidea_segment->next;
	NOIDEA_memory * noidea_enogh;
	while (noidea_pivot != noidea_segment)
	{
		if (noidea_pivot->size - sizeof(NOIDEA_memory) > noidea_size + sizeof(int))
		{
			noidea_enogh = noidea_pivot;
			noidea_pivot = noidea_segment->last;
		}
		noidea_pivot = noidea_pivot->next;
	}

	if (noidea_enogh != 0)
	{
		NOIDEA_memory * noidea_mov = (NOIDEA_memory*)((void*)noidea_enogh + noidea_size + sizeof(int));
		NOIDEA_yromem * noidea_set = (NOIDEA_yromem*)((void*)noidea_enogh + noidea_enogh->size - sizeof(NOIDEA_yromem));
		void * noidea_new = (void*) noidea_enogh;

		noidea_mov->id = noidea_enogh->id;
		noidea_mov->size = noidea_enogh->size - noidea_size - sizeof(int);
		noidea_mov->last = noidea_enogh->last;
		noidea_mov->next = noidea_enogh->next;
		noidea_mov->last->next = noidea_mov;
		noidea_mov->next->last = noidea_mov;

		noidea_set->space = noidea_mov;
		*(int*)noidea_new = noidea_size;
		noidea_new = noidea_new +sizeof(int);

		//debug debug debug debug debug debug
		*(int*)noidea_new = 0;
		*(int*)(noidea_new + sizeof(int)) = 0;
		*(int*)(noidea_new + (sizeof(int) * 2)) = 0;
		//debug debug debug debug debug debug

		return noidea_new;
	}
	return NULE;
}

void NOIDEA_free (NOIDEA_memory * noidea_segment, void * noidea_offset)
{
	int noidea_size = *(int*)(noidea_offset - sizeof(int));
	NOIDEA_memory * noidea_new = (NOIDEA_memory*) (noidea_offset - sizeof(int));
	NOIDEA_yromem * noidea_end = (NOIDEA_yromem*) (noidea_offset + noidea_size - sizeof(NOIDEA_yromem));
	noidea_new->id = MULE;
	noidea_new->size = noidea_size + sizeof(int);
	noidea_new->last = noidea_segment;
	noidea_new->next = noidea_segment->next;
	noidea_new->last->next = noidea_new;
	noidea_new->next->last = noidea_new;
	noidea_end->id = MULE;
	noidea_end->space = noidea_new;
	if ((*(int*)(noidea_offset + noidea_size)) == MULE )
	{

		NOIDEA_memory * noidea_forward = (NOIDEA_memory*)(noidea_offset + noidea_size);
		NOIDEA_yromem * noidea_foreend = (NOIDEA_yromem*)((void*)noidea_forward + noidea_forward->size - sizeof(NOIDEA_yromem));
		noidea_new->size = noidea_new->size + noidea_forward->size;
		noidea_forward->next->last = noidea_forward->last;
		noidea_forward->last->next = noidea_forward->next;
		noidea_foreend->space = noidea_new;
		//debug
		noidea_end->id = 0;
		noidea_end->space = 0;
		noidea_forward->id = 0;
		noidea_forward->size = 0;
		noidea_forward->last = 0;
		noidea_forward->next = 0;
		//debug
		noidea_end = noidea_foreend;
	}
	if ((*(int*)(noidea_offset - (sizeof(int)*2))) == MULE )
	{
		NOIDEA_yromem * noidea_backend = (NOIDEA_yromem*)(noidea_offset - (sizeof(int)*2) - sizeof(void*));
		NOIDEA_memory * noidea_bacward = noidea_backend->space;

		noidea_bacward->size = noidea_bacward->size + noidea_new->size;
		noidea_new->next->last = noidea_new->last;
		noidea_new->last->next = noidea_new->next;
		noidea_end->space = noidea_bacward;
		//debug
		noidea_backend->id = 0;
		noidea_backend->space = 0;
		noidea_new->id = 0;
		noidea_new->size = 0;
		noidea_new->last = 0;
		noidea_new->next = 0;
		//debug
		noidea_new = noidea_bacward;
	}
}

//================================================================================
//================================================================================
//================================================================================

/*http://en.wikipedia.org/wiki/Linked_list*/


typedef struct NOIDEA_node
{
	void * pack;
	struct NOIDEA_node * last;
	struct NOIDEA_node * next;
} NOIDEA_node;

NOIDEA_node * NOIDEA_create (NOIDEA_node * noidea_src, void * noidea_pack, void * noidea_ptr)
{
	if (noidea_ptr == 0)
	{	return 0;	}

	NOIDEA_node * noidea_nud = (NOIDEA_node*) noidea_ptr;
	noidea_nud->pack = noidea_pack;

	if (noidea_src != 0)
	{
		noidea_nud->last = noidea_src;
		noidea_nud->next = noidea_src->next;
		noidea_nud->last->next = noidea_nud;
		noidea_nud->next->last = noidea_nud;
	}
	else
	{
		noidea_nud->last = noidea_nud;
		noidea_nud->next = noidea_nud;
	}

	return noidea_nud;
}

void * NOIDEA_destroy (NOIDEA_node * noidea_src, NOIDEA_node * noidea_nud)
{
	if ((noidea_src == 0)||(noidea_nud == 0))
	{	return;		}

	if (noidea_src == noidea_nud)
	{
		//you shouldnt destroy sentinel if array is not empty
			if (noidea_src != noidea_src->next)
			{
				return;
			}
	}

	noidea_nud->last->next = noidea_nud->next;
	noidea_nud->next->last = noidea_nud->last;

	noidea_nud->pack = 0;
	noidea_nud->last = 0;
	noidea_nud->next = 0;

	return (void*) noidea_nud;	//return ptr in case of alloc/free
}

void * NOIDEA_loop (NOIDEA_node * noidea_src)
/* all node will move around noidea_src */
/* so its sure that youll get them all */
{
	NOIDEA_node * noidea_nud = noidea_src->next;

	noidea_src->last->next = noidea_src->next;
	noidea_src->next->last = noidea_src->last;

	noidea_nud->next->last = noidea_src;
	noidea_src->next = noidea_nud->next;

	noidea_nud->next = noidea_src;
	noidea_src->last = noidea_nud;


	return noidea_nud->pack;
}

void * NOIDEA_rev (NOIDEA_node * noidea_src)
/* all node will move around noidea_src */
/* so its sure that youll get them all */
{
	NOIDEA_node * noidea_nud = noidea_src->last;

	noidea_src->next->last = noidea_src->last;
	noidea_src->last->next = noidea_src->next;

	noidea_nud->last->next = noidea_src;
	noidea_src->last = noidea_nud->last;

	noidea_nud->last = noidea_src;
	noidea_src->next = noidea_nud;


	return noidea_nud->pack;
}


//================================================================================
//================================================================================
//================================================================================



typedef struct NOIDEA_agenda
{
	int type;	//identify type of node
	NOIDEA_node * node;		//the node that list it
	signed long int wait;		//interval between call
	void (*func)(void);		//function to call
	signed long int till;		//wait till to execute
	signed long int idle;		//time left before execute
	signed long int loop;		//value that end loop
} NOIDEA_agenda;


NOIDEA_node * NOIDEA_schedule (NOIDEA_node * noidea_sentinel, void (*noidea_func)(void), signed long int noidea_wait ,void * noidea_ptr)
{
	if ((noidea_sentinel != 0)&&( *(int*)noidea_sentinel->pack != NOIDEA_RT))
	{
		return 0;
	}
	NOIDEA_agenda * noidea_now = (NOIDEA_agenda*) noidea_ptr;
	NOIDEA_node * noidea_node_now = NOIDEA_create(noidea_sentinel,noidea_now,noidea_ptr + sizeof(NOIDEA_agenda));
	noidea_now->node = noidea_node_now;
	noidea_now->func = noidea_func;
	noidea_now->wait = noidea_wait;
	noidea_now->type = NOIDEA_RT;
	return noidea_node_now;
}

NOIDEA_node * NOIDEA_reschedule (NOIDEA_node * noidea_sentinel, NOIDEA_node * noidea_what, signed long int noidea_wait)
{
	if ((noidea_sentinel != 0)&&( *(int*)noidea_sentinel->pack != NOIDEA_RT))
	{
		return 0;
	}
	if ((noidea_sentinel == 0) || (noidea_what == 0))
	{
		return 0;
	}
	NOIDEA_agenda * noidea_today = (NOIDEA_agenda*)noidea_sentinel->pack;
	NOIDEA_agenda * noidea_now = (NOIDEA_agenda*)noidea_what->pack;

	signed long int noidea_till = noidea_now->till - noidea_now->wait + noidea_wait;
	signed long int noidea_idle = noidea_now->till - noidea_now->idle + noidea_till;

	noidea_now->wait = noidea_wait;
	noidea_now->idle = noidea_idle;
	noidea_now->till = noidea_till;

	return noidea_what;
}

void * NOIDEA_cancel (NOIDEA_node * noidea_node_today, NOIDEA_node * noidea_node_now)
{
	if ((noidea_node_today != 0)&&( *(int*)noidea_node_today->pack != NOIDEA_RT))
	{
		return ;
	}
	NOIDEA_agenda * noidea_now = noidea_node_now->pack;
	noidea_now->wait = 0;
	noidea_now->func = 0;
	noidea_now->idle = 0;
	noidea_now->till = 0;
	noidea_now->loop = 0;
	noidea_now->node = 0;
	noidea_now->type = 0;
	noidea_node_now = NOIDEA_destroy (noidea_node_today, noidea_node_now);
	return ((void*)noidea_now);
}

signed long int NOIDEA_do (NOIDEA_node * noidea_sentinel, signed long int noidea_time)
{
	if ((noidea_sentinel != 0)&&( *(int*)noidea_sentinel->pack != NOIDEA_RT))
	{
		return -1;
	}
	NOIDEA_agenda * noidea_today = (NOIDEA_agenda*)noidea_sentinel->pack;
	NOIDEA_agenda * noidea_now;
	noidea_today->loop += 1;
	noidea_today->idle = noidea_today->wait;
	int noidea_yet = 0;
	while (noidea_yet != 1)
	{

		noidea_now = NOIDEA_loop(noidea_sentinel);
			if (noidea_now->loop == noidea_today->loop)
			{
				noidea_yet = 1;
			}
			

			if (noidea_now->till == 0)
			{
				noidea_now->till = noidea_time + noidea_now->wait;
			}
			if (noidea_now->till <= noidea_time)
			{
				noidea_now->till = noidea_time + noidea_now->wait;
				noidea_now->idle = 0;
				noidea_now->func();
				noidea_yet = 1;
			}
			else
			{
				noidea_now->idle = noidea_now->till - noidea_time;
				noidea_now->loop = noidea_today->loop;
			}
			if (noidea_now->idle < noidea_today->idle)
			{
				noidea_today->idle = noidea_now->idle;
			}
	}
	return noidea_today->idle;
}

//================================================================================
//================================================================================
//================================================================================

typedef struct NOIDEA_event
{
	int type;	//identify the node type
	NOIDEA_node * node;		//the node that list it
	int event;			//event number that will summon it
	void (*func)(void);		//function to call at given event
} NOIDEA_event;

NOIDEA_node * NOIDEA_plug (NOIDEA_node * noidea_sentinel, int noidea_num, void (*noidea_func)(void), void * noidea_ptr)
{
	if ((noidea_sentinel != 0)&&( *(int*)noidea_sentinel->pack != NOIDEA_RT))
	{
		return 0;
	}
	if ((noidea_sentinel == 0)||(noidea_ptr || 0))
	{
		return 0;
	}
	NOIDEA_event * noidea_now = (NOIDEA_event*) noidea_ptr;
	NOIDEA_node * noidea_node_now = NOIDEA_create(noidea_sentinel, noidea_now, noidea_ptr + sizeof(NOIDEA_event));

	noidea_now->node = noidea_node_now;
	noidea_now->type = NOIDEA_EVENT;
	noidea_now->event = noidea_num;
	noidea_now->func = noidea_func;

	return noidea_node_now;
}

void * NOIDEA_unplug (NOIDEA_event * noidea_event)
{
}

void NOIDEA_eventloop (NOIDEA_event * noidea_event, int noidea_num)
{
}

//================================================================================
//================================================================================
//================================================================================
