#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <errno.h>

#include "sysdep.h"
#include "memory.h"
#include "bool.h"
#include "darray.h"
#include "FieldCore.h"
#include "MsgType.h"
#include "al.h"
#include "profile.h"
#include "MsgType.h"

#include "MTregP.h"
#include "MTreg.h"

#define MAX_LENGTH	128

AlMTregSt	AlMTR = {
	(Registry)0,
	Bool_FALSE
};

Bool
AlMTreg_init()
{
	Biregistry	env;
	const char	*smtr;
	FILE		*ifp,
			*nifp = NULL;

	AlMTR.reg = Registry_create(Registry_strcmp, Registry_strhash);
	env = Biregistry_create(Registry_strcmp, Registry_strhash,
				Registry_ptrcmp, Registry_ptrhash);
	Registry_add(AlMTR.reg, (VOIDP)"environment", (VOIDP)env);
	AlMTR.initialized = Bool_TRUE;
	smtr = AlProfile_get_CP_val(ALPROF_SYS_MTR);
	AlMTreg_load(smtr);
	return Bool_TRUE;
}
/* The following code is commented out because it supports the 
 * registry-of-registries feature.  This is a good feature, but for
 * release-engineering concerns, it needs to be kept deactivated until 
 * we can figure out how to support it without many mtrinit files
 * around in directories.  These files create problems when new
 * versions of ALens are put together (we have no way to update all the
 * existing mtrinit files if they need to change)
 *   [Jeff Siegal 3/20/89]
 */
#if 0
Bool
AlMTreg_init()
{
	Biregistry	env;
	const char	*mtri;
	char		buf[MAX_LENGTH];
	FILE		*ifp,
			*nifp = NULL;

	AlMTR.reg = Registry_create(Registry_strcmp, Registry_strhash);
	env = Biregistry_create(Registry_strcmp, Registry_strhash,
				Registry_ptrcmp, Registry_ptrhash);
	Registry_add(AlMTR.reg, (VOIDP)"environment", (VOIDP)env);
	AlMTR.initialized = Bool_TRUE;

	mtri = AlProfile_get_CP_val(ALPROF_MTRINIT);

	if ((ifp = fopen(mtri, "r")) == (FILE *)NULL) {
	  /* rap.  Oct 13 1989 */
	  /* replaces the commented out stuff below with this line */
	  Al_fatal_error1("Could not init MTR from file '%s'\n",
			  mtri);
	  /* 
	    const char *nmtri = AlProfile_get_CP_val(ALPROF_MTRSNIT);
	    if ((ifp = fopen(nmtri, "r")) == (FILE *)NULL) {
	    Al_fatal_error2("Could not init MTR from %s or %s (AlMTreg_init)",
	    mtri,
	    nmtri);
	    */
	} else {
	  if ((nifp = fopen(mtri, "w")) == (FILE *)NULL) {
	    Al_warning1("Could not create personal MTR init file %s (AlMTreg_init)",
			mtri);
	  }
	}
  
	while (fscanf(ifp, " %s",  buf) != EOF) {
	  if (AlMTreg_load(buf) == Bool_FALSE) {
	    Al_warning1("AlMTreg_init(): error loading file %s", buf);
	  }
	  if (nifp)
	    fprintf(nifp, "%s\n", buf);
	}
	if(nifp)
	  if (fclose(nifp) == EOF)
	    Al_fatal_error("Error closing MTR file (AlMTreg_init)");
	fclose(ifp);
	return Bool_TRUE;
}
#endif
	


Bool
AlMTreg_register(mt, sub_reg)
AlMsgType	mt;
Biregistry	sub_reg;
{
	VOIDP	name;

	assert(mt);
	assert(sub_reg);
	assert(AlMTR.initialized == Bool_TRUE);

	name = (VOIDP)AlMsgType_get_fullname(mt);
	if(!Biregistry_add(sub_reg, (VOIDP)name, (VOIDP)mt))
		/* set error */
		return Bool_FALSE;
	return Bool_TRUE;
}

Bool
AlMTreg_unregister(mt, sub_reg)
AlMsgType	mt;
Biregistry	sub_reg;
{
  VOIDP	name;
  
  assert(mt);
  assert(sub_reg);
  assert(AlMTR.initialized == Bool_TRUE);
  
  name = (VOIDP)AlMsgType_get_fullname(mt);
  if(!Biregistry_remove(sub_reg, (VOIDP)name))
    /* set error */
    return Bool_FALSE;
  return Bool_TRUE;
}

Bool
AlMTreg_sub_register(sub_reg_name, sub_reg)
const char	*sub_reg_name;
Biregistry	sub_reg;
{
	assert(sub_reg_name);
	assert(sub_reg);
	assert(AlMTR.initialized == Bool_TRUE);

	if(!Registry_add(AlMTR.reg, (VOIDP)sub_reg_name, (VOIDP)sub_reg))
		/* set error */
		return Bool_FALSE;
	return Bool_TRUE;
}

AlMsgType
AlMTreg_get_obj_with_name(name)
const char	*name;
{
	AlMsgType	mt;
	Darray		sub_regs;
	unsigned int	len;

	assert(AlMTR.initialized == Bool_TRUE);

	sub_regs = Darray_create();
	Darray_hint(sub_regs, (unsigned)0, 
		    len = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, (Darray)NULL, sub_regs);
	while(len--)
		if(mt = (AlMsgType)Biregistry_get((Biregistry)
						  Darray_reml(sub_regs),
						  (VOIDP)name))
                {
		        Darray_destroy(sub_regs);
			return mt;
		}	
	Darray_destroy(sub_regs);
	return (AlMsgType)NULL;
}

const char *
AlMTreg_get_name_with_obj(mt)
AlMsgType	mt;
{
	char		*name;
	Darray		sub_regs;
	unsigned int	len;

	assert(AlMTR.initialized == Bool_TRUE);

	sub_regs = Darray_create();
	Darray_hint(sub_regs, (unsigned)0, 
		    len = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, (Darray)NULL, sub_regs);
	while(len--)
		if(name = (char *)Biregistry_rev_get((Biregistry)
						     Darray_reml(sub_regs),
						     (VOIDP)mt))
		{
		        Darray_destroy(sub_regs);
			return name;
		}
	Darray_destroy(sub_regs);
	return (char *)NULL;
}

static NORET
Al_store_names(a0, a1, a2)
VOIDP	a0,
	a1,
	a2;
{
	char	*name = (char *)a0;
	Darray	results = (Darray)a2;

	Darray_addh(a2,  a0);
}

NORET
AlMTreg_get_all_msg_type_names(results)
Darray	results;
{
	Darray		sub_regs;
	unsigned int	len;

	assert(results);
	assert(AlMTR.initialized == Bool_TRUE);

	sub_regs = Darray_create();
	Darray_hint(sub_regs, (unsigned)0, 
		    len = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, (Darray)NULL, sub_regs);
	while(len--)
		Biregistry_traverse((Biregistry)Darray_reml(sub_regs),
				    (Registry_ActionProc)Al_store_names,
				    (VOIDP)results);
	Darray_destroy(sub_regs);
}

	
NORET
Al_WMT(name, obj, data)
VOIDP	name,
	obj,
	data;
{
	FILE	*f = (FILE *)data;
	Darray	fields = Darray_create();
	AlFieldCore	fc;
	unsigned	i,
			len;

	fprintf(f, "NAME: %s\nPARENT NAME: %s\nFIELDS: {\n", 
		(char *)AlMsgType_get_name((AlMsgType)obj),
		AlMsgType_get_gen((AlMsgType)obj));

	AlMsgType_all_fields((AlMsgType)obj, fields);
	for(i = 0, len = Darray_len(fields); i < len; i++)
	{	fc = (AlFieldCore)Darray_get(fields, i);
		fprintf(f, "\t%s <%s>\n", AlFieldCore_get_key(fc),
			AlFieldCore_get_field_type_name(fc));
	}
	fputs("}\n\n", f);
}

Bool
AlMTreg_save(fn)
const char	*fn;
{
	FILE		*f;
	Biregistry	sub_reg;

	assert(AlMTR.initialized == Bool_TRUE);

	if(!(sub_reg = (Biregistry)Registry_get(AlMTR.reg, (VOIDP)fn)))
		/* set error */
		return Bool_FALSE;

	if(!(f = fopen(fn, "w")))
		/* Error setting stuff !!! */
		return Bool_FALSE;

	fprintf(f, "#MT: %u\n", Biregistry_entry_count(sub_reg));
	Biregistry_traverse(sub_reg, (Registry_ActionProc)Al_WMT, (VOIDP)f);
	if (fclose(f) == EOF)
		/* Error setting stuff !!! */
		return Bool_FALSE;
	return	Bool_TRUE;
}

NORET
AlMTreg_saveall()
{
	Darray		keys;
	unsigned int	len;

	assert(AlMTR.initialized == Bool_TRUE);

	keys = Darray_create();
	Darray_hint(keys, (unsigned)0, 
		    len = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, keys, (Darray)NULL);
	while(len--)
		AlMTreg_save((char *)Darray_reml(keys));
	Darray_destroy(keys);
}

NORET
Al_reunite(a0, a1, data)
VOIDP	a0,
	a1,
	data;
{
	AlMsgType	mt = (AlMsgType)a1;

	if(AlMsgType_get_parent(mt))
		return;
	AlMsgType_set_parent(mt, AlMTreg_get_obj_with_name(
				AlMsgType_get_unverified_parent_name(mt)));
	AlMsgType_set_child(AlMsgType_get_parent(mt), mt);
}

Bool
AlMTreg_load(fn)
const char	*fn;
{
	Biregistry	sub_reg;
	AlFieldCore	fc;
	AlMsgType	mt;
	Darray		fields,
			sub_regs;
	FILE	*f;
	char	fk[MAX_LENGTH],
		ft[MAX_LENGTH],
		*fullname,
		name[MAX_LENGTH],
		parent[MAX_LENGTH];
	unsigned	size;
	int	c;
	
	assert(AlMTR.initialized == Bool_TRUE);

	if(!(f = fopen(fn, "r")))
		/* !! set error */
		return Bool_FALSE;

	fscanf(f, " #MT: %u", &size);
	sub_reg = Biregistry_create(Registry_strcmp, Registry_strhash,
				    Registry_ptrcmp, Registry_ptrhash);
	Biregistry_size_hint(sub_reg, size);
	*parent = '\0';
	while(fscanf(f, " NAME:%*[ ]%[^\n]\nPARENT NAME:%*[ ]%[^\n]\nFIELDS: {", name, parent) != EOF)
	{	fields = Darray_create();
		while(fscanf(f, " %s <%[^>]> ", fk, ft) != EOF)
		{	fc =  AlFieldCore_create(ft);
			AlFieldCore_set_key(fc, fk);
			Darray_addh(fields, (VOIDP)fc);
			if((c = getc(f)) == '}')
				break;
			ungetc(c, f);
		}
		fullname = (char *)Memory_allocate(strlen(name) + strlen(parent) + 2);
		if(*parent)
			sprintf(fullname, "%s%c%s", parent, 
				AlMsgType_DIVCHAR, name);
		else
			strcpy(fullname, name);
		/* !!!!!! modify to have create date stored in some form */
		mt = AlMsgType_create(fullname, fields, parent, (time_t)0);
		AlMTreg_register(mt, sub_reg);
		*parent = '\0';		/* so wont stay the same if no parent */
	}
	fclose(f);
	AlMTreg_sub_register(fn, sub_reg);
	sub_regs = Darray_create();
	Darray_hint(sub_regs, (unsigned)0, 
		    size = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, (Darray)NULL, sub_regs);
	while(size--)
		Biregistry_traverse((Biregistry)Darray_reml(sub_regs),
				    (Registry_ActionProc)Al_reunite,
				    (VOIDP)NULL);
	Darray_destroy(sub_regs);
	return Bool_TRUE;
}

NORET
Al_verify(a0, a1, data)
VOIDP	a0,
	a1,
	data;
{
	AlMsgType	mt = (AlMsgType)a1,
			m1;
	Bool		*result = (Bool *)data;
	const char	*s;

	if(*result == Bool_FALSE)	/* something before set it */
		return;
	if(strcmp(AlMsgType_get_unverified_parent_name(mt), 
		  (s = AlMsgType_get_parent_name(mt))))
		if(s)			/* Null parent just means not local, */
			*result = Bool_FALSE; /* but can have name. */
/*!!!*/	printf("Checked %s; children:", AlMsgType_get_fullname(mt));
	for(m1 = AlMsgType_get_child(mt); m1; m1 = AlMsgType_get_sibling(m1))
		printf(" %s", AlMsgType_get_name(m1));
	putchar('\n');
	return;
}

Bool
AlMTreg_verify()
{
	Bool		result = Bool_TRUE;
	Darray		sub_regs;
	unsigned int	len;

	sub_regs = Darray_create();
	Darray_hint(sub_regs, (unsigned)0, 
		    len = Registry_entry_count(AlMTR.reg));
	Registry_fetch_contents(AlMTR.reg, (Darray)NULL, sub_regs);
	while(len--)
	{	Biregistry_traverse((Biregistry)Darray_reml(sub_regs),
				    (Registry_ActionProc)Al_verify,
				    (VOIDP)&result);
		if(result == Bool_FALSE)
		{
		        Darray_destroy(sub_regs);
			return Bool_FALSE;
		}
	}
	Darray_destroy(sub_regs);
	return result;
}

/*--------------------------------------------------*/
Biregistry AlMTreg_get_files_biregistry(file)
char *file;
{
  Biregistry br;
  assert(AlMTR.initialized == Bool_TRUE);
  if ((br=(Biregistry)Registry_get(AlMTR.reg,(CONSTVOIDP)file))==NULL) 
    return(NULL);
  else
    return(br);
}

char * TheFileName;

/*--------------------------------------------------*/
NORET almtr_trav1(keyD,valueD,MTD)
VOIDP keyD, valueD,MTD;
{
  char *file;
  Biregistry br;
  AlMsgType MT;
  file=(char*)keyD;
  br=(Biregistry)valueD;
  MT=(AlMsgType)MTD;

  if (TheFileName!=NULL) return;
  else
    if (Biregistry_rev_get(br,MT)!=NULL)
      TheFileName=file;
}

/*--------------------------------------------------*/
char* AlMTreg_get_MTs_file(MT)
AlMsgType MT;
{
  Biregistry br;
  
  TheFileName=NULL;
  Registry_traverse(AlMTR.reg,almtr_trav1,(VOIDP)MT);
  return(TheFileName);
}
