/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmslib/obj.c
 *
 *	Code to deal with HNMS objects.
 *
 *	Jude George
 *	NAS Facility, NASA Ames Research Center
 *
 *	Copyright (c) 1994 Jude George
 *
 *	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 <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdarg.h>

#include "stdhnms.h"

/*\
 *  Internal HNMS object data structure.  Contains caches of frequently
 *  read variables, as well as the true VarBindList that describes
 *  the object.
\*/
static struct hnms_object {
    VarBindList			vbl;
    int				agent_id;
    char			uname[STRBUFLEN];
};
typedef struct hnms_object	*OBJ;

static OBJ			*objects = NULL;
static int			highest_hnms_id = 0;

/*\
 *  Given the class and a unique NULL-terminated ascii string describing
 *  an HNMS object (unique within each class, return the hnmsObjUName
 *  ascii string that uniquely identifies that object within all of HNMS.
 *  The string is static.
\*/
char *OBJ_make_uname(class, ustring)
    const int		class;
    const char		*ustring;
{
    static char		uname[STRBUFLEN];

    if (!ustring)
	return NULL;
    if (ustring[0] == 0)
	return NULL;

    bzero(uname, STRBUFLEN);
    sprintf(uname, "%s:%s", obj_class_names[class], ustring);
    return uname;
}

/*\
 *  Allocate an HNMS id for a new object.  FOR SERVER ONLY.
 *  Return the id if successful, -1 if not.
\*/
static int OBJ_allocate_id()
{
    if (highest_hnms_id == MAX_OBJECTS)
	return -1;
    else
	return ++highest_hnms_id;
}

/*\
 *  Add the given HNMS object to memory using the specified HNMS id.
 *
 *  FOR SERVER ONLY:
 *  The server module may call this function with an HNMS id of 0
 *  to try and create the object.
 *
 *  If the object is added, return the HNMS id.
 *  If the object already exists, return the negative of the HNMS id.
 *  If there is an error, return 0.
\*/
int OBJ_add(id, class, ustring)
    const int		id;
    const int		class;
    const char		*ustring;
{
    int			i;
    char		*cp;
    char		*uname;
    OBJ			obj;
    int			my_module_type;
    int			current_time;

    /*
     * Initialize the objects array if this is the first time I am
     * calling this function.
     */
    if (!objects)
	objects = HNMS_alloc(MAX_OBJECTS + 1, sizeof (void *));

    /*
     * If this id is already taken, return an error.
     */
    if (id)
	if (OBJ_exists(id))
	    return 0 - id;

    /*
     * If an object with this uname already exists, return the object.
     */
    uname = OBJ_make_uname(class, ustring);
    for (i = 1; i <= highest_hnms_id; i++)
	if (OBJ_exists(i)) {
	    cp = OBJ_get_uname(i);
	    if (!strcmp(cp, uname))
		return 0 - i;
	}

    my_module_type = PARAM_get_int(oid_hnmsModuleType);

    if (id == 0) {
	if (my_module_type != MODULE_SERVER)
	    return 0;
	i = OBJ_allocate_id();
	if (i == -1)
	    return 0;
    }
    else
	i = id;

    obj = HNMS_alloc(1, sizeof **objects);
    
    current_time = get_int_time();
    strcpy(obj->uname, uname);
    VarBindList_set(&(obj->vbl), oid_hnmsObjUName, uname, strlen(uname),
		    MIB_displaystring, 0, &current_time);
    VarBindList_set(&(obj->vbl), oid_hnmsObjClass, &class, sizeof(int),
		    MIB_integer, 0, &current_time);
    VarBindList_set(&(obj->vbl), oid_hnmsObjUString, ustring, strlen(ustring),
		    MIB_displaystring, 0, &current_time);
    objects[i] = obj;

    if (i > highest_hnms_id)
	highest_hnms_id = i;

    if (my_module_type == MODULE_SERVER)
	PEER_announce_one_to_all(i);

    return i;
}

/*\
 *  Like OBJ_add, except pass in a uname instead of class/ustring.
\*/
int OBJ_add_by_uname(id, uname)
    const int		id;
    const char		*uname;
{
    int			rval;
    int			i;
    static char		class_str[STRBUFLEN];
    static char		ustring[STRBUFLEN];
    int			subclass;

    subclass = 0;
    rval = sscanf(uname, "%[^:]:%[^\n]", class_str, ustring);
    if (rval !=2)
	return 0;
    for (i = 1; i <= NUM_CLASSES; i++)
	if (!strcmp(obj_class_names[i], class_str))
	    break;
    if (i > NUM_CLASSES)
	return 0;
    else
	return OBJ_add(id, i, ustring);
}

/*\
 *  Delete an HNMS object from memory.
\*/
int OBJ_delete(id)
    const int		id;
{
    if (OBJ_exists(id)) {
	VarBindList_free(objects[id]->vbl);
	free(objects[id]);
	objects[id] = NULL;
    }
    return 0;
}

/*\
 *  Test if a given object id is used.  Return 1 if it does, 0 if not.
\*/
int OBJ_exists(id)
    const int		id;
{
    if (objects)
	if (objects[id])
	    return 1;
    return 0;
}

/*\
 *  Find an HNMS object in memory, given it's hnmsObjUName.  Returns the
 *  id of the object, or 0 if the object does not exist.  CPU intensive.
\*/
int OBJ_find(uname)
    const char		*uname;
{
    int			i;
    char		*cp;

    if (uname == NULL)
	return 0;
    else if (uname[0] == 0)
	return 0;

    for (i = 1; i <= highest_hnms_id; i++) {
	cp = OBJ_get_uname(i);
	if (cp)
	    if (!strcmp(uname, cp))
		return i;
    }
    return 0;
}

/*\
 *  Find an HNMS object in memory that matches the variable bindings
 *  given in the VarBindList.  Returns the first match.  CPU intensive.
\*/
int OBJ_find_by_vbl(vbl)
    const VarBindList	vbl;
{
    int			i;
    VarBindList		v;
    PE			pe;

    if (!vbl)
	return 0;

    for (i = 1; i <= highest_hnms_id; i++)
	if (OBJ_exists(i)) {
	    for (v = vbl; v; v = v->next) {
		pe = OBJ_get(i, v->VarBind->name, 0, 0, 0, 0, 0);
		if (!pe)
		    break;
		if (pe_cmp(pe, v->VarBind->value) != 0)
		    break;
	    }
	    if (!v)
		return i;
	}
    return 0;
}

/*\
 *  Get the cached value of the object's UName.  It's set when the
 *  object is created.
\*/
char *OBJ_get_uname(id)
    int			id;
{
    if (!OBJ_exists(id))
	return NULL;
    else
	return objects[id]->uname;
}

/*\
 *  Get the cached value of the object's Agent's HNMS id.
 *  If it isn't already set, set it.
\*/
int OBJ_get_agent_id(id)
    int			id;
{
    static char		uname[STRBUFLEN];

    if (!OBJ_exists(id))
	return 0;
    if (objects[id]->agent_id == 0) {
	bzero(uname, STRBUFLEN);
	OBJ_get(id, oid_hnmsObjAgent, uname, 0, 0, 0, 0);
	if (uname[0] == 0)
	    return 0;
	objects[id]->agent_id = OBJ_find(uname);
    }
    return objects[id]->agent_id;
}

/*\
 *  Return the VarBindList of the object.  It cannot be modified.
 *  If you wish to set or unset variables in the object's VarBindList,
 *  use OBJ_set() or OBJ_unset().
\*/
const VarBindList OBJ_get_vbl(id)
    const int		id;
{
    if (OBJ_exists(id))
	return objects[id]->vbl;
    else
	return NULL;
}

/*\
 *  Get the value of a variable in an object's VarBindList.
 *  Calling conventions are similar to VarBindList_get.
\*/
const PE OBJ_get(id, oid, value, len, type, interval, ts)
    const int		id;
    const OID		oid;
    caddr_t		value;
    unsigned int	*len;
    unsigned int	*type;
    unsigned int	*interval;
    unsigned int	*ts;
{
    if (!OBJ_exists(id))
	return NULL;

    return VarBindList_get(objects[id]->vbl, oid, value, len, type,
			   interval, ts);
}

/*\
 *  Set the value of a variable in an object's VarBindList.
 *  Pass in the value as an (int *) or a (char *).
\*/
const PE OBJ_set(id, oid, value, len, type, interval, ts)
    const int		id;
    const OID		oid;
    const caddr_t	value;
    const unsigned int	len;
    const unsigned int	type;
    unsigned int	*interval;
    unsigned int	*ts;
{
    if (!OBJ_exists(id))
	return NULL;

    return VarBindList_set(&(objects[id]->vbl), oid, value, len, type,
			   interval, ts);
}

/*\
 *  Set all the vars from a VarBindList in an object's VarBindList.
 *  Preserve timestamps.
\*/
int OBJ_set_list(id, vbl)
    const int		id;
    const VarBindList	vbl;
{
    VarBindList		v;

    for (v = vbl; v; v = v->next)
	OBJ_set(id, v->VarBind->name, v->VarBind->value, 0, 0,
		v->VarBind->interval ?
		&v->VarBind->interval : 0,
		v->VarBind->timestamp->parm ?
		&v->VarBind->timestamp->parm : 0);
    return 0;
}

/*\
 *  Set all the HNMS vars from a VarBindList in an object's VarBindList.
 *  Preserve timestamps.
\*/
int OBJ_set_hnmsvars(id, vbl)
    const int		id;
    const VarBindList	vbl;
{
    VarBindList		v;

    for (v = vbl; v; v = v->next)
	if (oid_is_hnms(v->VarBind->name))
	    OBJ_set(id, v->VarBind->name, v->VarBind->value, 0, 0,
		    v->VarBind->interval ?
		    &v->VarBind->interval : 0,
		    v->VarBind->timestamp->parm ?
		    &v->VarBind->timestamp->parm : 0);
    return 0;
}

/*\
 *  Clear the value of a variable in an object's VarBindList.
\*/
int OBJ_unset(id, oid)
    const int		id;
    const OID		oid;
{
    if (!OBJ_exists(id))
	return -1;

    return VarBindList_unset(&(objects[id]->vbl), oid);
}

/*\
 *  Iterate a function over all known objects.  The function should
 *  take an HNMS id as its first parameter.  If magic exists, it will
 *  be passed to the function as the second parameter.  The function
 *  will be expected to know what to do with it.
\*/
void OBJ_iterate(func, magic)
    void		(*func)();
    caddr_t		magic;
{
    int			i;

    for (i = 1; i <= highest_hnms_id; i++)
	if (OBJ_exists(i))
	    if (magic)
		(*func)(i, magic);
	    else
		(*func)(i);
}

/*\
 *  Print one object.
\*/
void OBJ_print(id)
    const int		id;
{
    if (OBJ_exists(id))
	VarBindList_print(objects[id]->vbl);
}

/*\
 *  For debugging.  List out all known HNMS objects.
\*/
void OBJ_print_all()
{
    int			i;
    int			status;
    char		*cp;

    for (i = 1; i <= highest_hnms_id; i++)
	if (OBJ_exists(i)) {
	    cp = OBJ_get_uname(i);
	    status = 0;
	    OBJ_get(i, oid_hnmsObjReachStatus, &status, 0, 0, 0, 0);
	    if (cp)
		printf("%d: %s (%d)\n", i, cp, status);
	}

}
