#include <ansidef.h>
#undef	CONST
#define CONST
#include "sr-oid.h"
#include "sr-util.h"
#include "sr-logger.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <malloc.h>

/* Internals */

int		OID_debug	= 0;	/* Let OID_Oid2name return oid[1], *
					 * warn about OID_Oid2str(*,!=1)   *
					 * and about unregistered Oids.    */

typedef struct oid_tbl_elem {
   Oid		ref;			/*  The (unique) registered OID    */
   int		proto;			/* registered OID number -- 0 to 2 */
   const char	*str;
} oid_tbl_elem;

static struct Oid_Tbl {
   oid_tbl_elem	*tbl;
   unsigned	size, alloc;
} reg = { NULL, 0, 0 };

static oid_tbl_elem unreg_Oid = { NULL, 0, NULL };

static oid_tbl_elem *OID_find (const char *key, int insert)
{
   if (key) {
      unsigned	i;
      for (i = 0;  i < reg.size;  i++)
	 if (strcasecmp (key, reg.tbl[i].str) == 0)
	    return &reg.tbl[i];
      if (insert) {
	 /* Register the OID */
	 char		*oname	= strdup ( key );
	 Oid		o;
	 OID_contents(o) = (Oid_element *) malloc (sizeof (Oid_element));

	 if (reg.size == reg.alloc) {
	    reg.alloc += reg.alloc + 30;
	    reg.tbl = (oid_tbl_elem *)
	       (reg.tbl
		? realloc ((char *)reg.tbl, reg.alloc * sizeof (*reg.tbl))
		: malloc  (                 reg.alloc * sizeof (*reg.tbl)));
	 }
      
	 if (oname[*oname == '0']) {
	    LOG (facUtil, llevNotice, "Unregistered Oid: %s\n", oname);
	    if (OID_debug) fprintf (stderr, "Unregistered Oid: %s\n", oname);
	 }
	 
	 reg.tbl[reg.size].ref = o;
	 reg.tbl[reg.size].str =
	    OID_contents(o)->str[0] =
	    OID_contents(o)->str[1] =
	    OID_contents(o)->str[2] = oname;
	 reg.tbl[reg.size].proto = 0;
	 return &reg.tbl[reg.size++];
      }
   }
   return &unreg_Oid;
}


/* Definitions of externals */

#undef OID_Oid2str		/* in case these were defined as macros */
#undef OID_Oid2name
#undef OID_str2Oid
#undef OID_str2proto

/*CONST*/ char *OID_Oid2str ( Oid o, int proto )
{
   if (OID_debug && proto != 1) fprintf (stderr, "OID_Oid2str: proto(%d) != 1\n", proto);
   assert ((unsigned)(proto-1) < (unsigned)2);
   return OID_contents(o)->str[proto];
}

/*CONST*/ char *OID_Oid2name ( Oid o )
{
   return OID_contents(o)->str[OID_debug ? 1 : 0];
}

Oid OID_str2Oid   ( const char *key )	{  return OID_find (key, 1)->ref;    }
int OID_str2proto ( const char *key )	{  return OID_find (key, 0)->proto;  }

int OID_cmp ( Oid o1, Oid o2 )
{
   if ( OID_EQ ( o1, o2 ) )
      return 0;
   if ( !OID_contents ( o1 ) )
      return -1;
   if ( !OID_contents ( o2 ) )
      return 1;
   return strcasecmp ( OID_contents (o1)->str[1], OID_contents (o2)->str[1] );
}


int OID_isNull ( Oid o )
{
   const char	*str;
   if ( !OID_contents ( o ) )
      return True;
   str = OID_contents ( o ) -> str[1];
   return str[*str == '0'] == '\0' ? True : False;
}


Oid
OID_register ( const char *name, const char *oid1, const char *oid2 )
{
   int		i;
   Oid		o;
   const char	*arg[3];
   arg[0] = name;
   arg[1] = oid1;
   arg[2] = oid2;

   /* Convert from strings to oids, and check that the OIDs were unregisted */
   for ( i = 0;  i < 3;  i++ )
      if ( OID_find (arg[i], 0) != &unreg_Oid ) {
	 fprintf ( stderr, "oid already registered: %s\n", arg[i] );
	 LOG (facUtil, llevExceptions, "oid already registered: %s\n", arg[i]);
	 return OID_NULLPTR;
      }
   
   /* Register the OIDs */
   if ( reg.size + 3 >= reg.alloc ) {
      reg.alloc += reg.alloc + 30;
      reg.tbl = (oid_tbl_elem *)
	 (reg.tbl
	  ? realloc ( (char *)reg.tbl, reg.alloc * sizeof (*reg.tbl) )
	  : malloc  (                  reg.alloc * sizeof (*reg.tbl) ));
   }
   OID_contents(o) = (Oid_element *) malloc (sizeof (Oid_element));
   for (i = 0;  i < 3; ) {
      reg.tbl[reg.size].ref	= o;
      reg.tbl[reg.size].str	= OID_contents(o)->str[i] = strdup ( arg[i] );
      reg.tbl[reg.size++].proto	= i++;
   }
   return o;
}

#undef		OID_NULLPTR
CONST Oid	OID_NULLPTR	= OID_initializer ( NULL );
