#include "assert.h"   /* must be first for use eith hc2 */
#include "ArgPack.h"
#include "FTreg.h"
#include "FieldType.h"
#include "RMFBuffer.h"
#include "action.h"
#include "al.h"
#include "assert.h"
#include "biregistry.h"
#include "d2.h"
#include "darray.h"
#include "ed.h"
#include "end.h"
#include "ft.h"
#include "memory.h"
#include "mt.h"
#include "opvalscan.h"
#include "profile.h"
#include "rap.h"
#include "rl.h"
#include "ru.h"
#include "rule.h"
#include "ruleset.h"
#include "stddef.h"
#include "string.h"
#include "useful.h"
#include "verb.h"
#include "verbrgy.h"
#include "verbrgy.h"
#include "verbs.h"
#include "widgets.h"
#include "xstuff.h"
#include <ctype.h>
#include "alpop.h"
#include "text.h"
#include "menupack.h"

Bool     rl_FieldOp_make_correct_case PROTOTYPE((AlFieldType,char*));
MenuPack rl_Help_create_verbs_menu PROTOTYPE((reid));
MenuPack rl_Help_create_predicate_menu PROTOTYPE((reid));

static Bool DidMT;

#define RapSStr_duplicate(x) RapStr_duplicate(x)

#define EXIST_OP_NAME "FIELD EXISTANCE"
#define EXIST_OP_INDICATOR "EXISTS"
#define AVAILABLE_OPS "AVAILABLE OPERATORS"
#define DELETE_STR "Remove This Field"

#define ACT_AVAILABLE "Available Actions"
#define ACT_OPTIONS "Options"
#define ACT_CHANGE_SMP "Change This Action"
#define ACT_DELETE_STR "Remove This Action"

#define RL_BAD_VALUE_STRING         "bad value for field type"
#define RL_BAD_VALUE_STRING_HELP    "bad value for field type HELP"

extern NORET d2_Fields_print PROTOTYPE ((int,REMember));

/*--------------------------------------------------*/
void rl_Init(rei)
reid rei;
{
  char *name;
  unsigned i,j,l,l2;
  Bool exists;
  Darray ProfileFTOrder=Darray_create();
  Darray ProfileVerbOrder=Darray_create();
  Darray Found;

  /*---- make the field type presentation order */  
  l2=Darray_len(FieldTypeNames_GR);
  FieldTypePresOrder_GR=Darray_create();
  if (AlProfile_has_value(ALPROF_FTORD)==Bool_FALSE) {
    RE_debug("ft no order\n");
    for (i=0;i<l2;i++)
      Darray_addh(FieldTypePresOrder_GR,
		  RapStr_duplicate(Darray_get(FieldTypeNames_GR,i)));
  }
  else {
    RE_debug("else\n");
               /* clear the foundverbs darray.  This darray has a bool_TRUE */
	       /* for every verb in verbanames that was listed in the verb */
	       /* pres order. it is thus the same size as the verbnames */
    Found=Darray_create();
    for (i=0;i<l2;i++) Darray_addh(Found,Bool_FALSE);
    RapList_parse_into_darray(ProfileFTOrder,
			      AlProfile_get_CP_val(ALPROF_FTORD));
    l=Darray_len(ProfileFTOrder);
    for (i=0;i<l;i++) {
      name=Darray_get(ProfileFTOrder,i);
      exists=Bool_FALSE;
      for (j=0;j<l2;j++) {
	if (strcasecmp(name,Darray_get(FieldTypeNames_GR,j))==0) {
	  exists=Bool_TRUE;
	  if (Bool_FALSE==(Bool)Darray_get(Found,j)) {
	    Darray_addh(FieldTypePresOrder_GR,
			RapStr_duplicate(Darray_get(FieldTypeNames_GR,j)));
	    Darray_set(Found,j,(VOIDP)Bool_TRUE);
	    break;
	  }
	}
      }
      if (exists==Bool_FALSE) {
	Al_warning1("ruleeditor: the field type %s appearing in the profile",
		    name);
	Al_warning("            component FieldTypePresentationOrder is not \
valid.");
      }
    }
    RapList_destroy_darray_contents(ProfileFTOrder);
    Darray_destroy(ProfileFTOrder);
    /* now we have to add any verbs that were not part of the verb pres */
    /* order to the presentation order. */
    for (i=0;i<l2;i++) {
      if (Bool_FALSE==(Bool)Darray_get(Found,i)) {
	RE_debug1("added field type %s.\n",Darray_get(FieldTypeNames_GR,i));
	Darray_addh(FieldTypePresOrder_GR,
		    RapStr_duplicate(Darray_get(FieldTypeNames_GR,i)));
      }
    }
    Darray_destroy(Found);
  }

  l2=Darray_len(VerbNames_GR);
  VerbPresOrder_GR=Darray_create();
  if (AlProfile_has_value(ALPROF_VERBORD)==Bool_FALSE) {
    for (i=0;i<l2;i++)
      Darray_addh(VerbPresOrder_GR,
		  RapStr_duplicate(Darray_get(VerbNames_GR,i)));
  }
  else {
    Found=Darray_create();
               /* clear the Found darray.  This darray has a bool_TRUE */
	       /* for every verb in verbanames that was listed in the verb */
	       /* pres order. it is thus the same size as the verbnames */
    RapList_parse_into_darray(ProfileVerbOrder,
			      AlProfile_get_CP_val(ALPROF_VERBORD));
    for (i=0;i<l2;i++) Darray_addh(Found,Bool_FALSE);
    l=Darray_len(ProfileVerbOrder);
    for (i=0;i<l;i++) {
      name=Darray_get(ProfileVerbOrder,i);
      exists=Bool_FALSE;
      for (j=0;j<l2;j++)
	if (strcasecmp(name,Darray_get(VerbNames_GR,j))==0) {
	  exists=Bool_TRUE;
	  if (Bool_FALSE==(Bool)Darray_get(Found,j)) {
	    Darray_addh(VerbPresOrder_GR,
			RapStr_duplicate(Darray_get(VerbNames_GR,j)));
	    Darray_set(Found,j,(VOIDP)Bool_TRUE);
	  }
	}
      if (exists==Bool_FALSE) {
	Al_warning1("ruleeditor: the verb %s appearing in the profile",
		    name);
	Al_warning("            component VerbPresentationOrder is not \
valid.");
      }
    }
    RapList_destroy_darray_contents(ProfileVerbOrder);
    Darray_destroy(ProfileVerbOrder);
    /* now we have to add any verbs that were not part of the verb pres */
    /* order to the presentation order. */
    for (i=0;i<l2;i++) {
      if (Bool_FALSE==(Bool)Darray_get(Found,i)){
	Darray_addh(VerbPresOrder_GR,
		    RapStr_duplicate(Darray_get(VerbNames_GR,i)));
      }
    }
    Darray_destroy(Found);
  }

  VerbsHelpMenu_GR=rl_Help_create_verbs_menu(rei);
  PredicateHelpMenu_GR=rl_Help_create_predicate_menu(rei);

  if (rei->Rule!=NULL) {
    rei->TopLevelMember=rl_TranslateToRE(rei,AlRule_get_predicate(rei->Rule));
    rei->Actions=rl_TranslateActionsToRE(rei,AlRule_get_actions(rei->Rule));
    d2_Fields_print(210,rei->TopLevelMember);
  }
  else 
    Al_fatal_error("ruleeditor: requires a non-null rule.\n");
}
 
/*--------------------------------------------------*/
/* rl_destroy should destroy  (this is an incomplete list)  */
/*    FieldTypePresOder and use RapStr_destroy to kill each of its members. */
/*--------------------------------------------------*/

/*--------------------------------------------------*/
REMember rl_REMember_create(Type,object,father)
AlRMType Type;
VOIDP   object;
REMember  father;
{
  REAssoc REa;
  REField REf;
   
  REMember r;
  r=(REMember)Memory_allocate(sizeof(struct REMember_str));
  r->Type=Type;
  if (Type==ALRMT_ASSOC) {
    r->object.Assoc=(REAssoc)object;
    REa=(REAssoc)object;
    REa->container=r;
  }
  else if (Type==ALRMT_FIELD) {
    r->object.Field=(REField)object;
    REf=(REField)object;
    REf->container=r;
  }
  else 
    assert(0);
  r->father=father;
  if (father!=NULL) 
    Darray_addh(father->object.Assoc->Contents,r);
  return(r);
}

/*--------------------------------------------------*/
REMember rl_REMember_create_at_index(Type,object,father,index)
     AlRMType Type;
     VOIDP   object;
     REMember  father;
     int index;
{
  REAssoc REa;
  REField REf;
  REMember r;
  assert(father);
  assert(father->Type==ALRMT_ASSOC);
  r=(REMember)Memory_allocate(sizeof(struct REMember_str));
  r->Type=Type;
  if (Type==ALRMT_ASSOC) {
    r->object.Assoc=(REAssoc)object;
    REa=(REAssoc)object;
    REa->container=r;
  }
  else if (Type==ALRMT_FIELD) {
    r->object.Field=(REField)object;
    REf=(REField)object;
    REf->container=r;
  }
  else 
    assert(0);
  r->father=father;
  rl_Darray_insert(father->object.Assoc->Contents,r,index);
  return(r);
}

/*--------------------------------------------------*/
REField rl_REField_create(rei,Type,Key,Value,FieldType,FieldOp,FW)
     reid rei;
     AlRFType Type;
     char *Key, *Value, *FieldType, *FieldOp;
     REFromWhere FW;
{
  REField f;
  char *string, *tempo;
  Darray FieldOps=Darray_create();
  Bool UsingDFO;

  d2_Fields_print(200,rei->TopLevelMember);

  f=(REField)Memory_allocate(sizeof(struct REField_str));
  f->rei=rei;
  f->Type=Type;
  f->FromWhere=FW;
  f->IsState=AlRule_is_state(Type);
  if (f->IsState==Bool_TRUE)
    f->KeyIsMenu=Bool_TRUE;
  else
    f->KeyIsMenu=Bool_FALSE;
  f->FieldClass=RapStr_duplicate(AlRule_get_name_from_class(f->Type));
  if (f->IsState==Bool_TRUE) {
    f->Key=NULL;
  }
  else {  /* right justifies the key and makes sure there is a colon at the */
          /* end of it. */
    char *tkey;
    int mod;
    mod=0;
    if (f->Type==ALRFT_MSG) {
      tkey=rl_Key_rj_and_add_colon(Key,KEY_LENGTH,&mod);
      f->MaxKeyLength=strlen(tkey)+KEY_LENGTH;
      f->Key=strcpy(Memory_allocate(f->MaxKeyLength+1),tkey);
      if (mod==1) Memory_free(tkey);
    }
    else {
      f->MaxKeyLength=strlen(Key)+KEY_LENGTH;
      f->Key=strcpy(Memory_allocate(f->MaxKeyLength+1),Key);
    }
  }
  if (FieldType==NULL) {
    f->FieldTypeObject=DefaultFT_GR;
    string=AlFTreg_obj_to_string(DefaultFT_GR);
    f->FieldType=strcpy(Memory_allocate(strlen(string)+1),string);
  }
  else {
    f->FieldTypeObject=AlFTreg_string_to_obj(FieldType);
    f->FieldType=strcpy(Memory_allocate(strlen(FieldType)+1),FieldType);
  }

  AlFieldType_get_field_op_list(f->FieldTypeObject,FieldOps);
  /* this serves as getting the default field operator for a field type */
  f->defaultFieldOp=RapStr_duplicate((char *)Darray_get(FieldOps,0));

  if (FieldOp==NULL || FieldOp[0]=='\0') {
    f->FieldOp=RapStr_duplicate(f->defaultFieldOp);
    UsingDFO=Bool_TRUE;
  }
  else {
    if (rl_Field_op_is_valid(f->FieldTypeObject,FieldOp)==Bool_FALSE) {
      char s[300];
      f->FieldOp=RapStr_duplicate(f->defaultFieldOp);
      sprintf(s,
	      "The field operator %s for field %s is invalid.\n\
            It is being replaced with the default operator %s.\n\
            The new field operator will not become permanent untill \n\
            the rule is saved.",
	      FieldOp, f->Key, f->FieldOp);
/*      repop_Query(f->ValueW,"Rule Editor",s,"O.K."); */
      printf("%s\n",s);
      UsingDFO=Bool_TRUE;  /* usingDFO is ysed to select weather we display */
			   /* the field op on the screen or not. */
    }
    else {
      if (strcmp(f->defaultFieldOp,FieldOp)==0)
	UsingDFO=Bool_TRUE;
      else
	UsingDFO=Bool_FALSE;
      f->FieldOp=strcpy(Memory_allocate(strlen(FieldOp)+1),FieldOp);
    }
  }
  
  Darray_destroy(FieldOps);

  if (Value!=NULL)
    f->Value=strcpy(Memory_allocate(strlen(Value)+1),Value);
  else
    f->Value=NULL;

  if (UsingDFO==Bool_TRUE)
    tempo=NULL;
  else
    tempo=f->FieldOp;
  
  f->MaxValueBufferLength=VALUE_LENGTH;
  
  /* make the menus */
  f->OpMenu=rl_MakeOpMenu(f);
  f->ClassMenu=NULL;
  f->TypeMenu=NULL;
  f->KeyMenu=NULL;

  /* clear the widgets */
  f->ContainerW=NULL;
  f->LeftContainerW=NULL;
  f->RightContainerW=NULL;
  f->KeyW=NULL; 
  f->OtherKeyW=NULL;
  f->ValueW=NULL;
  f->OpW=NULL;
  f->ClassW=NULL;
  f->TypeW=NULL;
  f->SelectW=NULL;

  f->ShowOp=( (Registry_get(ShowOpOnTheseFieldTypes_GR,
			    f->FieldType)==NULL) ? Bool_FALSE : Bool_TRUE );
  
  f->ExistanceOp=Bool_FALSE;  /* !!! has to be dynamic depending on inputs */
  f->ValueBufferSave=NULL;

  sprintf(f->MyAddress,"%d",f);
  f->predValTranslations=rl_REField_make_translation("<Leave>",
						     "rl_Monitor_field_value",
						     f->MyAddress);
  if (FW==REF_FROM_USER) 
    f->predKeyTranslations=rl_REField_make_translation("<Leave>",
						       "rl_Monitor_field_key",
						       f->MyAddress);
  else
    f->predKeyTranslations=NULL;
  f->ValueIsParsable=Bool_TRUE;

  f->Mode=rei->Mode;
  f->VisibleMode=rei->Mode;

  return(f);
}

/*--------------------------------------------------*/
MenuPack rl_MakeOpMenu(f)
     REField f;
{
  MenuPack sm;
  int i,l,l2,j;
  Darray FieldOps=Darray_create();
  Darray FieldOpFuncs=Darray_create();
  char *fieldop;
  FieldOperatorProc *opProc;
  FTOpRet FTOR;
  Bool addit;

  sm=MenuPack_create_option(NULL,"field-operator-menu","");
  AlFieldType_get_field_op_list(f->FieldTypeObject,FieldOps);
  l=Darray_len(FieldOps);
  for (i=0;i<l;i++) {
    fieldop=(char *)Darray_get(FieldOps,(unsigned)i);
    opProc=AlFieldType_get_field_op_proc(f->FieldTypeObject,fieldop);
    l2=Darray_len(FieldOpFuncs);
    addit=Bool_TRUE;
    for (j=0;j<l2;j++) {
      if (opProc==(FieldOperatorProc*)Darray_get(FieldOpFuncs,(unsigned)j)) {
	addit=Bool_FALSE;
	break;
      }
    }
    if (addit==Bool_TRUE) {
      Darray_addl(FieldOpFuncs,opProc);
      FTOR=(FTOpRet)Memory_allocate(sizeof(struct FTOpRet_str));
      FTOR->fieldOp=fieldop;
      FTOR->Field=f;
      MenuPack_add_option(FTOR->fieldOp,sm,
			  (XtCallbackProc)rl_SelectFieldOp_CB,
			  (caddr_t)FTOR, NULL);
    }
  }
  Darray_destroy(FieldOpFuncs);
  Darray_destroy(FieldOps);
  MenuPack_set_as_default(sm,f->FieldOp);
  
  return(sm);
}

/*--------------------------------------------------*/
void rl_SelectFieldOp_CB(w,ftor_data,cadda)
     Widget w;
     caddr_t ftor_data;
     caddr_t cadda;
{
  REField Field;
  FTOpRet ftor;
  
  ftor=(FTOpRet)ftor_data; 
  Field=ftor->Field;
    /* change the menus */
  MenuPack_set_as_default(Field->OpMenu,ftor->fieldOp);
    /* change the rule editor rule */
  Memory_free(Field->FieldOp);
  Field->FieldOp=RapStr_duplicate(ftor->fieldOp);
  Field->rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
REAssoc rl_REAssoc_create(rei,Type)
reid rei;
AlRAType Type;
{
  REAssoc r;
  r=(REAssoc)Memory_allocate(sizeof(struct REAssoc_str));
  r->rei=rei;
  r->Type=Type;  /* !!! need error checking here */
  r->Menu=rl_REAssoc_MakeMenu(r);
  r->Contents=Darray_create();
  r->ContainerW=NULL; r->InnerContainerW=NULL;
  r->LabelW=NULL;
  return(r);
}

/*--------------------------------------------------*/
MenuPack rl_REAssoc_MakeMenu(r)
REAssoc r;
{
  MenuPack mp=MenuPack_create_popup(NULL,"Options");
  return(mp);
}

/*--------------------------------------------------*/
REArgument rl_REArgument_create(rei,ArgString,ac)
     reid rei;
     REAction ac;
     char *ArgString;
{
  REArgument r;
  r=(REArgument)Memory_allocate(sizeof(struct REArgument_str));
  if (ArgString!=NULL)
    r->Arg=strcpy(Memory_allocate(strlen(ArgString)+1),ArgString);
  else 
    r->Arg=NULL;
  r->action=ac;
  return(r);
}
/*--------------------------------------------------*/
NORET rl_REArgument_destroy(arg)
     REArgument arg;
{
  if (arg->Arg!=NULL)
    Memory_free(arg->Arg);
  Memory_free(arg);
}

/*--------------------------------------------------*/
/* pass in Arguments as NULL to have the action filled with the default */
/* numbver of blank arguments */
/*----------------------------------------*/
REAction rl_REAction_create(rei,VerbName,Arguments)
     reid rei;
     char *VerbName;
     Darray Arguments;
{
  REAction r;
  unsigned i,l;
  char *Argument;
  r=(REAction)Memory_allocate(sizeof(struct REAction_str));
  r->rei=rei;
  r->VerbName=strcpy(Memory_allocate(strlen(VerbName)+1),VerbName);
  r->Verb=AlVerbReg_get_obj(VerbName);
  r->Arguments=Darray_create();
  if (Arguments!=NULL) {
    l=Darray_len(Arguments);
    if ((l>AlVerb_max_args(r->Verb)) || (l<AlVerb_min_args(r->Verb)))
      Al_fatal_error1("ruleeditor: too many arguments to the verb %s.",
		      r->VerbName);
    for (i=0;i<l;i++) {
      Argument=Darray_get(Arguments,i);
      Darray_addh(r->Arguments,rl_REArgument_create(rei,Argument,r));
    }
  }
  else {
    l=AlVerb_def_args(r->Verb);
    for (i=0;i<l;i++)
      Darray_addh(r->Arguments,rl_REArgument_create(rei,(char*)NULL,r));
  }

  r->MenuW=NULL; r->VerbW=NULL;
  r->ContainerW=NULL;
  r->Menu=MenuPack_create_option(NULL,"action-menu","");

  rl_Verbs_add_to_menu(r->Menu,ed_Verb_change_CB,(VOIDP)r);
  MenuPack_add_separator(r->Menu,(ArgPack)NULL);
  MenuPack_add_sub_menu("Help",r->Menu,VerbsHelpMenu_GR);
  
  return(r);
}
/*--------------------------------------------------*/
NORET rl_REAction_destroy(action)
     REAction action;
{
  int i,l;
  Memory_free(action->VerbName);
  l=Darray_len(action->Arguments);
  for (i=0;i<l;i++) 
    rl_REArgument_destroy((REArgument)Darray_get(action->Arguments,(unsigned)i));
  Darray_destroy(action->Arguments);
  if (action->Menu!=NULL)
    MenuPack_destroy(action->Menu);
  Memory_free(action);
}

/*--------------------------------------------------*/
NORET rl_Verbs_add_to_menu(menu,proc,data)
     MenuPack menu;
     XtCallbackProc proc;
     VOIDP data;
{
  int i,l;
  char *name;
  l=Darray_len(VerbPresOrder_GR);
  for (i=0;i<l;i++) {
    name=Darray_get(VerbPresOrder_GR,(unsigned)i);
    MenuPack_add_option(name,menu,proc,
			(VOIDP)RapPack_create2((VOIDP)name,data),
			(ArgPack)NULL);
  }
}

/*--------------------------------------------------*/
MenuPack rl_Help_create_verbs_menu(rei)
     reid rei;
{
  int i,l;
  char *name;
  MenuPack menu=MenuPack_create_cascade(NULL,"verbs-help");
  l=Darray_len(VerbPresOrder_GR);
  for (i=0;i<l;i++) {
    name=Darray_get(VerbPresOrder_GR,(unsigned)i);
    MenuPack_add_option(name,menu,ed_Help2_CB,
			(VOIDP)RapPack_create5((VOIDP)name,
					       (VOIDP)VERBS_HELP_STRING,
					       (VOIDP)rei,
					       (VOIDP)ed_Reset_verb,
					       (VOIDP)rei),
			(ArgPack)NULL);
  }
  return(menu);
}

/*--------------------------------------------------*/
MenuPack rl_Help_create_predicate_menu(rei)
     reid rei;
{
  MenuPack menu=MenuPack_create_cascade(NULL,"predicate-help");
  return(menu);
}


/*--------------------------------------------------*/
REMember rl_TranslateToRE(rei,RM)
reid rei;
AlRuleMember RM;
{
  REMember ret;
  if (AlRuleMember_get_type(RM)==ALRMT_ASSOC)
    ret=rl_TranslateAssocToRE(rei,RM,NULL);
  else {
     /* if the top member is a fiel, make it an and box */
    REMember rem;
    REAssoc rea;
    rea=rl_REAssoc_create(rei,ALRAT_AND);
    rem=rl_REMember_create(ALRMT_ASSOC,rea,NULL);
    ret=rl_TranslateFieldToRE(rei,RM,rem);
  }
  return(ret);
}

/*--------------------------------------------------*/
REMember rl_TranslateFieldToRE(rei,RM,father)
     reid rei;
     AlRuleMember RM;
     REMember  father;
{
  AlRuleField RF=AlRuleMember_get_field(RM);
  REMember REm;
  REField  REf;
  AlRFType Type;
  char *Key, *Value, *FieldType, *FieldOp;
  AlFieldCore FC;

  Type=AlRuleField_get_type(RF);
  FC=AlRuleField_get_fc(RF);
  Key=AlFieldCore_get_key(FC);
  Value=AlRuleField_get_strvalue(RF);
  FieldOp=AlRuleField_get_op_name(RF);
  FieldType=AlFieldCore_get_field_type_name(FC);
  REm=NULL;
  
  REf=rl_REField_create(rei,
			Type,Key,Value,FieldType,
			FieldOp,REF_FROM_TEMPLATE);
  REm=rl_REMember_create(ALRMT_FIELD,REf,father);
  assert(REm);
  REf->container=REm;
  return(REm);
}

/*--------------------------------------------------*/
REMember rl_TranslateAssocToRE(rei,RM,father)
reid rei;
AlRuleMember RM;
REMember father;
{
  REAssoc REa;
  REMember REm;
  AlRuleAssoc RA=AlRuleMember_get_assoc(RM);
  AlRuleMember TempRM;
  unsigned i;
  Darray Contents=Darray_create();

  AlRAType Type;
  Type=AlRuleAssoc_get_type(RA);
  REa=rl_REAssoc_create(rei,Type);
  REm=rl_REMember_create(ALRMT_ASSOC,REa,father);
  assert(REm);
  REa->container=REm;
  AlRuleAssoc_get_contents(RA,Contents);
  for (i=0;i<Darray_len(Contents);i++) {
    TempRM=(AlRuleMember)Darray_get(Contents,i);
    if (AlRuleMember_get_type(TempRM)==ALRMT_ASSOC)
      rl_TranslateAssocToRE(rei,TempRM,REm);
    else
      rl_TranslateFieldToRE(rei,TempRM,REm);
  }

  d2_Fields_print(220,REm);

  return(REm);
}

/*--------------------------------------------------*/
Darray rl_TranslateActionsToRE(rei,Actions)
reid rei;
Darray Actions;
{
  AlAction action;
  AlVerb Verb;
  char const *VerbName;
  Darray Arguments;
  unsigned i;

  for (i=0;i<Darray_len(Actions);i++) {
    action=(AlAction)Darray_get(Actions,i);
    Verb=AlAction_verb(action);
    VerbName=AlVerbReg_get_name(Verb);
    Arguments=AlAction_args(action);
    Darray_addh(rei->Actions,(VOIDP)rl_REAction_create(rei,
						       (char *)VerbName,
						       Arguments));
  }

  return(rei->Actions);
}

/*--------------------------------------------------*/
Darray rl_TranslateActionsFromRE(rei,REacs)
reid rei;
Darray REacs;
{
  Darray AlActions=Darray_create();
  AlAction Action;
  REAction reaction;
  Darray Arguments;
  REArgument REarg;
  unsigned i, j,l,k;
  k=Darray_len(REacs);
  for (i=0;i<k;i++) {
    Arguments=Darray_create();
    reaction=(REAction)Darray_get(REacs,i);
    l=Darray_len(reaction->Arguments);
    for (j=0;j<l;j++)  {
      REarg=(REArgument)Darray_get(reaction->Arguments,j);
      Darray_addh(Arguments,
		  XmTextGetString(REarg->ArgW));
    }
    Action=AlAction_create(reaction->Verb,Arguments);
    Darray_addh(AlActions,Action);
    Darray_destroy(Arguments);
  }
  return(AlActions);
}

/*--------------------------------------------------*/
AlRuleMember rl_TranslateFromRE(rei,rem)
     reid rei;
     REMember rem;
{
  AlRuleMember ret;
  DidMT=Bool_FALSE;
  rei->DisableEdit=Bool_TRUE;
  if (rem->Type==ALRMT_FIELD)
    ret=rl_TranslateFieldFromRE(rei,rem,NULL);
  else if (rem->Type==ALRMT_ASSOC)
    ret=rl_TranslateAssocFromRE(rei,rem,NULL);
  else
    Al_fatal_error("bad REMember type in fiel rl.c : ^^^2\n");

  rei->DisableEdit=Bool_FALSE;
  return(ret);
}

/*8888888888888888888888888888888888888888888888888888888888888888888*/

/*--------------------------------------------------*/
AlRuleMember rl_TranslateFieldFromRE(rei,REm,father)
     reid rei;
     REMember REm;
     AlRuleMember father;
{
  REField field=REm->object.Field;
  AlRuleMember Member;
  char *key, *value, *oldvalue;
  Bool Popped;
  char *s;
  char *format;
  char *help;

  if (field->KeyIsMenu==Bool_FALSE)
    key=XmTextGetString(field->KeyW);
  else
    key="";
  if (field->ValueW!=NULL)
    value=XmTextGetString(field->ValueW);

  /* this loop will keep bothering the user with popups if the field is bad */
  /* (indicated by a NULL Member) and (he has changed the value or it is the */
  /* first time through the while loop) */
  oldvalue=value;
  Popped=Bool_FALSE;
  format=NULL;
  while ((Member=AlRuleMember_mcreate_field(field->Type,
					    key,
					    field->FieldType,
					    value,
					    field->FieldOp))==NULL &&
	 ( (strcmp(oldvalue,value)!=0) || Popped==Bool_FALSE) ) {

    if (format==NULL) {
      format=AlText_get(RE_TEXT_FILE,RL_BAD_VALUE_STRING);
      help=AlText_get(RE_TEXT_FILE,RL_BAD_VALUE_STRING_HELP);
    }

    s=Memory_allocate(strlen(format)+strlen(value)+strlen(key)+
		      strlen(field->FieldType)+2);
    sprintf(s,format,value,key,field->FieldType);

    AlPop_Query(ALPOP_WARNING,
		s,
		help,
		field->ContainerW,
		ALPOP_HANG_BELOW,
		rei->display,
		ALPOP_OK | ALPOP_HELP);
    
    Memory_free(s);
    Popped=Bool_TRUE;
    oldvalue=value;
    value=XmTextGetString(field->ValueW);
    if (field->KeyIsMenu==Bool_FALSE)
      key=XmTextGetString(field->KeyW);
    else
      key="";
  }

  if (Member==NULL)
    Member=AlRuleMember_mcreate_field_no_parse(field->Type,
					       key,
					       field->FieldType,
					       value,
					       field->FieldOp);
  if (Member==NULL)
    Al_fatal_error("rl_TranslateFieldFromRE:could not create a field member.");

  if (father!=NULL) {
    if (AlRuleMember_get_type(father)!=ALRMT_ASSOC)
      Al_fatal_error("parsing error in TranslateFieldFromRE,file rl.c ^^^3\n");
    AlRuleAssoc_make_member_of(AlRuleMember_get_assoc(father),Member);
  }
  return(Member);
}

/*--------------------------------------------------*/
AlRuleMember rl_TranslateAssocFromRE(rei,REm,father)
     reid rei;
     REMember REm;
     AlRuleMember father;
{
  REAssoc REa=REm->object.Assoc;
  AlRuleMember Member, MTMember;
  REMember TempREm;
  unsigned i;
  Member=AlRuleMember_mcreate_assoc(REa->Type);
  if (father!=NULL) {
    if (AlRuleMember_get_type(father)!=ALRMT_ASSOC)
     Al_fatal_error("parsong error in TranslateFieldFromRE, file rl.c ^^^4\n");
    AlRuleAssoc_make_member_of(AlRuleMember_get_assoc(father),Member);
  }    

  for (i=0;i<Darray_len(REa->Contents);i++) {
    TempREm=(REMember)Darray_get(REa->Contents,i);
    if (TempREm->Type==ALRMT_FIELD)
      rl_TranslateFieldFromRE(rei,TempREm,Member);
    else if (REm->Type==ALRMT_ASSOC)
      rl_TranslateAssocFromRE(rei,TempREm,Member);
    else
      Al_fatal_error("bad REMember type in fiel rl.c : ^^^5\n");
  }
  return(Member);

}

/*--------------------------------------------------*/
Bool rl_Field_op_is_valid(FT,op)
AlFieldType FT;
char* op;
{
  Darray FieldOps=Darray_create();
  unsigned i,l;
  AlFieldType_get_field_op_list(FT,FieldOps);
  l=Darray_len(FieldOps);
  for (i=0;i<l;i++) 
    if (strcasecmp((char*)Darray_get(FieldOps,i),op)==0)
      return(Bool_TRUE);
  return(Bool_FALSE);
}

/*--------------------------------------------------*/
REField rl_REField_duplicate(rei,REf)
reid rei;
REField REf;
{
  REField f;
  f=rl_REField_create(rei,
		      REf->Type,
		      REf->Key,
		      REf->Value,
		      REf->FieldType,
		      REf->FieldOp,
		      REf->FromWhere);
  return(f);
}

/*======================================================================*/
/*     The Darray functions                                             */
/*======================================================================*/

/*--------------------------------------------------*/
Darray rl_Darray_remove_ptr(d,p)
Darray d;
VOIDP p;
{
  int i,l,source,dest;
  l=Darray_len(d);
  for (i=0;i<l;i++) 
    if ((VOIDP)Darray_get(d,(unsigned)i)==p) break;
  if (i==l)
    return(d);
  for (dest=i,source=i+1;source<l;dest++,source++)
    Darray_set(d,(unsigned)dest,Darray_get(d,(unsigned)source));
  Darray_remh(d);
  return(d);
}

/*--------------------------------------------------*/
Darray rl_Darray_insert(d,p,index)
     Darray d;
     VOIDP p;
     int index;
{
  int l,source,dest;
  l=Darray_len(d);
  assert(index<=l);
  if (index>=l || l==0) {
    Darray_addh(d,p);
    return(d);
  }
  else {
    Darray_addh(d,Darray_get(d,(unsigned)l-1));
    for (dest=l-1,source=l-2;
	 source>=index && source>=0;
	 source--, dest--) 
      Darray_set(d,(unsigned)dest,Darray_get(d,(unsigned)source));
    Darray_set(d,(unsigned)index,p);
    return(d);
  }
}



/*======================================================================*/

/*--------------------------------------------------*/
char* rl_REField_make_translation(event,function,args)
char *event,*function,*args;
{
  RMFBuffer Buff;
  char *ret;
  Buff=RMFBuffer_create(event,(unsigned)20);
  RMFBuffer_append(Buff,":");
  RMFBuffer_append(Buff,function);
  RMFBuffer_append(Buff,"(");
  RMFBuffer_append(Buff,args);
  RMFBuffer_append(Buff,")");
  ret=RMFBuffer_copy_of_buff(Buff);
  RMFBuffer_destroy(Buff);
  return(ret);
}

/*--------------------------------------------------*/
char* rl_REField_augment_translations(t,event,function,args)
char *t,*event,*function,*args;
{
  RMFBuffer Buff;
  char *ret;
  Buff=RMFBuffer_create(t,(unsigned)20);
  RMFBuffer_append_char(Buff,'\n');
  RMFBuffer_append(Buff,event);
  RMFBuffer_append(Buff,":");
  RMFBuffer_append(Buff,function);
  RMFBuffer_append(Buff,"(");
  RMFBuffer_append(Buff,args);
  RMFBuffer_append(Buff,")");
  ret=RMFBuffer_copy_of_buff(Buff);
  RMFBuffer_destroy(Buff);
  return(ret);
}

    
/*--------------------------------------------------*/
void rl_Modified_rule(w,Xe,parms,NParms)
Widget w;
XEvent* Xe;
String* parms;
Cardinal* NParms;
{
  reid rei;
  int a=atoi(parms[0]);
  rei=(reid)a;
  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void rl_Monitor_field_value(w,Xe,parms,NParms)
Widget w;
XEvent* Xe;
String* parms;
Cardinal* NParms;
{
}

/*--------------------------------------------------*/
void rl_Monitor_field_key(w,Xe,parms,NParms)
Widget w;
XEvent* Xe;
String* parms;
Cardinal* NParms;
{
}

/*--------------------------------------------------*/
/* look into weather it is possible to reuse these pixmaps */
/*----------------------------------------
Pixmap rl_get_pixmap(rei,which)
reid rei;
unsigned which;
{
  static Pixmap MenuPM=NULL;
  Screen *screen;
  Display *display;
  int ScreenNumber;
  char *bits;
  unsigned width, height;

  switch (which) { 
  case SOLID_LINE:
    bits=solidline_bits;
    width=solidline_width;
    height=solidline_height;
    break;
  case DASHED_LINE:
    bits=dashedline_bits;
    width=dashedline_width;
    height=dashedline_height;
    break;  
  }
  display=XtDisplay(rei->ShellW);
  screen=XtScreen(rei->ShellW);
  ScreenNumber=XDefaultScreen(display);
  MenuPM=XCreatePixmapFromBitmapData(display,
				     RootWindowOfScreen(screen),
				     bits,
				     width,
				     height,
				     XBlackPixel(display,ScreenNumber),
				     XWhitePixel(display,ScreenNumber),
				     DefaultDepthOfScreen(screen));
  return(MenuPM);
}
*/

/*--------------------------------------------------*/
/*----------------------------------------*/
char *rl_Key_rj_and_add_colon(Key,SIZE,mod)
     char *Key;
     int SIZE;
     int *mod;
{
  int l;
  char *r;
  Bool Alt;
  l=strlen(Key)-1;
  if (l==(-1))
    return(Key);
  Alt=Bool_FALSE;
  while (Key[l]==' ' && l>0 ) { Alt=Bool_TRUE; l--; }
  if (Alt==Bool_FALSE) {
    if (Key[l]==':')  
      return(Key);
    else {
      *mod=1;
      if (l+3>SIZE)  /* loose memory allocated by parser for Key field */
	r=Memory_allocate(l+4);
      else
	r=Memory_allocate(SIZE+1);
      strcpy(r,Key);
      r[l+1]=':';
      r[l+2]='\0';
      r[l+3]='\0';
    }
    return(r);
  }
  else {
    *mod=1;
    if (l+3>SIZE)  /* loose memory allocated by parser for Key field */
      r=Memory_allocate(l+4);
    else
      r=Memory_allocate(SIZE+1);
    if (l<0)
      r[0]='\0';
    else  {
      strcpy(r,Key);
      r[l+1]='\0';
      if (r[l]!=':') {
	r[l+1]=':';
	r[l+2]='\0';
	r[l+3]='\0';
      } 
    }
    return(r);
  }
}

/*--------------------------------------------------*/
NORET rl_REMember_destroy(rem)
     REMember rem;
{
  RE_debug1("rl: destroying a %s\n", (char*)((rem->Type==ALRMT_ASSOC) ?
					     "ASSOC" :
					     "FIELD"));
}

/*--------------------------------------------------*/
Widget rl_Get_widget_from_member(rem)
REMember rem;
{
  if (rem==NULL) 
    return(NULL);
  else if (rem->Type==ALRMT_ASSOC)
    return(rem->object.Assoc->ContainerW);
  else
    return(rem->object.Field->ContainerW);
}


/*--------------------------------------------------*/
NORET  rl_Get_surrounding_widgets(rei,rem,
				  PrevW,NextW,ParentW)
reid rei;
REMember rem;
Widget *PrevW, *NextW, *ParentW;
{
  REMember Prev, Next, Parent;

  rl_Get_surrounding_members(rei,rem,&Prev,&Next,&Parent);
  if (Prev==NULL) {
    if (Parent!=NULL)
      *PrevW=Parent->object.Assoc->BarW;
    else
      *PrevW=NULL;
  }
  else
    *PrevW=rl_Get_widget_from_member(Prev);
  *NextW=rl_Get_widget_from_member(Next);
  
  if (Parent==NULL)
    *ParentW=rei->IfW;
  else {
    if (Parent->object.Assoc->InnerContainerW==NULL)
      *ParentW=rei->IfW;
    else
      *ParentW=Parent->object.Assoc->InnerContainerW;
  }
}

/*--------------------------------------------------*/
NORET rl_Get_surrounding_members(rei,rem,Prev,Next,Parent)
reid rei;
REMember rem, *Prev, *Next, *Parent;
{
  REMember Prem=rem->father;

  if (Prem==NULL) {
    *Prev=NULL;
    *Next=NULL;
    *Parent=NULL;
    return;
  }
  else if (Prem->Type!=ALRMT_ASSOC) 
    Al_fatal_error("rl_Get_surrounding_members: bad rule language.\n");
  else {
    int i,l;
    REAssoc Prea=Prem->object.Assoc;
    REMember Crem;
    l=Darray_len(Prea->Contents);
    for (i=0;i<l;i++) {
      Crem=(REMember)Darray_get(Prea->Contents,(unsigned)i);
      if (Crem==rem) {
	if (i==0)
	  *Prev=NULL;
	else
	  *Prev=(REMember)Darray_get(Prea->Contents,(unsigned)i-1);

	if ((i+1)>=l)
	  *Next=NULL;
	else 
	  *Next=(REMember)Darray_get(Prea->Contents,(unsigned)i+1);

	*Parent=Prem;
	break;
      }
    }
  }
}

/*--------------------------------------------------*/
int rl_REMember_get_index(rem)
REMember rem;
{
  REMember Prem=rem->father;
  REAssoc rea;
  int i;

  if (Prem==NULL)
    return(-1);
  else {
    assert(Prem->Type==ALRMT_ASSOC);
    rea=Prem->object.Assoc;
    i=0;
    while ((REMember)Darray_get(rea->Contents,(unsigned)i)!=rem) i++;
    if (i==Darray_len(rea->Contents)) 
      Al_fatal_error("rl_REMember_get_index: parent not owner of member!!\n");
    return(i);
  }
}

/*--------------------------------------------------*/
NORET rl_REAssoc_clear_for_screen(assoc)
     REAssoc assoc;
{
  int i,l;
  assoc->LabelW=NULL;
  assoc->BarW=NULL;
  assoc->ContainerW=NULL;
  if (assoc->Menu!=NULL)
    MenuPack_destroy(assoc->Menu);
  l=Darray_len(assoc->Contents);
  for (i=0;i<l;i++) 
    rl_REMember_clear_for_screen((REMember)Darray_get(assoc->Contents,(unsigned)i));
}

/*--------------------------------------------------*/
NORET rl_REField_clear_for_screen(field)
     REField field;
{
  if (field->OpMenu!=NULL) {
    MenuPack_destroy(field->OpMenu);
    field->OpMenu=NULL;
  }
  if (field->ClassMenu!=NULL) {
    MenuPack_destroy(field->ClassMenu);
    field->ClassMenu=NULL;
  }
  if (field->TypeMenu!=NULL) {
    MenuPack_destroy(field->TypeMenu);
    field->TypeMenu=NULL;
  }
  if (field->KeyMenu!=NULL) {
    MenuPack_destroy(field->KeyMenu);
    field->KeyMenu=NULL;
  }

  if (field->KeyIsMenu==Bool_FALSE) {
    if (field->Key!=NULL)
      Memory_free(field->Key);
    if (field->KeyW!=NULL)
      field->Key=RapStr_duplicate(XmTextGetString(field->KeyW));
    else
      field->Key=NULL;
  }
  /* otherwise leave the Key buffer alone */

  if (field->Value!=NULL)
    Memory_free(field->Value);
  if (field->ValueW!=NULL)
    field->Value=RapStr_duplicate(XmTextGetString(field->ValueW));
  else
    assert(0);   /* shouldn't happen */
  
  /* some of these are useless, but I preffer consistancy to speed */
  field->KeyW=NULL; field->OtherKeyW=NULL;
  field->ClassW=NULL; field->TypeW=NULL;
  field->ValueW=NULL; field->OpW=NULL; field->ContainerW=NULL;
  field->RightContainerW=NULL; field->LeftContainerW=NULL;

  field->KeyIsMenu=Bool_FALSE;
}

/*--------------------------------------------------*/
NORET rl_REMember_clear_for_screen(member)
     REMember member;
{
  if (member->Type==ALRMT_ASSOC)
    rl_REAssoc_clear_for_screen(member->object.Assoc);
  else if (member->Type==ALRMT_FIELD)
    rl_REField_clear_for_screen(member->object.Field);
  else
    Al_fatal_error("rl_REMember_clear_for_screen: bad rule language\n");
}


/*--------------------------------------------------*/
NORET rl_Actions_get_surrounding_widgets(rei,action,PreviousW,NextW)
     reid rei;
     REAction action;
     Widget *PreviousW, *NextW;
{
  REAction NAction,PAction;
  int Index=RapDarray_pointer_get_index(rei->Actions,action);
  int l;

  assert(Index!=(-1));

  if (Index==0)
    *PreviousW=rei->ThenBarW;
  else {
    PAction=(REAction)Darray_get(rei->Actions,(unsigned)Index-1);
    *PreviousW=PAction->ContainerW;
  }
 
  l=Darray_len(rei->Actions);
  if (Index+1 >=l) 
    *NextW=NULL;
  else {
    NAction=(REAction)Darray_get(rei->Actions,(unsigned)Index+1);
    *NextW=NAction->ContainerW;
  }
}

/*--------------------------------------------------*/
NORET rl_Arguments_get_surrounding_widgets(action,arg,PreviousW,NextW)
     REAction action;
     REArgument arg;
     Widget *PreviousW, *NextW;
{
  int Index,l;
  REArgument Parg, Narg;

  if (RapDarray_pointer_index(action->Arguments,
			      (VOIDP)arg,&Index)==Bool_FALSE)
    Al_fatal_error("error while adding argument.\n");

  if (Index==0)
    *PreviousW=NULL;
  else {
    Parg=(REArgument)Darray_get(action->Arguments,(unsigned)Index-1);
    *PreviousW=Parg->ArgW;
  }

  l=Darray_len(action->Arguments);
  if (Index+1 >= l) 
    *NextW=NULL;
  else {
    Narg=(REArgument)Darray_get(action->Arguments,(unsigned)Index+1);
    *NextW=Narg->ArgW;
  }
}

