#include "sysdep.h"
#include "stddef.h"
#include "al.h"
#include "MsgType.h"
#include "MTreg.h"
#include "mte.h"
#include "darray.h"
#include "RMFBuffer.h"
#include "FieldCore.h"
#include "string.h"
#include "rap.h"

#ifdef __STDC__
NORET mteditor_save(VOIDP,AlMsgType);
NORET mteditor_quit(VOIDP,AlMsgType);
char* mteditor_make_full_name(char*,char*);
NORET mteditor_duplicate_field_cores(Darray,Darray);
NORET mteditor_usage();
char* mteditor_get_parent_name(char*);
#else
NORET mteditor_save();
NORET mteditor_quit();
char* mteditor_make_full_name();
NORET mteditor_duplicate_field_cores();
NORET mteditor_usage();
char* mteditor_get_parent_name();
#endif

struct MteditorID {
  Bool ReadOnly;         /*     1             1    */
  char *newName;         /*     -             1       */
  char *fullName;        /*     1             1       */
  char *parName;         /*     -             1       */
  char *saveFileName;/*    -             1        */
  Bool SaveFileExists;
  char *parentsFileName; /*     -             m       */
  AlMsgType MT, PMT;     /*     1             1       */
  Darray NewFieldCores;      /*     -             1       */
  Biregistry FilesBR;    /*     -             1       */
};    

main (argc,argv)
char **argv;
int argc;
{
  MteGR GR;
  Darray MTs=Darray_create();
  Darray ParFields=Darray_create();
  Darray OldFields=Darray_create();
  char *newName, *parName, *fullName;
  struct MteditorID mtid;

  mtid.ReadOnly=Bool_TRUE;
  mtid.MT=NULL;  mtid.PMT=NULL;
  mtid.FilesBR=NULL;
  mtid.newName=NULL;
  mtid.fullName=NULL;
  mtid.parName=NULL;
  mtid.NewFieldCores=Darray_create();
  mtid.saveFileName=NULL;
  mtid.SaveFileExists=Bool_TRUE;
  Al_init();
  {
    unsigned i,l,j;
    if (argc==1)
      mteditor_usage();
    
    for (i=1;i<argc;i++) {
      if (argv[i][0]=='-' && argv[i][1]=='h') {
	mteditor_usage();
      }
    }
    

    /* should be a -l option to load in just plain old mtreg files that we */
    /* feel might be important. Put that code here. */
    for (i=1;i<argc;i++) {
      if (strcmp(argv[i],"-l")==0) {
	if (argc<=i+1)
	  mteditor_usage();
	for (j=i+1;j<argc;j++) {
	  if (argv[j][0]=='-')
	    break;
	  else 
	    if (AlMTreg_load(argv[j])==Bool_FALSE)
	      Al_warning1("mteditor: could not load file %s\n",argv[j]);
	}
      }
    }

    if (argv[1][0]!='-') { /* just viewing a mt */
      mtid.ReadOnly=Bool_TRUE;
      mtid.MT=AlMTreg_get_obj_with_name(argv[1]);
      if (mtid.MT==NULL)
	Al_fatal_error1("mteditor: could not find message type %s.",argv[1]);
    }
    else {
      /* find the message type if there is one. Ther is a problem here, I do */
      /* not know if the MTreg will distinguish between MTs named the same */
      /* way but in different files.  Presumably if the user specifies a */
      /* file with the -f option he would like his mt to come from that file */
      /* (and if he were creating a mt it would go to that file). */
      /* I should add a funcation to the MTreg that will allow you to get a */
      /* MT from a particular file.  Note that I THINK the MTreg does NOT */
      /* handle multiply named MTs properly. */
      for (i=1;i<argc;i++) {
	if (strcmp(argv[i],"-mt")==0) {
	  if ((argc<=i+1) ||
	      (mtid.MT=AlMTreg_get_obj_with_name(argv[i+1]))==NULL)
	    mteditor_usage();
	}
      }
    }
    
    /* initialize the rest of the mtid.  Up to here we have either loaded a */
    /* file which will be the file we put any new MT into or we have */
    /* specified that we want to look at a certain MT.  */
    if (mtid.MT!=NULL) {
      mtid.ReadOnly=Bool_TRUE;
      AlMsgType_all_fields(mtid.MT,OldFields);
      mteditor_duplicate_field_cores(OldFields,mtid.NewFieldCores);
      mtid.fullName=RapStr_duplicate(AlMsgType_get_fullname(mtid.MT));
      mtid.parName=RapStr_duplicate(AlMsgType_get_parent_name(mtid.MT));
    }  /* should be creating a new msg type */
    else {
        /* The reason we load in the -f file first is that the parent of the */
	/* new MT is most likely (and certainly can be) part of that file. */
      for (i=0;i<argc;i++) {
	if (strcmp(argv[i],"-f")==0) {
	  if (argc<=i+1)
	    mteditor_usage();
	  mtid.saveFileName=RapStr_duplicate(argv[i+1]);
	  if ((mtid.FilesBR=
	       AlMTreg_get_files_biregistry(mtid.saveFileName))==NULL) {
	    if (AlMTreg_load(mtid.saveFileName)==Bool_FALSE)
	      mtid.SaveFileExists=Bool_FALSE;
	    else {
	      if((mtid.FilesBR=
		  AlMTreg_get_files_biregistry(mtid.saveFileName))==NULL)
		Al_fatal_error("mteditor: unsettling error 1.");
	      mtid.SaveFileExists=Bool_TRUE;
	    }
	  }
	}
      }
      
      for (i=0;i<argc;i++) {
	if (strcmp(argv[i],"-p")==0) {
	  if ((argc<=i+1) ||
	      (mtid.PMT=AlMTreg_get_obj_with_name(argv[i+1]))==NULL) {
	    Al_warning1("mteditor: could not find parent message type %s.",
			argv[i+1]);
	    mteditor_usage();
	  }
	  else {
	    mtid.ReadOnly=Bool_FALSE;
	    mtid.parName=RapStr_duplicate(AlMsgType_get_fullname(mtid.PMT));
	  }
	}
      }
      
      for (i=0;i<argc;i++) {
	if (strcmp(argv[i],"-n")==0) {     /* !!! more and varied error */
					   /* messages here please. */
	  if ((argc<=i+1) || mtid.MT!=NULL)
	    mteditor_usage();
	  else {
	    mtid.newName=RapStr_duplicate(argv[i+1]);
	    if (mtid.PMT==NULL) { /* NOTE: both of these can be null */
	      mtid.fullName=RapStr_duplicate(mtid.newName);
	      mtid.parName=mteditor_get_parent_name(mtid.fullName);
	      mtid.PMT=AlMTreg_get_obj_with_name(mtid.parName);
	      if (mtid.parName[0]!='\0' && mtid.PMT==NULL) {
		Al_warning1("mteditor: could not find parent message type %s.",
			    mtid.parName);
		mteditor_usage();
	      }
	    }
	    else {  /* there was a parent ==> new name is not full */
	      mtid.fullName=mteditor_make_full_name(mtid.parName,
						    mtid.newName);
	    }

	    if (AlMTreg_get_obj_with_name(mtid.fullName)!=NULL) 
	      Al_fatal_error("mteditor: it is not yet possile for anyone to \
alter existing message types.\n");

	    if (mtid.PMT!=NULL) {
	      AlMsgType_all_fields(mtid.PMT,ParFields);
	      mteditor_duplicate_field_cores(ParFields,mtid.NewFieldCores);
	    }
	    else if (mtid.saveFileName==NULL) {
	      printf("mteditor: Since the new message type you specified has no parent\n");
	      printf("          and no file was specified with the -f option,\n");   
	      printf("          there is no file to save your new message type.\n");
	      printf("          Please retry mteditor specifying a file using the -f option.\n");
	      Al_fatal_error("");
	    }
	    mtid.ReadOnly=Bool_FALSE;	
	  }
	}
      }
      
      if (mtid.newName==NULL || mtid.parName==NULL) /* not found both parent */
	mteditor_usage();			   /* and new mt name. */
      if ((mtid.MT=AlMsgType_create(mtid.fullName,
				    mtid.NewFieldCores,
				    mtid.parName,
				    (time_t)0))==NULL) 
	Al_fatal_error("mteditor: could not create new message type, sorry.");

      if (mtid.PMT==NULL) {   /* createing new MT but did not specify parent */
	if (mtid.newName==NULL)
	  mteditor_usage();
	mtid.PMT=AlMsgType_get_parent(mtid.MT);
      }

      if (mtid.saveFileName==NULL) {
	if ((mtid.saveFileName=AlMTreg_get_MTs_file(mtid.PMT))==NULL)
	  Al_fatal_error("mteditor: unsettling error 2");
	else {
	  mtid.saveFileName=RapStr_duplicate(mtid.saveFileName);
	  mtid.FilesBR=AlMTreg_get_files_biregistry(mtid.saveFileName);
	  mtid.SaveFileExists=Bool_TRUE;
	}
      }
    } /* end else (search for adding a new mt) */
  } /* end parse of command line */

  /* saveFileName duplicates as the file name of where the msg type came */
  /* from when viewing it. */
  if (mtid.ReadOnly==Bool_TRUE)
    mtid.saveFileName=RapStr_duplicate(AlMTreg_get_MTs_file(mtid.MT));

  GR=Mte_create(MTE_FROM_COMMAND_LINE,
		(AlMsgType)mtid.MT,
		(MteSaveFunc)mteditor_save,
		(MteQuitFunc)mteditor_quit,
		(int)argc,
		(char**)argv,
		(char*)mtid.saveFileName,
		(int)Bool_to_int(mtid.ReadOnly),
		(VOIDP)(&mtid));
  
  Darray_destroy(ParFields);
  Mte_realize(GR);
}
  

/*--------------------------------------------------*/
/* returns a copy. */
/*----------------------------------------*/
char *mteditor_make_full_name(par,new)
char *par,*new;
{
  char *retname;
  RMFBuffer ret=RMFBuffer_create(par,20);
  RMFBuffer_append(ret,"|");
  RMFBuffer_append(ret,new);
  retname=RMFBuffer_copy_of_buff(ret);
  RMFBuffer_destroy(ret);
  return(retname);
}

/*--------------------------------------------------*/
/* returns a copy. */
/*----------------------------------------*/
char *mteditor_get_parent_name(n)
char *n;
{
  char *r,c;
  unsigned i,l;
  l=strlen(n);
  for (i=l;i>0;i--) {
    if (n[i-1]=='|' || n[i-1]==':') {
      c=n[i-1];
      n[i-1]='\0';
      r=RapStr_duplicate(n);
      n[i-1]=c;
      return(r);
    }
  }
  return(RapStr_duplicate(""));
}
      
      
/*--------------------------------------------------*/
NORET mteditor_duplicate_field_cores(old,new)
Darray old,new;
{
  unsigned i,l;

  l=Darray_len(old);
  for(i=0;i<l;i++) 
    Darray_addh(new,AlFieldCore_duplicate((AlFieldCore)Darray_get(old,i)));
}

/*--------------------------------------------------*/
NORET mteditor_save(GRv,mt)
VOIDP GRv;
AlMsgType mt;
{
  struct MteditorID *mtid;
  MteGR GR;
  unsigned i,l;
  AlMsgType MT;

  GR=(MteGR)GRv;
  mtid=(struct MteditorID*)Mte_get_client_data(GR);
  if (mtid->ReadOnly==Bool_TRUE) {
    Al_warning("mteditor: can't save changes to existing message type.");
    return;
  }

  /* the file has not been loaded yet.  Note that when we parsed the command */
  /* line we tried opening it.  It either did not exist or it was not */
  /* parsable bu the MTreg object.  THERE SHOULD BE BETTERE ERROR CHECKING */
  /* FOR THE CASE WHEN IT WAS NOT PARSABLE. */
  /* If the following if is true it means that we specified the -f option */
  /* but we couls not laod in the file as per above. */
  if (mtid->SaveFileExists==Bool_FALSE) {
    mtid->FilesBR= Biregistry_create(Registry_strcmp, Registry_strhash,
				     Registry_ptrcmp, Registry_ptrhash);
    if (AlMTreg_sub_register(mtid->saveFileName,
			     mtid->FilesBR)==Bool_FALSE)
	Al_fatal_error("mteditor: save: could not register biregistry\
 with MTreg");
  }
   
  /* get rid of the old Message Type in the MTreg if it is there */
  AlMTreg_unregister(mtid->MT,mtid->FilesBR);  /* this will remove it if */
					       /* it is there and do */
					       /* nothing if it is not */

  /* destroy the old MT */
  /*  AlMsgType_destroy(mtid->MT); */
  
  /* destroy the old field cores which constituted the old message type */
  /*----- we cannot allow this code because there is no function to retrieve */
  /*the field cores from the MT, and their shouldn't be.  As it is the MT */
  /*object does NOT make its own copy of the fields, so it is up to the MT */
  /*creator to keep track of the Field cores and delte them when necessary. */
  /* ******This is bad*********. */
  /* we could pass to here the new field cores so they could be kept ttrack */
  /* of and then deleted the next time etc.. but that is too messy.  */
/*
    l=Darray_len(mtid->NewFieldCores);
    for (i=0;i<l;i++) 
      AlFieldCore_destroy(Darray_get(mtid->NewFieldCores,i));
    Darray_destroy(mtid->NewFieldCores);
    mtid->NewFieldCores=FC;
*/

  mtid->MT=mt;
  /* register the new message type with the right file biregistry */
  if (AlMTreg_register(mtid->MT,mtid->FilesBR)==Bool_FALSE)
    Al_warning1("mteditor: could not save %s message type.1",
		mtid->fullName);

  /* save that file (i.e. save all MTs that aew in that file) */
  if (AlMTreg_save(mtid->saveFileName)==Bool_FALSE)
    Al_warning1("mteditor: could not save %s message type.2",
		mtid->fullName);
  
  /* that file must exist now */
  mtid->SaveFileExists=Bool_TRUE;
}

/*--------------------------------------------------*/
NORET mteditor_quit(GRv,mt)
VOIDP GRv;
AlMsgType mt;
{
  MteGR GR;
  struct MteditorID *mtid;

  GR=(MteGR)GRv;
  mtid=(struct MteditorID*)Mte_get_client_data(GR);

/*  AlMsgType_destroy(Mte_get_message_type(GR)); */
  /* if it has been registered in the MTreg by the save roputine we do not */
  /* want to destroy it. */
  Mte_destroy(GR);
  exit(0);
}

static char * Mteditor_Usage="\
Message Type Editor usage\n\
  To view a message type\n\
    mteditor [-mt] <messgae-type>\n\
  To create a message type\n\
    mteditor -n <new-message-type-name> -p <parent-name> [-f <file>]\n\
  where the new-message-type-name is a single name interpreted as a child\n\
  of the <parent-name>. Or\n\
    mteditor -n <new-message-type-name> [-f <file>]\n\
  where the new-message-type-name is a complete message type name.\n\
  The <file> specifies a file to store the new message type in.\n\
  You can also specify any number of files containing message types which\n\
  you want to load with the [-l [<file-of-messge-types>...]] option.\n";



/*--------------------------------------------------*/
NORET mteditor_usage()
{
  printf("%s",Mteditor_Usage);
  exit(0);
}
