/////////////////////////////////////////////////////////////////////////////
//	AI Nodelist stuff
//
//	by Nelno, 10/30/97
//
//	some things that were changed to integrate nodes:
//		Ctrl-K (connect entities) connects nodes with linkX instead of
//		target/targetname.  qe3.c, ConnectEntities.
//	cloning (copying) nodes: when a node is cloned, the new node is given a
//		number so that it can be linked.  
//	drawing nodes: because there can be a lot of nodes on a level, they can
//		be toggled on/off in all views so that the mappers aren't looking at
//		them when they don't have to.
/////////////////////////////////////////////////////////////////////////////

#include "qe3.h"
#include "mru.h"

/////////////////////////////////////////////////////////////////////////////
//	defines
/////////////////////////////////////////////////////////////////////////////

#define	MAX_NODE_LINKS	6

#define	NODE_NORMAL		0
#define	NODE_DOOR		1
#define	NODE_TELEPORTER	2
#define	NODE_ROCKETJUMP	4
#define	NODE_SWITCH		8
#define	NODE_LIFT		16

/////////////////////////////////////////////////////////////////////////////
//	typedefs
/////////////////////////////////////////////////////////////////////////////

typedef	struct	mapNode_s
{
	struct	mapNode_s	*next_node;					// used to maintain a linear list of all nodes
	
	vec3_t				origin;						// location of this node in the world

	struct	mapNode_s	*link [MAX_NODE_LINKS];		// links to other nodes reachable from current node

//	userEntity_t		*marker;					// links to other nodes reachable from current node
	int					number;
/*
	char				*target;
	char				*targetname;
*/
	//	node types
	int					node_type;
	void				*node_data;

//	nodeTime_t			*node_time;
} mapNode_t;

typedef struct	mapHeader_s
{
	struct	mapNode_s	*head;

	int		node_count;
} mapHeader_t;

/////////////////////////////////////////////////////////////////////////////
//	globals
/////////////////////////////////////////////////////////////////////////////

OPENFILENAME	ofn;						//	common dialog box structure
char			nelnoDirName[MAX_PATH];		//	directory string
char			nelnoFile [260];				//	filename string
char			nelnoFileTitle [260];			//	file title string 
char			nelnoFilter [260] = "Daikatana node file (*.nod)\0*.nod\0\0";  // filter string
char			nelnoTitle [260] = "Load Node File\0";
char			chReplace;					//	string separator for nelnoFilter
HANDLE			hf;							//	file handle

// toggle for drawing/not drawing nodes
int	node_show_nodes;
int	node_show_links;

////////////////////////////////////////////
//	node_openDialog
//
////////////////////////////////////////////

char	*node_openDialog (void)
{
/*
	if (katana_mode)
	{
		strcpy(nelnoDirName, basedir);
		strcat(nelnoDirName, "\\data\\maps\\nodes");
	}
	else
*/
	{
	    strcpy (nelnoDirName, ValueForKey (g_qeglobals.d_project_entity, "basepath") );
	    strcat (nelnoDirName, "\\maps\\nodes");
	}

	/* Place the terminating null character in the nelnoFile. */ 
 
	nelnoFile[0] = '\0'; 
 
	/* Set the members of the OPENFILENAME structure. */ 
 
	ofn.lStructSize = sizeof (OPENFILENAME); 
	ofn.hwndOwner = g_qeglobals.d_hwndMain; 
	ofn.lpstrFilter = nelnoFilter; 
	ofn.nFilterIndex = 1; 
	ofn.lpstrFile = nelnoFile; 
	ofn.nMaxFile = sizeof(nelnoFile); 
	ofn.lpstrFileTitle = nelnoFileTitle; 
	ofn.nMaxFileTitle = sizeof(nelnoFileTitle);
	ofn.lpstrInitialDir = nelnoDirName; 
	ofn.lpstrTitle = nelnoTitle; 
	ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST | 
		OFN_FILEMUSTEXIST; 

	if (!GetOpenFileName (&ofn))
		return	NULL;	// canceled
	else
		return	ofn.lpstrFile;
}

////////////////////////////////
//	node_create_header
//
//	creates a new header to a list of nodes
////////////////////////////////

mapHeader_t	*node_create_header (void)
{
	mapHeader_t	*map_header;

	map_header = (mapHeader_t *) qmalloc (sizeof (mapHeader_t));
	
	map_header->head = NULL;
	map_header->node_count = 0;

	return	map_header;
}

////////////////////////////////
//	node_create_node
//
//	mallocs up a new node at the end of the list
////////////////////////////////

mapNode_t	*node_create_node (mapHeader_t **header)
{
	mapNode_t	*new_node, *head;

	new_node = (mapNode_t *) qmalloc (sizeof (mapNode_t));
	memset (new_node, 0x00, sizeof (mapNode_t));

	if (!new_node)
		return	NULL;

	head = (*header)->head;

	if (head)
	{
		// go to the end of the list
		while (head->next_node)
			head = head->next_node;

		head->next_node = new_node;
	}
	// first node in this list
	else
		(*header)->head = new_node;

	(*header)->node_count++;
	
	new_node->number = (*header)->node_count;

	return	new_node;
}

////////////////////////////////////////////
//	node_new_entbrush
//
////////////////////////////////////////////

entity_t *node_new_entbrush (eclass_t *c, vec3_t org)
{
	brush_t			*b;
	entity_t		*e;
	vec3_t			mins, maxs;

	//	create a new entity
	e = qmalloc (sizeof(entity_t));
	e->brushes.onext = e->brushes.oprev = &e->brushes;
	e->eclass = c;
	SetKeyValue (e, "classname", c->name);

	//	add the entity to the entity list
	e->next = entities.next;
	entities.next = e;
	e->next->prev = e;
	e->prev = &entities;

	//	first create a brush of arbitrary size, in the correct location
	VectorAdd (c->mins, org, mins);
	VectorAdd (c->maxs, org, maxs);
	
//	Sys_Printf ("Creating brush at %.0f %.0f %.0f\n", org [0], org [1], org [2]);
//	Sys_Printf ("  mins: %.0f %.0f %.0f\n", mins [0], mins [1], mins [2]);
//	Sys_Printf ("  maxs: %.0f %.0f %.0f\n", maxs [0], maxs [1], maxs [2]);
		
	b = Brush_Create (mins, maxs, &c->texdef);
	b->owner = world_entity;

	//	set the entity's origin
	VectorCopy (org, e->origin);

	//	associate entity with brush
	Entity_LinkBrush (e, b);

	//	add the brush to the selected brushes list
	Brush_AddToList (b, &active_brushes);

	//	make sure brush gets drawn
	Brush_Build (b);

	return	e;
}

////////////////////////////////////////////
//	node_create_entities
//
////////////////////////////////////////////

void	node_create_entities (mapHeader_t *node_header)
{
	mapNode_t		*node;
	entity_t		*e;
	eclass_t		*c;
	vec3_t			mins, maxs;
	char			value [128];
	char			key [128];
	int				i;
	char			sf;

	// unselect everything first
	Select_Deselect ();

	// get the entity class for nodes
	c = Eclass_ForName ("node_node", false);

	for (node = node_header->head; node; node = node->next_node)
	{
		e = node_new_entbrush (c, node->origin);

		//	set this node's number
		sprintf (value, "%i", node->number);
		SetKeyValue (e, "number", value);

		sf = 0;
		//	set this nodes's type, if any
		if (node->node_type & NODE_DOOR)
			sf |= NODE_DOOR;

		sprintf (value, "%i", sf);
		SetKeyValue (e, "spawnflags", value);

		for (i = 0; i < MAX_NODE_LINKS; i++)
		{
			if (node->link [i])
			{
				sprintf (key, "link%i", i);
				sprintf (value, "%i", (int)node->link [i]);
				SetKeyValue (e, key, value);
			}
		}
	}

	UpdateWindow (g_qeglobals.d_hwndEntity);
}

////////////////////////////////////////////
//	node_readlist
//
////////////////////////////////////////////

void	node_readlist (char *filename)
{
	FILE			*f;
	int				i, j, number;
	mapHeader_t		*node_header;
	mapNode_t		*node, *next;
	char			s [128];

	if (!(f = fopen (filename, "rb")))
	{
		MessageBox (g_qeglobals.d_hwndMain, "Could not open file", "You Sucked It Down", MB_OK);
		return;
	}

	// create the new list
	node_header = node_create_header ();

	// read in the entire list of nodes
	while (!feof (f))
	{
		if (!fread (&number, sizeof (int), 1, f))
			break;

		node = node_create_node (&node_header);
		//	force node number
		node->number = number;

		fread (node->origin, sizeof (vec3_t), 1, f);

		//	read type
		fread (&node->node_type, sizeof (int), 1, f);
		
		//	read links
		fread (node->link, sizeof (int), MAX_NODE_LINKS, f);
	}

	fclose (f);

	// now go through and set up the entities for KED
	
	node_create_entities (node_header);

	/////////////////////////////////////////////
	// remove the node list
	/////////////////////////////////////////////

	node = node_header->head;
	while (node)
	{
		next = node->next_node;
		free (node);
		node = next;
	}

	i = node_header->node_count;
	free (node_header);

	sprintf (s, "Read %i nodes from file.", i);
	MessageBox (g_qeglobals.d_hwndMain, s, "Loaded Node File", MB_OK);
}

////////////////////////////////////////////
//	node_remove_nodes
//
////////////////////////////////////////////

int	node_remove_nodes (void)
{
	brush_t	*b, *next;

	if (MessageBox (g_qeglobals.d_hwndMain, "All current node entities will be lost.\nContinue anyway?", "You Shall Suck It Down", MB_OKCANCEL) == IDCANCEL)
		return	FALSE;

	///////////////////////////////////////////////////
	//	get rid of all entities/brushes that are node_nodes
	///////////////////////////////////////////////////

	for (b = active_brushes.next; b != &active_brushes; b = next)
	{
		next = b->next;

		if (b->owner)
		{
			if (!stricmp (b->owner->eclass->name, "node_node"))
				Entity_Free (b->owner);
		}
	}

	for (b = selected_brushes.next; b != &selected_brushes; b = next)
	{
		next = b->next;

		if (b->owner)
		{
			if (!stricmp (b->owner->eclass->name, "node_node"))
				Entity_Free (b->owner);
		}
	}
	
	Sys_UpdateWindows (W_ALL);

	return	TRUE;
}

////////////////////////////////////////////
//	node_load
//
////////////////////////////////////////////

void	node_load (void)
{
	char	*filename;

	if (!node_remove_nodes ())
		return;

	if (!(filename = node_openDialog ()))
		return;

	node_readlist (filename);

	Sys_UpdateWindows (W_ALL);
}

/////////////////////////////////
//	node_draw_links
//
//	builds a display list of lines for all node links
//
/////////////////////////////////

void	node_draw_links (void)
{
	entity_t	*cur_ent, *linked_ent;
	eclass_t	*c;
	brush_t		*b;

	char		*value, *num;
	char		key [128];
	int			i, j;

	vec3_t		org, end;

//	if (!node_show_links)
	if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_LINKS)
		return;

	///////////////////////////////////////////////////
	//	Set up gl for node lines
	///////////////////////////////////////////////////

	c = Eclass_ForName ("node_node", false);
	glColor3f (c->color[0], c->color[1], c->color[2]);

	glBegin(GL_LINES);

	///////////////////////////////////////////////////
	//	Prepare thyself for one hell of a nasty loop
	///////////////////////////////////////////////////

	//	loop through entire list of entities
	for (cur_ent = entities.next; cur_ent != &entities; cur_ent = cur_ent->next)
	{
		if (!cur_ent->eclass)
			continue;
		if (!cur_ent->eclass->name)
			continue;

		//	if this is a node_node, then draw all links for it
		if (!stricmp (cur_ent->eclass->name, "node_node"))
		{
			b = cur_ent->brushes.onext;
			
			for (i=0 ; i<3 ; i++)
				org [i] = (b->mins[i] + b->maxs[i]) * 0.5; 

			//	go through each link
			for (i = 0; i < MAX_NODE_LINKS; i++)
			{
				sprintf (key, "link%i", i);
				value = ValueForKey (cur_ent, key);
				
				//	is there a link i for this node?
				//	if so, value will not be ""
				if (stricmp (value, ""))
				{
					//	loop through all entities to match target
					for (linked_ent = entities.next; linked_ent != &entities; linked_ent = linked_ent->next)
					{
						if (!linked_ent->eclass)
							continue;
						if (!linked_ent->eclass->name)
							continue;

						num = ValueForKey (linked_ent, "number");

						//	if this ent has the same "number" as link i's "value"
						//	then draw a line from cur_ent to linked ent
						if (!stricmp (value, num))
						{
							b = linked_ent->brushes.onext;

							for (j = 0 ; j < 3 ; j++)
								end [j] = (b->mins [j] + b->maxs [j]) * 0.5; 

							glVertex3fv (org);
							glVertex3fv (end);
						}
					}
				}
			}
		}
	}

	glEnd ();
}

////////////////////////////////////
//	node_count_nodes
////////////////////////////////////

int	node_count_nodes (void)
{
	entity_t	*e;
	int			count;

	for (count = 0, e = entities.next; e != NULL && e != &entities; e = e->next)
	{
		if (!strcmp (e->eclass->name, "node_node"))
			count++;
	}

	return	count;
}

////////////////////////////////////
//	node_reorder_links
////////////////////////////////////

void	node_reorder_links (void)
{
	char		link_str [64], new_num_str [64], val [64];
	int			n, link;
	int			links [MAX_NODE_LINKS];
	entity_t	*e;
	//	now reorder links so that there are no linkX epairs skipped

	for (e = entities.next; e != NULL && e != &entities; e = e->next)
	{
		//	go through all links saving link numbers and
		//	removing link keys
		for (n = 0, link = 0; n < MAX_NODE_LINKS; n++)
		{
			sprintf (link_str, "link%i", n);
			
			strcpy (val, ValueForKey (e, link_str));
			links [n] = -1;

			if (stricmp (val, ""))
			{
				//	save link
				links [link] = atoi (val);
				link++;

				Sys_Printf ("Removing key %s\n", link_str);
				//	remove key
				DeleteKey (e, link_str);
			}
		}

		//	add back links
		for (n = 0; n < MAX_NODE_LINKS; n++)
		{
			if (links [n] != -1)
			{
				sprintf (link_str, "link%i", n);
				sprintf (new_num_str, "%i", links [n]);

				SetKeyValue (e, link_str, new_num_str);
			}
		}
	}
}

////////////////////////////////////
//	node_find_link
////////////////////////////////////

int	node_find_link (entity_t *node, int link)
{
	char	link_str [64], num_str [64];
	int		i, n;

	for (i = 0; i < MAX_NODE_LINKS; i++)
	{
		sprintf (link_str, "link%i", i);

		strcpy (num_str, ValueForKey (node, link_str));
		if (stricmp (num_str, ""))
		{
			n = atoi (num_str);
			if (n == link)
				return	i;
		}
	}

	return	-1;
}

////////////////////////////////////
//	node_connect_nodes
////////////////////////////////////

void	node_connect_nodes (entity_t *e1, entity_t *e2)
{
	int		link1, link2, num1, num2;
	char	l1 [64], l2 [64];
	char	*n1, *n2;
	int		link;
	int		linked = FALSE;

	//	get node number of e1
	n1 = ValueForKey (e1, "number");
	num1 = atoi (n1);
	//	get node number of e2
	n2 = ValueForKey (e2, "number");
	num2 = atoi (n2);

	/////////////////////////////////////////////////////
	//	if nodes are already linked to one another
	//	then remove the links
	/////////////////////////////////////////////////////

	link = node_find_link (e1, num2);
	if (link >= 0)
	{
		sprintf (l1, "link%i", link);
		Sys_Printf ("Deleting key %s from node #%i\n", l1, num1);
		DeleteKey (e1, l1);
		linked = TRUE;
	}
	link = node_find_link (e2, num1);
	if (link >= 0)
	{
		sprintf (l2, "link%i", link);
		Sys_Printf ("Deleting key %s from node #%i\n", l2, num2);
		DeleteKey (e2, l2);
		linked = TRUE;
	}

	if (linked)
	{
		node_reorder_links ();

		Sys_UpdateWindows (W_XY | W_CAMERA);

		Select_Deselect();
		Select_Brush (g_qeglobals.d_select_order [1]);

		return;
	}

	/////////////////////////////////////////////////////
	//	link the brushes together
	/////////////////////////////////////////////////////

	//	find first empty link # in e1
	link1 = 0; 
	while (1)
	{
		sprintf (l1, "link%i", link1);
		if (!stricmp (ValueForKey (e1, l1), ""))
			break;
		link1++;
	}

	//	find first empty link # in e2
	link2 = 0; 
	while (1)
	{
		sprintf (l2, "link%i", link2);
		if (!stricmp (ValueForKey (e2, l2), ""))
			break;
		link2++;
	}
	
	//	link them
	SetKeyValue (e1, l1, n2);
	SetKeyValue (e2, l2, n1);

	Sys_UpdateWindows (W_XY | W_CAMERA);

	Select_Deselect();
	Select_Brush (g_qeglobals.d_select_order[1]);
}

////////////////////////////////////
//	node_number_node_entity
////////////////////////////////////

void	node_number_node_entity (entity_t *node)
{
	int		node_count;
	char	num_str [64];

	node_count = node_count_nodes ();

	sprintf (num_str, "%i", node_count);
	SetKeyValue (node, "number", num_str);

	UpdateWindow (g_qeglobals.d_hwndEntity);
}

////////////////////////////////////
//	node_delete_node
////////////////////////////////////

void	node_delete_node (entity_t *node)
{
	int			cur_num, n, link, old_num, new_num;
	char		*cur_num_str;
	char		str [64];
	char		link_str [64];
	char		old_num_str [64], new_num_str [64];
	entity_t	*e;
	
	cur_num_str = ValueForKey (node, "number");
	cur_num = atoi (cur_num_str);
	Sys_Printf ("cur_num = %i\n", cur_num);

	//	loop through all entities and remove any links
	//	to the deleted node
	for (e = entities.next; e != NULL && e != &entities; e = e->next)
	{
		if (!stricmp (e->eclass->name, "node_node"))
		{
			//	go through all links
			link = 0;
			while (1)
			{
				sprintf (link_str, "link%i", link);
				if (!stricmp (ValueForKey (e, link_str), ""))
					break;
				if (!stricmp (ValueForKey (e, link_str), cur_num_str))
					DeleteKey (e, link_str);
				link++;
			}
		}
	}

//	Entity_Free (node);

	//	now go through all nodes, subtracting one from the
	//	number of any nodes whose number is > cur_num
	//	and then relinking any nodes that point to the old number
	for (e = entities.next; e != NULL && e != &entities; e = e->next)
	{
		if (!stricmp (e->eclass->name, "node_node"))
		{
			strcpy (old_num_str, ValueForKey (e, "number"));
			old_num = atoi (old_num_str);

			if (old_num > cur_num)
			{
				new_num = old_num - 1;
				sprintf (new_num_str, "%i", new_num);

				Sys_Printf ("changing %i to %i\n", old_num, new_num);

				SetKeyValue (e, "number", new_num_str);
			}
		}
	}

	//	now subtract 1 from all links that are > cur_num
	for (e = entities.next; e != NULL && e != &entities; e = e->next)
	{
		//	go through all links
		for (link = 0; link < MAX_NODE_LINKS; link++)
		{
			sprintf (link_str, "link%i", link);
			
			strcpy (old_num_str, ValueForKey (e, link_str));
			old_num = atoi (old_num_str);

			if (old_num > cur_num)
			{
				new_num = old_num - 1;
				sprintf (new_num_str, "%i", new_num);
				
				Sys_Printf ("Changing %s %i to %i\n", link_str, old_num, new_num);
				SetKeyValue (e, link_str, new_num_str);
			}
		}
	}

	node_reorder_links ();


	modified = 1;
}

////////////////////////////////////
//	node_remove_links
////////////////////////////////////

void	node_remove_links (entity_t *e)
{
	int		i;
	char	link_str [64];
	
	for (i = 0; i < MAX_NODE_LINKS; i++)
	{
		sprintf (link_str, "link%i", i);

		DeleteKey (e, link_str);
	}

	UpdateWindow (g_qeglobals.d_hwndEntity);
}

////////////////////////////////////
//	node_init
////////////////////////////////////

void	node_init (void)
{
	node_show_links = TRUE;
	node_show_nodes = TRUE;
}