/*
 *			GPAC - MPEG-4 Systems C Development Kit
 *
 *			Copyright (c) Jean Le Feuvre 2000-2005
 *					All rights reserved
 *
 *  This file is part of GPAC / 3D rendering plugin
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  GPAC 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.  See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *
 */

#include "render3d_nodes.h"

Chain *Bindable_GetStack(SFNode *bindable)
{
	void *st;
	if (!bindable) return 0;
	st = Node_GetPrivate(bindable);
	switch (Node_GetTag(bindable)) {
	case TAG_MPEG4_Background2D: return ((Background2DStack*)st)->reg_stacks;
	case TAG_MPEG4_Background: case TAG_X3D_Background: return ((BackgroundStack*)st)->reg_stacks;
	case TAG_MPEG4_Viewport: return ((ViewStack*)st)->reg_stacks;
	case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: 
	case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: 
	case TAG_MPEG4_Fog: case TAG_X3D_Fog:
		return ((ViewStack*)st)->reg_stacks;
	default: return 0;
	}
}

Bool Bindable_GetIsBound(SFNode *bindable)
{
	if (!bindable) return 0;
	switch (Node_GetTag(bindable)) {
	case TAG_MPEG4_Background2D: return ((M_Background2D*)bindable)->isBound;
	case TAG_MPEG4_Viewport: return ((M_Viewport*)bindable)->isBound;
	case TAG_MPEG4_Background: case TAG_X3D_Background: return ((M_Background*)bindable)->isBound;
	case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: return ((M_NavigationInfo*)bindable)->isBound;
	case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: return ((M_Viewpoint*)bindable)->isBound;
	case TAG_MPEG4_Fog: case TAG_X3D_Fog: return ((M_Fog*)bindable)->isBound;
	default: return 0;
	}
}

void Bindable_SetIsBound(SFNode *bindable, Bool val)
{
	Bool has_bind_time = 0;
	if (!bindable) return;
	switch (Node_GetTag(bindable)) {
	case TAG_MPEG4_Background2D: 
		((M_Background2D*)bindable)->isBound = val;
		break;
	case TAG_MPEG4_Viewport:
		((M_Viewport*)bindable)->isBound = val;
		((M_Viewport*)bindable)->bindTime = Node_GetSceneTime(bindable);
		has_bind_time = 1;
		break;
	case TAG_X3D_Background:
		((X_Background*)bindable)->bindTime = Node_GetSceneTime(bindable);
		has_bind_time = 1;
	case TAG_MPEG4_Background:
		((M_Background*)bindable)->isBound = val;
		break;
	case TAG_X3D_NavigationInfo: 
		((X_NavigationInfo*)bindable)->bindTime = Node_GetSceneTime(bindable);
		has_bind_time = 1;
	case TAG_MPEG4_NavigationInfo: 
		((M_NavigationInfo*)bindable)->isBound = val;
		break;
	case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: 
		((M_Viewpoint*)bindable)->isBound = val;
		((M_Viewpoint*)bindable)->bindTime = Node_GetSceneTime(bindable);
		has_bind_time = 1;
		break;
	case TAG_X3D_Fog: 
		((X_Fog*)bindable)->bindTime = Node_GetSceneTime(bindable);
		has_bind_time = 1;
	case TAG_MPEG4_Fog: 
		((M_Fog*)bindable)->isBound = val;
		break;
	default: return;
	}
	Node_OnEventOutSTR(bindable, "isBound");
	if (has_bind_time) Node_OnEventOutSTR(bindable, "bindTime");
}


Bool Bindable_GetSetBind(SFNode *bindable)
{
	if (!bindable) return 0;
	switch (Node_GetTag(bindable)) {
	case TAG_MPEG4_Background2D: return ((M_Background2D*)bindable)->set_bind;
	case TAG_MPEG4_Viewport: return ((M_Viewport*)bindable)->set_bind;
	case TAG_MPEG4_Background: case TAG_X3D_Background: return ((M_Background*)bindable)->set_bind;
	case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: return ((M_NavigationInfo*)bindable)->set_bind;
	case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: return ((M_Viewpoint*)bindable)->set_bind;
	case TAG_MPEG4_Fog: case TAG_X3D_Fog: return ((M_Fog*)bindable)->set_bind;
	default: return 0;
	}
}

void Bindable_SetSetBind(SFNode *bindable, Bool val)
{
	if (!bindable) return;
	switch (Node_GetTag(bindable)) {
	case TAG_MPEG4_Background2D: 
		((M_Background2D*)bindable)->set_bind = val;
		((M_Background2D*)bindable)->on_set_bind(bindable);
		break;
	case TAG_MPEG4_Viewport: 
		((M_Viewport*)bindable)->set_bind = val;
		((M_Viewport*)bindable)->on_set_bind(bindable);
		break;
	case TAG_MPEG4_Background: case TAG_X3D_Background:
		((M_Background*)bindable)->set_bind = val;
		((M_Background*)bindable)->on_set_bind(bindable);
		break;
	case TAG_MPEG4_NavigationInfo: case TAG_X3D_NavigationInfo: 
		((M_NavigationInfo*)bindable)->set_bind = val;
		((M_NavigationInfo*)bindable)->on_set_bind(bindable);
		break;
	case TAG_MPEG4_Viewpoint: case TAG_X3D_Viewpoint: 
		((M_Viewpoint*)bindable)->set_bind = val;
		((M_Viewpoint*)bindable)->on_set_bind(bindable);
		break;
	case TAG_MPEG4_Fog: case TAG_X3D_Fog: 
		((M_Fog*)bindable)->set_bind = val;
		((M_Fog*)bindable)->on_set_bind(bindable);
		break;
	default: return;
	}
}


void Bindable_OnSetBind(SFNode *bindable, Chain *stack_list)
{
	u32 i;
	Bool on_top, is_bound, set_bind;
	SFNode *node;

	set_bind = Bindable_GetSetBind(bindable);
	is_bound = Bindable_GetIsBound(bindable);

	for (i=0; i<ChainGetCount(stack_list); i++) {
		Chain *stack = ChainGetEntry(stack_list, i);
		on_top = (ChainGetEntry(stack, 0)==bindable) ? 1 : 0;

		if (!set_bind) {
			if (is_bound) Bindable_SetIsBound(bindable, 0);

			if (on_top && (ChainGetCount(stack)>1)) {
				ChainDeleteEntry(stack, 0);
				ChainAddEntry(stack, bindable);
				node = ChainGetEntry(stack, 0);
				Bindable_SetSetBind(node, 1);
			}
		} else {
			if (!is_bound) Bindable_SetIsBound(bindable, 1);
			if (!on_top) {
				node = ChainGetEntry(stack, 0);
				ChainDeleteItem(stack, bindable);
				ChainInsertEntry(stack, bindable, 0);
				Bindable_SetSetBind(node, 0);
			}
		}
	}
}

void BindableStackDelete(Chain *stack)
{
	while (ChainGetCount(stack)) {
		Chain *bind_stack_list;
		SFNode *bindable = ChainGetEntry(stack, 0);
		ChainDeleteEntry(stack, 0);
		bind_stack_list = Bindable_GetStack(bindable);
		if (bind_stack_list) {
			ChainDeleteItem(bind_stack_list, stack);
			assert(ChainFindEntry(bind_stack_list, stack)<0);
		}
	}
	DeleteChain(stack);
}


void PreDestroyBindable(SFNode *bindable, Chain *stack_list)
{
	Bool is_bound = Bindable_GetIsBound(bindable);
	Bindable_SetIsBound(bindable, 0);

	while (ChainGetCount(stack_list)) {
		SFNode *stack_top;
		Chain *stack = ChainGetEntry(stack_list, 0);
		ChainDeleteEntry(stack_list, 0);
		ChainDeleteItem(stack, bindable);
		if (is_bound) {
			stack_top = ChainGetEntry(stack, 0);
			if (stack_top) Bindable_SetSetBind(stack_top, 1);
		}
	}
}

