/*****
 NAME
	cidlist.m - source code for CIdList class
 VERSION
	$Id$
 CHANGELOG
	$Log$
 */

#include <coconut/cidlist.h>
#include <coconut/csystem.h>
#include <coconut/cerror.h>
#include <coconut/ptext.h>

#define	OBJ(PTR)	((id) PTR)

static GList * makeNewNode(id obj){
	GList * newnode ;
	
	newnode = g_list_alloc() ;
	[CSystem checkPtr: newnode] ;
	[obj retain] ;
	newnode->data = (gpointer) obj ;
	return newnode ;
}

@implementation CIdList

- init
{
	root_node = current_node = NULL ;
	return [super init] ;
}

- (void) dealloc
{
	[self clear] ; 
	return [super dealloc] ;
}

- (u_int) count
{
	return g_list_length(root_node) ;
}

- clear
{
	GList *	node ;

	if(root_node){
		for(node = root_node ; node ; node = node->next)
			[OBJ(node->data) release] ;
		g_list_free(root_node) ;
	}
	root_node = current_node = NULL ;
	return nil ;
}

- foreach: (SEL) msg with: p1 with: p2
{
	GList *	node ;
	id	result ;
	for(node = root_node ; node ; node = node->next){
		result = [OBJ(node->data) perform: msg with: p1 with: p2] ;
		if(result)
			return result ;
	}
	return nil ;
}

- foreach: (id) obj message: (SEL) msg with: p1 
{
	GList *	node ;
	id	result ;
	for(node = root_node ; node ; node = node->next){
		result = [obj perform: msg with: node->data with: p1] ;
		if(result)
			return result ;
	}
	return nil ;
}

- append: (id) obj
{
	GList * newnode ;

	/* this function does not returns last node */
	[obj retain] ;
	newnode = g_list_append(current_node, obj) ;
	if(current_node == NULL){
		root_node = current_node = newnode ;
	}
	return nil ;
}

- prepend: (id) obj
{
	/* this function does not returns last node */
	[obj retain] ;
	root_node = g_list_prepend(root_node, obj) ;
	if(current_node == NULL){
		current_node = root_node ;
	} 
	return nil ;
}

- add: (id) obj
{
	GList * newnode ;
	GList * nextnode ;

	newnode = makeNewNode(obj) ;
	if(current_node){
		if((nextnode = current_node->next) != NULL)
			nextnode->prev = newnode ;
		current_node->next = newnode ;
		newnode->next = nextnode ;
		newnode->prev = current_node ;
		current_node = newnode ;
	} else
		root_node = current_node = newnode ;
	return nil ;
}

- insert: (id) obj
{
	GList * newnode ;
	GList * prevnode ;

	newnode = makeNewNode(obj) ;
	if(current_node){
		if((prevnode = current_node->prev) != NULL)
			prevnode->next = newnode ;
		current_node->prev = newnode ;
		newnode->next = current_node ;
		newnode->prev = prevnode ;
		if(prevnode == NULL)
			root_node = newnode ;
	} else
		root_node = current_node = newnode ;
	return nil ;
}

- (id) currentItem
{
	return current_node ? OBJ(current_node->data) : nil ;
}

- (id) item: (u_int) no
{
	GList * node ;

	if(root_node){
		if((node = g_list_nth(root_node, no)) != NULL)
			return OBJ(node->data) ;
	}
	return nil ;
}

- (id) moveNext
{
	GList * 	next ;
	id		result = nil ;

	if(current_node){
		if((next = current_node->next) != NULL){
			current_node = next ;
			result = OBJ(current_node->data) ;
		}
	}
	return result ;
}

- (id) movePrev
{
	GList * prev ;
	id	result = nil ;

	if(current_node){
		if((prev = current_node->prev) != NULL){
			current_node = prev ;
			result = OBJ(current_node->data) ;
		}
	}
	return result ;
}

- (id) moveToHead
{
	current_node = root_node ;
	return current_node ? OBJ(current_node->data) : nil ;
}

- (id) moveToTail
{
	id result = nil ;
	if(current_node){
		current_node = g_list_last(current_node) ;
		result = OBJ(current_node->data) ;
	}
	return result ;
}

- (id <PError>) remove
{
	GList * newnode ;
	if(current_node){
		newnode = current_node->prev ;
		if(newnode == NULL)
			newnode = current_node->next ;
		root_node = g_list_remove_link(root_node, current_node) ;
		[OBJ(current_node->data) release] ;
		g_list_free_1(current_node) ;
		current_node = newnode ;
		return nil ;
	} else
		return [CError not_exist] ;
}

- print: (id <PIndentStream>) stream
{
	GList *	node ;

	[stream putPtr: "(list\n"] ;
	  [stream incLevel] ;
	  for(node = root_node ; node ; node = node->next){
		[OBJ(node->data) print: stream] ;
		[stream putChar: '\n'] ;
	  }
	  [stream decLevel] ;
	[stream putPtr: ")\n"] ;
	return nil ;
}

@end

