# include "opihi.h"
# define VERBOSE 0

# define TWO_OP(A,B,FUNC) \
  if (!strncasecmp (&stack[i - 2].type, A, 1) && !strncasecmp (&stack[i - 1].type, B, 1)) \
    status = FUNC (&tmp_stack, &stack[i - 2], &stack[i - 1], stack[i].name); 

# define ONE_OP(A,FUNC) \
  if (!strncasecmp (&stack[i - 1].type, A, 1)) \
    status = FUNC (&tmp_stack, &stack[i - 1], stack[i].name); 

int evaluate_stack (StackVar *stack, int *Nstack) {
  
  int i, j, Nvar, Nout, status;
  char line[512]; // this is only used to report an error
  StackVar tmp_stack;
  Nout = Nvar = 0;

  status = TRUE;
  init_stack (&tmp_stack);

  if (*Nstack == 1) {
    if (stack[0].type == 'S') {
      clear_stack (&tmp_stack);
      return (TRUE);
    }
    if (stack[0].type == 'V') {
      /* need to make a copy so we set output value? */
      V_unary (&tmp_stack, &stack[0], "=");
      move_stack (&stack[0], &tmp_stack);
      return (TRUE);
    }
    if (stack[0].type == 'M') {
      /* need to make a copy so we set output value? */
      M_unary (&tmp_stack, &stack[0], "=");
      move_stack (&stack[0], &tmp_stack);
      return (TRUE);
    }
    push_error ("syntax error: not a math expression");
    clear_stack (&tmp_stack);
    return (FALSE);
  }      
    
  for (i = 0; i < *Nstack; i++) {

    if (VERBOSE) {
      gprint (GP_ERR, "%d: ", i);
      for (j = 0; j < *Nstack; j++) {
	gprint (GP_ERR, "%s ", stack[j].name);
      }
      if (*Nstack > 0) gprint (GP_ERR, "\n");
      gprint (GP_ERR, "%d: ", i);
      for (j = 0; j < *Nstack; j++) {
	gprint (GP_ERR, "%d ", stack[j].type);
      }
      if (*Nstack > 0) gprint (GP_ERR, "\n");
    }

    /***** binary operators *****/
    if ((stack[i].type >= 3) && (stack[i].type <= 8)) {

      if (i < 2) {  /* need two variables to operate on */
	sprintf (line, "syntax error: binary operator with one operand: %s\n", stack[i].name);
	push_error (line);
	clear_stack (&tmp_stack);
	return (FALSE);
      }

      status = FALSE;
      TWO_OP ("M","M",MM_binary);
      TWO_OP ("M","V",MV_binary);
      TWO_OP ("M","S",MS_binary);
      TWO_OP ("V","M",VM_binary);
      TWO_OP ("V","V",VV_binary);
      TWO_OP ("V","S",VS_binary);
      TWO_OP ("S","M",SM_binary);
      TWO_OP ("S","V",SV_binary);
      TWO_OP ("S","S",SS_binary);      
      TWO_OP ("W","W",WW_binary);      
      TWO_OP ("W","S",WW_binary);      
      TWO_OP ("S","W",WW_binary);      
      
      if (!status) {
	sprintf (line, "syntax error: invalid operand for binary operation: %s or %s\n", stack[i-1].name, stack[i-2].name);
	push_error (line);
	clear_stack (&tmp_stack);
	return (FALSE);
      }
      move_stack (&stack[i-2], &tmp_stack);
      delete_stack (&stack[i-1], 2);
      for (j = i + 1; j < *Nstack; j++) {
	move_stack (&stack[j - 2], &stack[j]);
      }
      *Nstack -= 2;
      i -= 2;
      init_stack (&tmp_stack);
      continue;
    }

    /***** unary operators **/
    if (stack[i].type == 9) {

      if (i < 1) {  /* need one variable to operate on */
	push_error ("syntax error: unary operator with no operand");
	clear_stack (&tmp_stack);
	return (FALSE);
      }

      ONE_OP ("M", M_unary);
      ONE_OP ("V", V_unary);
      ONE_OP ("S", S_unary);

      /* there are no valid unary string operators */
      if (!strncasecmp (&stack[i - 1].type, "W", 1)) {
	push_error ("syntax error: no valid string unary ops");
	clear_stack (&tmp_stack);
	return (FALSE);
      }

      move_stack (&stack[i-1], &tmp_stack);
      delete_stack (&stack[i], 1);
      for (j = i + 1; j < *Nstack; j++) {
	move_stack (&stack[j - 1], &stack[j]);
      }
      init_stack (&tmp_stack);
      *Nstack -= 1;
      i -= 1;
      continue;
    } 
  }
  clear_stack (&tmp_stack);

  if (*Nstack > 1) {
    push_error ("syntax error in evaluation");
    return (FALSE);
  }
  return (TRUE);
}

/* copy data to new stack variable */
void copy_stack (StackVar *stack1, StackVar *stack2) {
  stack1[0].name   = stack2[0].name  ;
  stack1[0].type   = stack2[0].type  ;
  stack1[0].ptr    = stack2[0].ptr   ;
  stack1[0].buffer = stack2[0].buffer;
  stack1[0].vector = stack2[0].vector;
  stack1[0].Float  = stack2[0].Float ;
  if (!strncasecmp (&stack1[0].type, "S", 1)) {
    stack1[0].ptr    = &stack1[0].Float;
  }
}

/* replace data with new stack variable */
void move_stack (StackVar *stack1, StackVar *stack2) {
  clear_stack (stack1);
  copy_stack (stack1, stack2);
  stack2[0].name = NULL;
}

/* delete name and data */
void clean_stack (StackVar *stack, int Nstack) {

  int i;

  for (i = 0; i < Nstack; i++) {
    if (IsBufferPtr (stack[i].buffer) && (stack[i].type == 'm')) {
      if (VERBOSE) gprint (GP_ERR, "free %s (buff) (%lx)\n", stack[i].name, (long) stack[i].buffer);
      free (stack[i].buffer[0].header.buffer);
      free (stack[i].buffer[0].matrix.buffer);
      free (stack[i].buffer);
      stack[i].buffer = NULL;
    }	
    if (IsVectorPtr (stack[i].vector) && (stack[i].type == 'v')) {
      if (VERBOSE) gprint (GP_ERR, "free %s (vect) (%lx)\n", stack[i].name, (long) stack[i].vector);
      free (stack[i].vector[0].elements);
      free (stack[i].vector);
      stack[i].vector = NULL;
    }	
    if (VERBOSE) gprint (GP_ERR, "free %s (name) (%d) (%lx)\n", stack[i].name, i, (long) stack[i].name);
    clear_stack (&stack[i]);
  }

}

/* delete name only, not data */
void delete_stack (StackVar *stack, int Nstack) {
  int i;
  for (i = 0; i < Nstack; i++) {
    clear_stack (&stack[i]);
  }
}

void init_stack (StackVar *stack) {
  stack[0].buffer = NULL;
  stack[0].vector = NULL;
  stack[0].name = NULL;
}

void assign_stack (StackVar *stack, char *name, int type) {
  stack->name = strcreate (name);
  stack->type = type;
}

void clear_stack (StackVar *stack) {
  if (stack->name == NULL) return;
  // fprintf (stderr, "free %s\n", stack->name);
  free (stack->name);
  stack->name = NULL;
  return;
}
