/**********************************************************************
 
	Copyright (C) 2003-2004
	Hirohisa MORI <joshua@nichibun.ac.jp>
	Tomoki SEKIYAMA <sekiyama@yahoo.co.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

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



#include <string.h>

#include "v/VTreeView.h"
extern "C" {
#include "memory_debug.h"
}

// ========================================================= 
//   VTreeNode
// =========================================================

VTreeNode::VTreeNode(int cols)
{
	data = new const void*[cols];
	memset(data, 0, sizeof(void*)*cols);
	editable = 0;
}

VTreeNode::VTreeNode(int cols, const VTreeNode& node)
{
	data = new const void*[cols];

	key = node.key;
	for ( int i = 0 ; i < cols ; i++ ) {
		data[i] = node.data[i];
	}
	editable = node.editable;
}

VTreeNode::~VTreeNode()
{
	delete[] data;
}



// ========================================================= 
//   VTreeView machine-independent
// =========================================================

VTreeView::Selection *
VTreeView::copy_selection(const VTreeView *view)
{
	// must be called in msequence
	Selection *ret = new Selection;
	int n = ret->n = view->selection->n;
	ret->keys = new void*[n];
	for ( int i = 0 ; i < n ; i++ )
		ret->keys[i] = view->selection->keys[i];
	return ret;
}

void
VTreeView::select_event(VTreeView *view, Selection* sel)
{
	// called from machine-dependent code on row(s) selected
	// sel -> newly allocated Selection object
	delete view->selection;
	view->selection = sel;
	if ( view->select_event_handler )
		vq_insert_callback(view, view->select_event_handler,
			view->user_arg_select, 0, 0);
}

void
VTreeView::row_edit_event(VTreeView *view, RowEdit *re)
{
	// called from machine-dependent code on row(s) edited
	// re -> RowEdit with newly allocated data
	if ( (view->types[re->col] & VTT_TYPE_MASK) == VTT_TEXT
			&& re->node->data[re->col] )
		d_f_ree((void*)re->node->data[re->col]);
	re->node->data[re->col] = re->data;
	view->row_edit_event_done(re);
	if ( view->row_edit_event_handler )
		vq_insert_callback(view, view->row_edit_event_handler,
				view->user_arg_row_edit, re, sizeof(RowEdit));
}

void
VTreeView::debug_print(int depth, VTreeNode *node)
{
	if ( ring == 0 )
		return;
	if ( node == 0 )
		node = ring;
	VTreeNode* target = node;
	do {
		for ( int i = 0 ; i < depth*4 ; i++ )
			putchar(' ');
		printf("%p\n", target->key);
		if ( target->children )
			debug_print(depth+1, target->children);
		target = target->next;
	} while ( target != node );
}

bool
VTreeView::add_node(VTreeNode **_node, void *parent_key, void *next_key)
{
// must be called via msequence
	VTreeNode *parent = 0, *next = 0, *target = *_node;
	if ( parent_key ) {
		parent = get_node(parent_key);
		if ( parent == 0 )
			return false;
	}
	if ( next_key ) {
		next = get_node(next_key);
		if ( next == 0 )
			return false;
		if ( parent && next != parent && next->parent != parent )
			return false;
	}
	
	VTreeNode *node = new VTreeNode(cols, *target);
	for ( int i = 0 ; i < cols ; i++ )
		if ( (types[i] & VTT_TYPE_MASK) == VTT_TEXT )
			node->data[i] = ll_copy_str((L_CHAR*)target->data[i]);
	
	// add to ring
	if ( ring == 0 ) {
		ring = node;
		node->prev = node->next = node;
		node->parent = 0;
	}
	else {
		if ( parent == 0 ) {
			if ( next == 0 )
				next = ring;
			else
				parent = next->parent;
		}
		if ( parent && (parent->children == 0 || parent->children == next) )
			parent->children = node;
		node->parent = parent;
		
		if ( next == 0 )
			next = parent->children;
		if ( next != node ) {
			node->prev = next->prev;
			node->next = next;
			next->prev->next = node;
			next->prev = node;
		}
		else
			node->next = node->prev = node;
	}
	node->children = 0;
	
	// add to hash
	unsigned hash_key = ((unsigned)target->key) % v_tree_view_hash_size;
	node->h_prev = 0;
	node->h_next = hash[hash_key];
	if ( hash[hash_key] )
		hash[hash_key]->h_prev = node;
	hash[hash_key] = node;
	
	*_node = node;
	return true;
}

void
VTreeView::remove_node(VTreeNode *node)
{
// must be called via msequence
	// delete children
	while ( node->children )
		remove_node(node->children);
	
	// remove from ring
	node->next->prev = node->prev;
	node->prev->next = node->next;
	if ( node->parent && node->parent->children == node ) {
		if ( node->next == node )
			node->parent->children = 0;
		else
			node->parent->children = node->next;
	}
	else if ( ring == node ) {
		if ( ring->next == ring )
			ring = 0;
		else
			ring = node->next;
	}
	
	// remove from hash
	if ( node->h_prev )
		node->h_prev->h_next = node->h_next;
	else {
		unsigned hash_key = ((unsigned)node->key) % v_tree_view_hash_size;
		hash[hash_key] = node->h_next;
	}
	if ( node->h_next )
		node->h_next->h_prev = node->h_prev;
	
	// delete node
	for ( int i  = 0 ; i < cols ; i++ )
		if ( node->data[i] && 
				(types[i] & VTT_TYPE_MASK) == VTT_TEXT )
			d_f_ree((void*)node->data[i]);
	delete node;
}

VTreeNode *
VTreeView::get_node(void *key) const
{
// must be called via msequence
	unsigned hash_key = ((unsigned)key) % v_tree_view_hash_size;
	for ( VTreeNode *node = hash[hash_key] ; node ; node = node->h_next ) {
		if ( node->key == key )
			return node;
	}
	return 0;
}

bool
VTreeView::set_node(VTreeNode **_node)
{
// must be called via msequence
	VTreeNode *node = get_node((*_node)->key), *target = *_node;
	if ( node == 0 )
		return false;
	for ( int i  = 0 ; i < cols ; i++ ) {
		if ( node->data[i] != target->data[i] ) {
			if ( node->data[i] && 
					(types[i] & VTT_TYPE_MASK) == VTT_TEXT ) {
				d_f_ree((void*)node->data[i]);
				node->data[i] = ll_copy_str((L_CHAR*)target->data[i]);
			}
			else
				node->data[i] = target->data[i];
		}
	}
	node->editable = target->editable;
	*_node = node;
	return true;
}
