///|
fn eval_and(call : EvalCall) -> FormulaValue {
  if call.values.length() == 0 || call.values.length() > 30 {
    Error(formula_error_value)
  } else {
    let mut result = true
    for value in call.values {
      match value {
        Empty => ()
        Error(err) => return Error(err)
        Number(num) => result = result && num != 0.0
        Bool(flag) => result = result && flag
        String(text) =>
          if text == "TRUE" {
            ()
          } else if text == "FALSE" {
            return Bool(false)
          } else {
            return Error(formula_error_value)
          }
        List(_) => return Error(formula_error_value)
      }
    }
    Bool(result)
  }
}

///|
fn eval_or(call : EvalCall) -> FormulaValue {
  if call.values.length() == 0 || call.values.length() > 30 {
    Error(formula_error_value)
  } else {
    for value in flatten_values(call.values) {
      match normalize_scalar(value) {
        Error(err) => return Error(err)
        Number(num) => if num != 0.0 { return Bool(true) }
        Bool(flag) => if flag { return Bool(true) }
        String(text) =>
          if text == "FALSE" {
            ()
          } else if text == "TRUE" {
            return Bool(true)
          } else {
            return Error(formula_error_value)
          }
        Empty | List(_) => ()
      }
    }
    Bool(false)
  }
}

///|
fn eval_not(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0] =>
      match normalize_scalar(v0) {
        Error(err) => Error(err)
        Bool(flag) => Bool(!flag)
        Number(num) => Bool(!(num != 0.0))
        String(text) =>
          if text.to_upper() == "TRUE" {
            Bool(false)
          } else if text.to_upper() == "FALSE" {
            Bool(true)
          } else {
            Error(formula_error_value)
          }
        _ => Error(formula_error_value)
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_xor(call : EvalCall) -> FormulaValue {
  if call.values.length() == 0 {
    Error(formula_error_value)
  } else {
    let mut count = 0
    let mut ok = false
    for value in flatten_values(call.values) {
      match normalize_scalar(value) {
        Error(err) => return Error(err)
        Number(num) => {
          ok = true
          if num != 0.0 {
            count = count + 1
          }
        }
        Bool(flag) => {
          ok = true
          if flag {
            count = count + 1
          }
        }
        String(_) => ()
        Empty | List(_) => ()
      }
    }
    if !ok {
      Error(formula_error_value)
    } else {
      Bool(count % 2 != 0)
    }
  }
}

///|
fn eval_if(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0] =>
      match value_as_bool(v0) {
        Ok(cond) => Bool(cond)
        Err(err) => err
      }
    [v0, v1] =>
      match value_as_bool(v0) {
        Ok(cond) => if cond { v1 } else { Bool(false) }
        Err(err) => err
      }
    [v0, v1, v2] =>
      match value_as_bool(v0) {
        Ok(cond) => if cond { v1 } else { v2 }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_iferror(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match normalize_scalar(v0) {
        Error(_) => v1
        Empty => Number(0.0)
        _ => v0
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_ifna(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match normalize_scalar(v0) {
        Error(err) => if err == formula_error_na { v1 } else { v0 }
        _ => v0
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_ifs(call : EvalCall) -> FormulaValue {
  if call.values.length() < 2 || call.values.length() % 2 != 0 {
    Error(formula_error_value)
  } else {
    for i in 0..<(call.values.length() / 2) {
      match value_as_bool(call.values[i * 2]) {
        Ok(flag) => if flag { return call.values[i * 2 + 1] }
        Err(err) => return err
      }
    }
    Error(formula_error_na)
  }
}

///|
fn eval_switch(call : EvalCall) -> FormulaValue {
  if call.values.length() < 3 {
    Error(formula_error_value)
  } else {
    let target = call.values[0]
    let arg_count = call.values.length() - 1
    let switch_count = arg_count / 2
    let has_default = arg_count % 2 != 0
    let mut result = if has_default {
      call.values[call.values.length() - 1]
    } else {
      Error(formula_error_na)
    }
    for i in 0.. {
          result = call.values[1 + i * 2 + 1]
          break
        }
        Ok(_) => ()
        Err(err) => return err
      }
    }
    result
  }
}