#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include "error.h"
#include "tuple.h"
#include "predicate.h"
#include "iterator.h"
#include "scan_iterator.h"

bool_expr *make_eq_expr(int arg1, int arg2){
  bool_expr *result;
  
  result = (bool_expr *) calloc(1, sizeof(bool_expr));
  result->bool_type = EQ_EXPR;
  result->expr_body.r_expr.arg1 = arg1;
  result->expr_body.r_expr.arg2 = arg2; 
  return result;
}

bool_expr *make_and_expr(bool_expr *left, bool_expr *right){
  bool_expr *result;
  
  result = (bool_expr *) calloc(1, sizeof(bool_expr));
  result->bool_type = AND_EXPR;
  result->expr_body.l_expr.left = left;
  result->expr_body.l_expr.right = right;
  return result;
}

bool_expr *make_or_expr(bool_expr *left, bool_expr *right){
  bool_expr *result;
  
  result = (bool_expr *) calloc(1, sizeof(bool_expr));
  result->bool_type = OR_EXPR;
  result->expr_body.l_expr.left = left;
  result->expr_body.l_expr.right = right;
  return result;
}

bool_expr *make_not_expr(bool_expr *left){
  bool_expr *result;
  
  result = (bool_expr *) calloc(1, sizeof(bool_expr));
  result->bool_type = NOT_EXPR;
  result->expr_body.l_expr.left = left;
  result->expr_body.l_expr.right = NULL;
  return result;
}

bool eval_eq_expr(bool_expr *eq_expr, tuple *arg_tuple, tuple *cons){
  int arg1, arg2;
  int str_len1, str_len2;
  char *str1, *str2;
  attribute attr1, attr2;
  
  arg1 = eq_expr->expr_body.r_expr.arg1;
  arg2 = eq_expr->expr_body.r_expr.arg2;
 
  // sanity checks
  if (cons == NULL){
    // no constants are present
    assert((arg1 >= 0) && (arg1 < arg_tuple->num_attrs));
    assert((arg2 >= 0) && (arg2 < arg_tuple->num_attrs));
   
    attr1 = arg_tuple->attr_list[arg1];
    attr2 = arg_tuple->attr_list[arg2];
  }
  else {
    // some constants are present
    assert((arg1 >= 0) && (arg1 < (arg_tuple->num_attrs + cons->num_attrs)));
    assert((arg2 >= 0) && (arg2 < (arg_tuple->num_attrs + cons->num_attrs)));
   
    // GET FIRST ARGUMENT
    if (arg1 >= arg_tuple->num_attrs){
      // must be a constant, so remove the offset added by
      // having the arg_tuple->num_attrs
      arg1 -= arg_tuple->num_attrs;
      attr1 = cons->attr_list[arg1];
    }
    else {
      attr1 = arg_tuple->attr_list[arg1];
    }

    // GET SECOND ARGUMENT
    if (arg2 >= arg_tuple->num_attrs){
      // must be a constant, so remove the offset added by
      // having the arg_tuple->num_attrs
      arg2 -= arg_tuple->num_attrs;
      attr2 = cons->attr_list[arg2];
    }
    else {
      attr2 = arg_tuple->attr_list[arg2];
    }
    
  }

  //attribute& attr1 = arg_tuple->attr_list[arg1];
  //attribute& attr2 = arg_tuple->attr_list[arg2];
 
  // make sure they are comparable
  assert(attr1.attr_type == attr2.attr_type);
  
  if (attr1.attr_type == INT_TYPE){
    return attr1.attr_val.int_val == attr2.attr_val.int_val;
  }
  else if (attr1.attr_type == DOUBLE_TYPE){
    return attr1.attr_val.double_val == attr2.attr_val.double_val;
  }
  else {
    // get the string 1
    str_len1 = attr1.attr_val.char_val.len;
    str1 = attr1.attr_val.char_val.str;
    
    // get the string 2
    str_len2 = attr2.attr_val.char_val.len;
    str2 = attr2.attr_val.char_val.str;
    
    // make sure they have the same size
    assert(str_len1 == str_len2);
    
    // determine if they are the same
    return strncmp(str1, str2, str_len1) == 0;
  }
}

bool eval_and_expr(bool_expr *expr, tuple *src, tuple *cons){
  return eval_selection(expr->expr_body.l_expr.left, src, cons) &&
    eval_selection(expr->expr_body.l_expr.right, src, cons);
}

bool eval_or_expr(bool_expr *expr, tuple *src, tuple *cons){
  return eval_selection(expr->expr_body.l_expr.left, src, cons) ||
    eval_selection(expr->expr_body.l_expr.right, src, cons);
}

bool eval_not_expr(bool_expr *expr, tuple *src, tuple *cons){
  return !eval_selection(expr->expr_body.l_expr.left, src, cons);
}


