/*
 *
 *	object.c
 *
 *	Object support code.
 *
 *	HNMS User Interface
 *	HNMS 2.0
 *
 *	February 1994
 *
 *	Leslie Schlecht
 *	Computer Sciences Corporation
 *	Numerical Aerodynamic Simulation Systems Division
 *	NASA Ames Research Center
 *
 *	Copyright (c) 1994 Leslie Schlecht
 *
 *	This program 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 1, or (at your option)
 *	any later version.
 *
 *	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.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include	<stdio.h>

#include	"defines.h"
#include	"object.h"
#include	"externs.h"

static caddr_t	root=NULL;
static int	count = 0, varchange=0;
static caddr_t	unamevar;
static OBJECT	*current_object=NULL;
static caddr_t	parentvar[NUM_PARENT];
static int	parentclass[NUM_OBJCLASSES][NUM_PARENT] =
		{
		/* ipparent, physparent, adminparent */
		{0, 0, 0},
		{0, 0, 0},				/* Agent	 */
		{0, 0, 0},				/* Internet	 */
		{OBJ_internet, 0, 0},			/* Network	 */
		{OBJ_network, 0, 0},			/* Subnet	 */
		{OBJ_subnet, OBJ_processor, 0},		/* Ipaddress	 */
		{0, OBJ_internet, 0},			/* Site		 */
		{0, OBJ_site, OBJ_administrator},	/* Processor	 */
		{0, OBJ_processor, 0},			/* Interface	 */
		{0, OBJ_site, 0},			/* Equipment	 */
		{0, OBJ_site, 0},			/* Administrator */
		{0, 0, 0},				/* Module	 */
		};
static char	*parentode[] = {
		"hnmsObjIpParent.0",
		"hnmsObjPhysParent.0",
		"hnmsObjAdminParent.0",
		};
static int	parentcount[NUM_PARENT] = {0, 0, 0};
static caddr_t	relvbl[NUM_OBJCLASSES];

typedef		struct unknownrel {
		OBJECT		*child;
		int		rel, class;
		char		*uname;
		} UNKNOWNREL;

static caddr_t	relq=NULL;
static int	relqcount = 0;


/*
 *	Add an object to an object list by name and return the position.
 */
int
AddObjectByName(list, obj)
caddr_t	*list;
OBJECT		*obj;
	{
	if (!obj) return(0);
	if (list == NULL) list = &root;
	return(AddEntry(list, obj, CompareObjectName, 0));
	}


/*
 *	Add an object to an object list by IP address and return the position.
 */
int
AddObjectByIPAddress(list, obj)
caddr_t	*list;
OBJECT	*obj;
	{
	if (!obj) return(0);
	if (list == NULL) list = &root;
	return(AddEntry(list, obj, CompareObjectIpaddress, 0));
	}


/*
 *	Add an object to an object list by IP address and return the position.
 */
int
AddObjectByHID(list, obj)
caddr_t	*list;
OBJECT	*obj;
	{
	if (!obj) return(0);
	if (list == NULL) list = &root;
	return(AddEntry(list, obj, CompareObjectHID, 0));
	}

	

/*
 *	Add all of an object's children to a view.
 */
void
AddObjectChildToView(obj, view, class, rel)
caddr_t	view;
OBJECT	*obj;
int	class, rel;
	{
	caddr_t		oe;
	OBJECT		*o;
	
	oe = root;
	while (NextEntry(&oe, &o)) {
		if (o->class != class) continue;
		if (o->parent[rel] == obj)
			Q_AddViewChild(view, obj, o, rel);
		}
	}


/*
 *	Add an object's parent to a view.
 */
void
AddObjectParentToView(obj, view, rel)
OBJECT	*obj;
caddr_t	view;
int	rel;
	{
	if (obj->parent[rel])
		Q_AddViewParent(view, obj, obj->parent[rel], rel);
	}
	

/*
 *	Add a view to the object view list.
 */
void
AddViewToObject(obj, view)
OBJECT	*obj;
caddr_t	view;
	{
	AddEntry(&(obj->viewlist), view, NULL, 0);
	}


/*
 *	Put an announce message on the process queue.
 */
void
AnnounceObject(h, v)
int	h;
caddr_t	v;
	{
	WorkProc(MakeObject, 2, h, v, NULL, NULL);
	}


/*
 *	Announce all objects to a view.
 */
void
AnnounceObjectsToView(view)
caddr_t	view;
	{
	caddr_t	oe;
	OBJECT	*obj;

	oe = root;
	while (NextEntry(&oe, &obj))
		if (obj->hid)
			AnnounceObjectToView(view, obj);
	}


/*
 *	Compare object class and name to given class and name.
 */
int
CompareClassAndName(obj, class, name)
OBJECT	*obj;
int	class;
char	*name;
	{
	if (class == obj->class)
		if (strcmp(obj->name, name) == 0) return(0);
	return(-1);
	}
	

/*
 *	Compare object HNMS id to given HID.
 */
int
CompareHID(obj, hid, a2)
OBJECT	*obj;
int	hid;
caddr_t	a2;
	{
	if (obj->hid < hid)
		return(-1);
	else if (obj->hid == hid)
		return(0);
	else
		return(1);
	}


/*
 *	Compare two objects by HNMS id.
 */
int
CompareObjectHID(listobj, newobj)
OBJECT	*listobj, *newobj;
	{
	if (listobj->hid < newobj->hid) return(-1);
	if (listobj->hid == newobj->hid) return(0);
	return(1);
	}


/*
 *	Compare two objects by ipaddress.
 */
int
CompareObjectIpaddress(listobj, newobj)
OBJECT	*listobj, *newobj;
	{
	if (listobj->ipaddr < newobj->ipaddr) return(-1);
	if (listobj->ipaddr == newobj->ipaddr) return(0);
	return(1);
	}


/*
 *	Compare two objects by name.
 */
int
CompareObjectName(listobj, newobj)
OBJECT	*listobj, *newobj;
	{
	return(strcmp(listobj->name, newobj->name));
	}


/*
 *	Compare variables in a object variable list.
 */
int
CompareVariable(ove, var, a2)
OBJVARENTRY	*ove;
caddr_t	var;
caddr_t	a2;
	{
	if (var == ove->var) return(0);
	return(-1);
	}


/*
 *	Put an delete message on the process queue.
 */
void
DeleteObject(h)
int	h;
	{
	WorkProc(RemoveObject, 1, h, NULL, NULL, NULL);
	}


/*
 *	Destroy an object.
 */
void
DestroyObject(obj)
OBJECT	*obj;
	{
	if (!obj) return;
	RemoveObjectFromObjectList(obj);
	if (obj->def) RemoveDefaultViewPanel(NULL, obj, NULL);
	if (obj->state) RemoveObjectDump(NULL, obj, NULL);
	RemoveEntryList(obj->varlist, DestroyOBJVARENTRY);
	RemoveEntryList(obj->viewlist, NULL);
	if (obj->uname) free(obj->uname);
	free(obj);
	}


/*
 *	Free an object list.
 */
void
DestroyObjects()
	{
	RemoveEntryList(root, DestroyObject);
	RemoveEntryList(relq, DestroyUNKNOWNREL);
	root = NULL;
	relq = NULL;
	relqcount = 0;
	parentcount[0] = 0;
	parentcount[1] = 0;
	parentcount[2] = 0;
	}
	

/*
 *	Destroy object variable list entry.
 */
int
DestroyOBJVARENTRY(ove)
OBJVARENTRY	*ove;
	{
	OBJVARENTRY	*o;

	if (!ove) return;
	if (IsVarString(ove->var) && ove->value) free(ove->value);
	free(ove);
	}


/*
 *	Destroy an unknown relation entry.
 */
void
DestroyUNKNOWNREL(urel)
UNKNOWNREL	*urel;
	{
	free(urel->uname);
	free(urel);
	}


/*
 *	Find an object by class and name.
 */
int
FindObjectByCN(list, class, name, obj)
caddr_t		list;
int		class;
char		*name;
OBJECT		**obj;
	{
	int	pos;

	if (list == NULL)
		list = root;
	pos = FindEntry(list, CompareClassAndName, class, name, obj);
	return(pos);
	}
	

/*
 *	Find an object by HNMS id.
 */
int
FindObjectByHID(list, hid, obj)
caddr_t	list;
int	hid;
OBJECT	**obj;
	{
	int	pos;

	if (list == NULL)
		list = root;
	pos = FindEntry(list, CompareHID, hid, NULL, obj);
	return(pos);
	}


/*
 *	Get the current object.
 */
void
GetCurrentObject(obj)
OBJECT	**obj;
	{
	*obj = current_object;
	}


/*
 *	Get an object at a specific position in a object list.
 */
void
GetObjectPosition(list, pos, obj)
caddr_t	list;
int	*pos;
OBJECT	**obj;
	{
	if (!obj) {
		*pos = 0;
		return;
		}
	if (list == NULL) list = root;
	EntryAtPosition(list, pos, obj);
	}


/*
 *	Initialize object code.
 */
int
InitializeObjects()
	{
	register	i, j;
	int		l;
	char		*ext;
	
	CreateVariable("hnmsObjUName.0", &unamevar);
	for (i=0; i<NUM_PARENT; i++)
		CreateVariable(parentode[i], &(parentvar[i]));
	for (j=0; j<NUM_OBJCLASSES; j++) {
		relvbl[j] = NULL;
		for (i=0; i<NUM_PARENT; i++) {
			if (parentclass[j][i])
				AddVariableToVbl(&(relvbl[j]), parentvar[i], 5);
			}
		}
	return(1);
	}


/*
 *	Update an object with a message from the server.
 */
int
LoadObject(hid, vbl)
int	hid;
caddr_t	vbl;
	{
	OBJECT		*obj;
	OBJVARENTRY	*ve;
	caddr_t		oid, value, p, q, v, view;
	char		*cval=NULL;
	unsigned int	tsc, len, type;
	char		buf[BUFSIZ];

	if (!FindObjectByHID(NULL, hid, &obj)) {
		LogMessage(logbuf("Unable to find object %d.\n", hid));
		FreeVbl(vbl);
		return;
		}
	if (!vbl) {
		LogMessage(logbuf("Empty VarBindList in SendData for %s.\n",
			obj->name));
		return;
		}
	/* update each variable in the varbindlist */
	v = vbl;
	while (GetNextVariableFromVbl(v, &oid, buf, &len, &tsc, &type)) {
		v = NULL;
		/* find the variable in the object's list */
		p = obj->varlist;
		while (NextEntry(&p, &ve)) {
			if (!OidInVar(ve->var, oid)) continue;
			SetVarType(ve->var, type);
			if (IsVarString(ve->var)) {
				if (ve->value)
					free(ve->value);
				if (!len)
					ve->value = NULL;
				else
					ve->value = (caddr_t)newstr(buf);
				/*
				printf("%s %s %s\n", obj->name, VarOde(ve->var), buf);
				*/
				}
			else
				bcopy(buf, &(ve->value), sizeof(unsigned int));
			ve->tsc = tsc;
			q = obj->viewlist;
	/*
	printf("Update %s %s = ", obj->name, VarOde(ve->var));
	if (IsVarString(ve->var)) printf("%s\n", ve->value);
	else printf("%d\n", ve->value);
	*/
			/* update all views interested in the variable */
			while (NextEntry(&q, &view))
				UpdateViewObject(view, obj, ve->var, ve->value,
					ve->tsc);
			break;
			}
		}
	FreeVbl(vbl);
	}


/*
 *	Make a new network object.
 */
void
MakeObject(hid, vbl)
int	hid;
caddr_t	vbl;
	{
	OBJECT		*obj, *o;
	char		*name, *uname, *ext;
	int		class, pos;
	char		buf[BUFSIZ];

	if (!GetVariableFromVbl(vbl, unamevar, buf, NULL, NULL, NULL)) {
		FreeVbl(vbl);
		return;
		}
	FreeVbl(vbl);
	uname = newstr(buf);
	if (FindObjectByHID(NULL, hid, &o)) {
		/*
		LogMessage(logbuf("Duplicate id for %s (id %d).\n", uname,
			hid));
		*/
		free(uname);
		return;
		}
	if (!ParseUniqueName(uname, &class, &name, &ext)) {
		LogMessage(logbuf("Unable to parse unique name: %s.\n", uname));
		free(uname);
		return;
		}
	if ((pos=FindObjectByCN(NULL, class, name, &o))) {
		LogMessage(logbuf("Duplicate name for %s (id %d).\n", uname,
			hid));
		free(uname);
		return;
		}
	
	/* allocate new object */
	if ((obj=(OBJECT*)myalloc(NULL, 1, sizeof(OBJECT))) == NULL) return;
	obj->hid = hid;
	obj->uname = uname;
	obj->name = name;
	obj->ext = ext;
	obj->class = class;
	if (!AllowObject(obj)) {
		free(obj->uname);
		free(obj);
		return;
		}
			
	AddObjectByHID(NULL, obj, 0, NULL);
	AddObjectToObjectList(obj);
	SubscribeObjectRelations(obj);
	AnnounceObjectToViews(obj);
	ResolveUnknownRelations(obj);
	/*
	LogMessage(logbuf("Announcing %s (id = %d)\n", uname, hid));
	*/
	count ++;
	}


/*
 *	Merge all variables from all views interested in an object and
 *	create a varbindlist.
 */
void
MergeObjectVariables(obj, vbl)
OBJECT	*obj;
caddr_t	*vbl;
	{
	register	i;
	OBJVARENTRY	*ove;
	caddr_t		*viewvar, objvar;
	int		*interval, *recvonly, count;

	MergeViewVariables(obj->viewlist, obj->class, &viewvar, &interval,
		&recvonly, &count);
	objvar = obj->varlist;
	obj->varlist = NULL;
	varchange = 0;
	for (i=0; i<count; i++) {
		if (FindEntry(objvar, CompareVariable, viewvar[i], 0, &ove)) {
			if (ove->interval != interval[i]) {
				ove->interval = interval[i];
				varchange = 1;
				}
			else if (ove->recvonly != recvonly[i]) {
				ove->recvonly = recvonly[i];
				varchange = 1;
				}
			RemoveEntry(&objvar, ove, NULL);
			AddEntry(&(obj->varlist), ove, NULL,0);
			}
		else {
			ove = (OBJVARENTRY*)myalloc(NULL, 1,
				sizeof(OBJVARENTRY));
			ove->var = viewvar[i];
			ove->interval = interval[i];
			ove->recvonly = recvonly[i];
			if (!recvonly[i])
				varchange = 1;
			AddEntry(&(obj->varlist), ove, NULL, 0);
			}
		if (!ove->recvonly)
			AddVariableToVbl(vbl, ove->var, ove->interval);
		}
	RemoveEntryList(objvar, DestroyOBJVARENTRY);
	free(viewvar);
	free(interval);
	free(recvonly);
	}


/*
 *	Walk through the object list performing a function on each object.
 */
void
ObjectWalk(view, func, arg)
caddr_t	view;
void	(*func)();
caddr_t	arg;
	{
	caddr_t	oe;
	OBJECT	*obj;

	oe = root;
	while (NextEntry(&oe, &obj)) {
		WorkProc(func, 3, view, obj, arg, NULL);
		}
	}


/*
 *	Put a variable request on the process queue.
 */
void
Q_RequestObjectVariable(obj, view, var)
OBJECT	*obj;
caddr_t	view;
caddr_t	var;
	{
	WorkProc(RequestObjectVariable, 3, obj, view, var, NULL);
	}


/*
 *	Remove an object from the ui.
 */
void
RemoveObject(hid)
int	hid;
	{
	OBJECT	*obj;
	caddr_t	n, view;

	if (!FindObjectByHID(NULL, hid, &obj)) return;
	obj->hid = 0;
	n = obj->viewlist;
	while (NextEntry(&n, &view))
		Q_RemoveViewObject(view, obj);
	WorkProc(DestroyObject, 1, obj, NULL, NULL, NULL);
	}


/*
 *	Remove a view from an object view list.
 */
int
RemoveViewFromObject(obj, view)
OBJECT	*obj;
caddr_t	view;
	{
	return(RemoveEntry(&(obj->viewlist), view, NULL));
	}


/*
 *	Remove a view from all objects' view list.
 */
void
RemoveViewFromObjects(view)
caddr_t	view;
	{
	caddr_t	oe, n, v;
	OBJECT	*obj;

	oe = root;
	while (NextEntry(&oe, &obj)) {
		n = obj->viewlist;
		while (NextEntry(&n, &v)) {
			if (view == v) {
				RemoveEntry(&(obj->viewlist), v, NULL);
				SubscribeObjectVariables(obj);
				break;
				}
			}
		}
	}


/*
 *	Request the value of a object variable.
 */
void
RequestObjectVariable(obj, view, var)
OBJECT	*obj;
caddr_t	view;
caddr_t	var;
	{
	OBJVARENTRY	*ove;
	caddr_t		n;

	n = obj->varlist;
	while (NextEntry(&n, &ove)) {
		if (ove->var == var) {
			UpdateViewObject(view, obj, ove->var, ove->value,
					ove->tsc);
			break;
			}
		}
	}


/*
 *	Process any pending relation requests for this object.
 */
void
ResolveUnknownRelations(obj)
OBJECT	*obj;
	{
	caddr_t		n;
	UNKNOWNREL	*urel;
	
	n = relq;
	while (NextEntry(&n, &urel)) {
		if (urel->class != obj->class) continue;
		if (strcmp(urel->uname, obj->uname) != 0) continue;
		SetObjectParent(urel->child, urel->rel, obj);
		RemoveEntry(&relq, urel, DestroyUNKNOWNREL);
		relqcount --;
		}
	/*
	printf("unknown relations count %d\n", relqcount);
	*/
	}


/*
 *	Place a data message on the process queue.
 */
void
SendData(h, v)
int	h;
caddr_t	v;
	{
	WorkProc(LoadObject, 2, h, v, NULL, NULL);
	}


/*
 *	Put an relation message on the process queue.
 */
void
SendRelation(h, v)
int	h;
caddr_t	v;
	{
	WorkProc(UpdateRelation, 2, h, v, NULL, NULL);
	}


/*
 *	Set the current object.
 */
void
SetCurrentObject(obj)
OBJECT	*obj;
	{
	register	i;

	current_object = obj;
	if (!obj) {
		SetNewCurrentObject("", 0);
		UpdateCurrentObject("", "", 0);
		}
	else {
		UpdateCurrentObject(obj->name, obj_class_names[obj->class],
			obj->class);
		SetNewCurrentObject(obj->name, obj->class);
		}
	}


/*
 *	Fill object parent value and update views.
 */
void
SetObjectParent(obj, i, newparent)
OBJECT	*obj, *newparent;
int	i;
	{
	OBJECT	*oldparent;
	caddr_t	p, view;
	
	oldparent = obj->parent[i];
	if (newparent == oldparent) return;
	obj->parent[i] = newparent;
	p = obj->viewlist;
	while (NextEntry(&p, &view))
		UpdateViewParent(view, obj, newparent, i);
	p = newparent->viewlist;
	while (NextEntry(&p, &view))
		UpdateViewChild(view, newparent, obj, i);
	}


/*
 *	Invoke a default view panel.
 */
void
ShowCurrentObjectDefault()
	{
	CreateDefaultViewPanel(current_object);
	}


/*
 *	Invoke a dump panel.
 */
void
ShowCurrentObjectDump()
	{
	CreateObjectDump(current_object);
	}


/*
 *	Invoke a ping panel.
 */
void
ShowCurrentObjectPing()
	{
	PingObject(current_object);
	}


/*
 *	Invoke a telnet panel.
 */
void
ShowCurrentObjectTelnet()
	{
	TelnetObject(current_object);
	}


/*
 *	Invoke a traceroute panel.
 */
void
ShowCurrentObjectTraceroute()
	{
	TracerouteObject(current_object);
	}


/*
 *	Send subscription for object parents.
 */
void
SubscribeObjectRelations(o)
OBJECT	*o;
	{
	caddr_t	v;
	caddr_t	oid;
	int	type, len, interval, tsc;
	char	buf[BUFSIZ];

	if (relvbl[o->class]) {
		/*
		v = relvbl[o->class];
		while (GetNextVariableFromVbl(v,&oid,buf,&len,&interval,&tsc)) {
			v = NULL;
			printf("%s subscribing to %s\n", o->name,
				MIB_oid2ode(oid, &type));
			}
		*/
		SubHNMPRelations(o->hid, relvbl[o->class]);
		}
	}


/*
 *	Send a subscription for object variables.
 */
void
SubscribeObjectVariables(o)
OBJECT	*o;
	{
	caddr_t	vbl=NULL, v;
	caddr_t	oid;
	int	type, len, interval, tsc;
	char	buf[BUFSIZ];

	MergeObjectVariables(o, &vbl);
	if (!varchange) {
		FreeVbl(vbl);
		return;
		}
	if (!vbl)
		UnsubHNMPData(o->hid);
	else {
		/*
		v = vbl;
		while (GetNextVariableFromVbl(v,&oid,buf,&len,&interval,&tsc)) {
			v = NULL;
			printf("%s subscribing to %s\n", o->name,
				MIB_oid2ode(oid, &type));
			}
		*/
		SubHNMPData(o->hid, vbl);
		FreeVbl(vbl);
		}
	}


/*
 *	Update an object relation.
 */
void
UpdateRelation(hid, vbl)
int	hid;
caddr_t	vbl;
	{
	OBJECT		*obj, *newparent=NULL, *oldparent=NULL;
	char		*name, *uname, *ext;
	int		class;
	caddr_t		reloid, v;
	int		t;
	register	i;
	unsigned int	len=0;
	char		buf[BUFSIZ];
	UNKNOWNREL	*urel;

	/* find the requesting object */
	if (!FindObjectByHID(NULL, hid, &obj)) {
		LogMessage(logbuf("Can't find object with HID %d.\n", hid));
		FreeVbl(vbl);
		return;
		}
	if (!vbl) {
		LogMessage(logbuf("Empty VarBindList in SendRelation for %s.\n",
			obj->name));
		return;
		}
	
	v = vbl;
	buf[0] = '\0';
	while (t=GetNextVariableFromVbl(v, &reloid, buf, &len, NULL, NULL)) {
		v = NULL;
		if (!len) {
			LogMessage(logbuf("Zero length value.\n"));
			continue;
			}
		/* Get the name, class */
		if (!ParseUniqueName(buf, &class, &name, &ext)) {
			LogMessage(logbuf("Invalid unique name %s\n", buf));
			continue;
			}
		/* find the type of relation */
		for (i=0; i<NUM_PARENT; i++)
			if (OidInVar(parentvar[i], reloid)) {
				/*
				parentcount[i] ++;
				printf("%s %d\n", VarOde(parentvar[i]),
					parentcount[i]);
				*/
				break;
				}
		if (i == NUM_PARENT) {
			LogMessage("Unknown relation variable.\n");
			return;
			}
		/* find this object */
		if (!FindObjectByCN(NULL, class, name, &newparent)) {
			urel = (UNKNOWNREL*)myalloc(NULL, 1,sizeof(UNKNOWNREL));
			urel->child = obj;
			urel->rel = i;
			urel->uname = newstr(buf);
			urel->class = class;
			AddEntry(&relq, urel, NULL, 0);
			relqcount ++;
			}
		else
			SetObjectParent(obj, i, newparent);
		}
	FreeVbl(vbl);
	}
