/* Routines to evaluate the predicate part of a rule into a single
 * value (Bool_TRUE or Bool_FALSE) */

#include <stddef.h>
#include <assert.h>

#include "sysdep.h"
#include "rule.h"
#include "darray.h"
#include "bool.h"
#include "FieldCore.h"
#include "FieldType.h"
#include "msg.h"

#ifdef __STDC__
static Bool AlPredEvalAssoc(Msg, AlRuleAssoc);
static Bool AlPredEvalField(Msg, AlRuleField);
static Bool AlPredEvalString(const char *, AlFieldCore, FieldOperatorProc *);
static Bool AlPredEvalInt(int, AlFieldCore, FieldOperatorProc *);
#else
static Bool AlPredEvalAssoc();
static Bool AlPredEvalField();
static Bool AlPredEvalString();
static Bool AlPredEvalInt();
#endif

/* Evaluate a predicate expression, which can be either a FIELD or
 * an ASSOC.  FIELD's are basic expressions that evaluate to True
 * or False (e.g. is the "to:" field equal to "joe").  ASSOC's
 * are combinations using AND, OR, NOT. */

Bool AlPredEval(msg, member)
     Msg msg;
     AlRuleMember member;
{
  /* none of these switch arms are supposed to fall through  */
  switch (AlRuleMember_get_type(member)) {
  case ALRMT_ASSOC:
    return AlPredEvalAssoc(msg, AlRuleMember_get_assoc(member));
  case ALRMT_FIELD:
    return AlPredEvalField(msg, AlRuleMember_get_field(member));
  }
}

/* Breaks down an ASSOC (boolean expression) into its components,
 * evaluates those components, and evaluates the boolean operator.
 * Performs short-circuiting. */

static Bool AlPredEvalAssoc(msg, assoc)
     Msg msg;
     AlRuleAssoc assoc;
{
  /* none of these switch arms are supposed to fall through  */
  switch (AlRuleAssoc_get_type(assoc)) {
  case ALRAT_AND: 
    {
      Bool temp_value = Bool_TRUE;
      AlRuleMember current_member = AlRuleAssoc_get_first_member(assoc);

      while (temp_value == Bool_TRUE && current_member != NULL) {
	temp_value = Bool_and(temp_value, 
			      AlPredEval(msg, current_member));
	current_member = AlRuleMember_get_sibling(current_member);
      }
      return temp_value;
    }
  case ALRAT_OR:
    {
      Bool temp_value = Bool_FALSE;
      AlRuleMember current_member = AlRuleAssoc_get_first_member(assoc);

      while (temp_value == Bool_FALSE && current_member != NULL) {
	temp_value = Bool_or(temp_value, 
			     AlPredEval(msg,current_member));
	current_member = AlRuleMember_get_sibling(current_member);
      }
      return temp_value;
    }
  case ALRAT_NOT:
    return Bool_not(AlPredEval(msg, AlRuleAssoc_get_first_member(assoc)));
  }
}

/* Evaluate each rule field, returning True or False.  Do the appropriate
 * thing depending on whether the rule field refers to a message field, 
 * a user property, or a state. */

static Bool AlPredEvalField(msg, field)
     Msg msg;
     AlRuleField field;
{
  AlFieldCore temp_rule_fcore = AlRuleField_get_fc(field);
  FieldOperatorProc *temp_rule_op_func = AlRuleField_get_op_func(field);

  /* This special case handles predicates with no operator specified.
   * These should be ignored, to allow fields to be stored in a rule
   * without any matching requirements on them
   */

  if (temp_rule_op_func == 0)
    return Bool_TRUE;
  
  switch (AlRuleField_get_type(field)) {
  case ALRFT_MSG:
    {
      Bool temp_condition;
      Darray temp_msg_fcores = Darray_create();

      temp_condition 
	= AlMsg_get_fields(msg, 
			   AlFieldCore_get_key(temp_rule_fcore),
			   AlFieldCore_get_field_type(temp_rule_fcore),
			   temp_msg_fcores);
      if (temp_condition == Bool_TRUE) {
	unsigned int i;
	temp_condition = Bool_FALSE;
	for (i = 0; i < Darray_len(temp_msg_fcores); ++i) {
	  if (temp_condition == Bool_TRUE) break;
	  temp_condition = Bool_or(temp_condition, 
	      AlFieldCore_compare_with_field_op(
		  (AlFieldCore)Darray_get(temp_msg_fcores, i),
		  temp_rule_fcore,
		  temp_rule_op_func));
	}
      } else
	temp_condition = Bool_FALSE;
      AlMsg_clear_error_code(msg);
      Darray_destroy(temp_msg_fcores);
      return temp_condition;
    }
  case ALRFT_UP:
    {
      AlFieldCore temp_msg_fcore;
      AlFieldType temp_rule_type;
      Bool temp_condition = Bool_FALSE;
      const char *temp_rule_upname, *temp_msg_up;
      
      temp_rule_upname = AlFieldCore_get_key(temp_rule_fcore);
      temp_rule_type = AlFieldCore_get_field_type(temp_rule_fcore);
      temp_msg_fcore = AlFieldCore_create_with_obj(temp_rule_type);
      AlFieldCore_set_key(temp_msg_fcore, temp_rule_upname);

      temp_msg_up = AlMsg_get_uprop(msg, temp_rule_upname);
      if (temp_msg_up != NULL) {
	if (AlFieldCore_parse(temp_msg_fcore, temp_msg_up) == Bool_TRUE) {
          temp_condition = AlFieldCore_compare_with_field_op(
		  temp_msg_fcore,
		  temp_rule_fcore,
		  temp_rule_op_func);
	}
      }
      AlMsg_clear_error_code(msg);
      AlFieldCore_destroy(temp_msg_fcore);
      return temp_condition;
    }

  case ALRFT_FI:
    {
      AlFieldCore temp_msg_fcore;
      AlFieldType temp_rule_type;
      Bool temp_condition = Bool_FALSE;
      const char *temp_rule_fi, *temp_msg_fi;
      Registry fi_reg=AlMsg_get_fs_state_registry(msg);
      /*
      {
	Darray key=Darray_create();
	Darray val=Darray_create();
	int i,l;

	Registry_fetch_contents(fi_reg,key,val);
	l=Darray_len(key);
	printf("-------->>>>>>>>>>>>>>>>>>>>\n");
	for (i=0;i<l;i++)
	  printf("%s-->%s\n",Darray_get(key,(unsigned)i),
		 Darray_get(key,(unsigned)i));
	printf("--------<<<<<<<<<<<<<<<<<<<<\n");
      }
      */
      temp_rule_fi = AlFieldCore_get_key(temp_rule_fcore);
            
      if ((temp_msg_fi=Registry_get(fi_reg,temp_rule_fi))==NULL)
	return(Bool_FALSE);
      
      temp_rule_type = AlFieldCore_get_field_type(temp_rule_fcore);
      temp_msg_fcore = AlFieldCore_create_with_obj(temp_rule_type);
      AlFieldCore_set_key(temp_msg_fcore, temp_rule_fi);

      if (AlFieldCore_parse(temp_msg_fcore, temp_msg_fi) == Bool_TRUE) {
	temp_condition = AlFieldCore_compare_with_field_op(temp_msg_fcore,
							   temp_rule_fcore,
							   temp_rule_op_func);
      }
      AlMsg_clear_error_code(msg);
      AlFieldCore_destroy(temp_msg_fcore);
      return (temp_condition);
    }
							  
  case ALRFT_SMF:
    {
      char *temp_moved_from = AlMsg_get_moved_from_state(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalString(temp_moved_from, 
			      temp_rule_fcore,
			      temp_rule_op_func);
    }

  /* case <how many times moved>: Where is it??? */
  case ALRFT_SNRF:
    {
      int temp_num_rules_fired = AlMsg_get_num_rules_fired_state(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_num_rules_fired, 
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SNOF:
    {
      int temp_num_of_fields = AlMsg_get_num_of_fields(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_num_of_fields,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SMLL:
    {
      int temp_msg_line_length = AlMsg_get_msg_line_length(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_msg_line_length, 
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  /* case <message character length>: Where is it??? */
  case ALRFT_SBLL:
    {
      int temp_body_line_length = AlMsg_get_body_line_length(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_body_line_length,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SBCL:
    {
      int temp_body_char_length = AlMsg_get_body_char_length(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_body_char_length,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SNUB:
    {
      int temp_num_unprintables = AlMsg_unprintables_in_body(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_num_unprintables,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SFC:
    {
      int temp_state_field_count = AlMsg_get_state_field_count(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_state_field_count,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SNTM:
    {
      int temp_num_tm = AlMsg_get_how_many_times_moved(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_num_tm,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
  case ALRFT_SMCL:
    {
      int temp_num_mcl = AlMsg_get_msg_char_length(msg);
      AlMsg_clear_error_code(msg);
      return AlPredEvalInt(temp_num_mcl,
			   temp_rule_fcore, 
			   temp_rule_op_func);
    }
    

  default:
    assert(0);			/* All cases should have been handled */
  }
    
}

/* Check a string from the message against the match FieldCore and operator 
 * provided by the rule */

static Bool AlPredEvalString(msgstring, rule_fc, rule_op)
     const char *msgstring;
     AlFieldCore rule_fc;
     FieldOperatorProc *rule_op;
{
  AlFieldCore temp_msg_fcore;
  Bool temp_condition = Bool_FALSE;
  
  temp_msg_fcore = 
    AlFieldCore_create_with_obj(AlFieldCore_get_field_type(rule_fc));
  AlFieldCore_set_key(temp_msg_fcore, AlFieldCore_get_key(rule_fc));
  if (AlFieldCore_parse(temp_msg_fcore, msgstring) == Bool_TRUE) {
    temp_condition = 
      AlFieldCore_compare_with_field_op(temp_msg_fcore,
					rule_fc,
					rule_op);
  }
  AlFieldCore_destroy(temp_msg_fcore);
  return temp_condition;
}


/* Check an int from the message (usually from a state value) against 
 * the match FieldCore and operator 
 * provided by the rule */

static Bool AlPredEvalInt(msgint, rule_fc, rule_op)
     int msgint;
     AlFieldCore rule_fc;
     FieldOperatorProc *rule_op;
{
  AlFieldCore temp_msg_fcore;
  AlFieldType temp_field_type;
  Bool temp_condition = Bool_FALSE;
  char buffer[100];
      
  temp_field_type = AlFieldCore_get_field_type(rule_fc);
  temp_msg_fcore = AlFieldCore_create_with_obj(temp_field_type);
  AlFieldCore_set_key(temp_msg_fcore, AlFieldCore_get_key(rule_fc));
  sprintf(&buffer[0], "%d", msgint);
  if (AlFieldCore_parse(temp_msg_fcore, &buffer[0]) == Bool_TRUE) {
    temp_condition = AlFieldCore_compare_with_field_op(
	          temp_msg_fcore,
		  rule_fc,
		  rule_op);
  }
  AlFieldCore_destroy(temp_msg_fcore);
  return temp_condition;
}
