class Opal::Nodes::IfNode

def compile

def compile
  if should_compile_as_simple_expression?
    if true_body == s(:true)
      compile_with_binary_or
    elsif false_body == s(:false)
      compile_with_binary_and
    else
      compile_with_ternary
    end
  elsif could_become_switch?
    compile_with_switch
  else
    compile_with_if
  end
end

def compile_switch_case(test)

def compile_switch_case(test)
  line "case ", expr(test), ":"
  if @switch_additional_rules
    @switch_additional_rules.each do |rule|
      line "case ", expr(rule), ":"
    end
  end
  indent do
    line stmt(true_body)
    line "break;" if !true_body || !returning?(true_body)
  end
  if false_body
    if false_body.meta[:switch_default]
      compile_switch_default
    elsif false_body.meta[:switch_child]
      push stmt(false_body)
    end
  else
    push stmt(s(:nil))
  end
end

def compile_switch_default

def compile_switch_default
  line "default:"
  indent do
    line stmt(false_body)
  end
end

def compile_with_binary_and

def compile_with_binary_and
  if sexp.meta[:do_js_truthy_on_true_body]
    truthy = js_truthy(true_body || s(:nil))
  else
    truthy = expr(true_body || s(:nil))
  end
  push '('
  push js_truthy(test), ' && '
  push '(', truthy, ')'
  push ')'
end

def compile_with_binary_or

def compile_with_binary_or
  if sexp.meta[:do_js_truthy_on_false_body]
    falsy = js_truthy(false_body || s(:nil))
  else
    falsy = expr(false_body || s(:nil))
  end
  push '('
  push js_truthy(test), ' || '
  push '(', falsy, ')'
  push ')'
end

def compile_with_if

def compile_with_if
  push_closure if expects_expression?
  truthy = self.truthy
  falsy = self.falsy
  if falsy && !truthy
    # Let's optimize a little bit `unless` calls.
    push 'if (!', js_truthy(test), ') {'
    falsy, truthy = truthy, falsy
  else
    push 'if (', js_truthy(test), ') {'
  end
  # skip if-body if no truthy sexp
  indent { line stmt(truthy) } if truthy
  if falsy
    if falsy.type == :if
      line '} else ', stmt(falsy)
    else
      line '} else {'
      indent do
        line stmt(falsy)
      end
      line '}'
    end
  else
    line '}'
    # This resolution isn't finite. Let's ensure this block
    # always return something if we expect a return
    line 'return nil;' if expects_expression?
  end
  pop_closure if expects_expression?
  if expects_expression?
    return_kw = 'return ' if returning_if?
    if scope.await_encountered
      wrap "#{return_kw}(await (async function() {", '})())'
    else
      wrap "#{return_kw}(function() {", '})()'
    end
  end
end

def compile_with_switch

def compile_with_switch
  if sexp.meta[:switch_child]
    @switch_variable = sexp.meta[:switch_variable]
    @switch_additional_rules = sexp.meta[:switch_additional_rules]
    compile_switch_case(sexp.meta[:switch_test])
  else
    line "switch (", expr(@switch_first_test), ".valueOf()) {"
    indent do
      compile_switch_case(@switch_test)
    end
    line "}"
  end
end

def compile_with_ternary

def compile_with_ternary
  truthy = true_body
  falsy = false_body
  push '('
  push js_truthy(test), ' ? '
  push '(', expr(truthy || s(:nil)), ') : '
  if !falsy || falsy.type == :if
    push expr(falsy || s(:nil))
  else
    push '(', expr(falsy || s(:nil)), ')'
  end
  push ')'
end

def could_become_switch?

def could_become_switch?
  return false if expects_expression?
  return true if sexp.meta[:switch_child]
  test_match = SWITCH_TEST_MATCH.match(test) || SWITCH_TEST_MATCH_CONTINUED.match(test)
  return false unless test_match
  @switch_test, @switch_variable, @switch_first_test, additional_rules = *test_match
  additional_rules = handle_additional_switch_rules(additional_rules)
  return false unless additional_rules # It's ok for them to be empty, but false denotes a mismatch
  @switch_additional_rules = additional_rules
  return false unless valid_switch_body?(true_body)
  could_become_switch_branch?(false_body)
end

def could_become_switch_branch?(body)

def could_become_switch_branch?(body)
  if !body
    return true
  elsif body.type != :if
    if valid_switch_body?(body)
      body.meta[:switch_default] = true
      return true
    end
    return false
  end
  test, true_body, false_body = *body
  test_match = SWITCH_BRANCH_TEST_MATCH.match(test) || SWITCH_BRANCH_TEST_MATCH_CONTINUED.match(test)
  unless test_match
    if valid_switch_body?(body, true)
      body.meta[:switch_default] = true
      return true
    end
  end
  switch_test, switch_variable, additional_rules = *test_match
  switch_additional_rules = handle_additional_switch_rules(additional_rules)
  return false unless switch_additional_rules # It's ok for them to be empty, but false denotes a mismatch
  return false unless switch_variable == @switch_variable
  return false unless valid_switch_body?(true_body)
  return false unless could_become_switch_branch?(false_body)
  body.meta.merge!(switch_child: true,
                   switch_test: switch_test,
                   switch_variable: @switch_variable,
                   switch_additional_rules: switch_additional_rules
  )
  true
end

def expects_expression?

def expects_expression?
  expr? || recv?
end

def falsy

def falsy
  returnify(false_body)
end

def handle_additional_switch_rules(additional_rules)

def handle_additional_switch_rules(additional_rules)
  switch_additional_rules = []
  while additional_rules
    match = SWITCH_BRANCH_TEST_MATCH.match(additional_rules) || SWITCH_BRANCH_TEST_MATCH_CONTINUED.match(additional_rules)
    return false unless match
    switch_test, switch_variable, additional_rules = *match
    return false unless switch_variable == @switch_variable
    switch_additional_rules << switch_test
  end
  switch_additional_rules
end

def returnify(body)

def returnify(body)
  if expects_expression? && body
    compiler.returns(body)
  else
    body
  end
end

def returning?(body)

def returning?(body)
  %i[return js_return next].include?(body.type) ||
    (body.type == :begin && %i[return js_return next].include?(body.children.last.type))
end

def returning_if?

def returning_if?
  @sexp.meta[:returning]
end

def should_compile_as_simple_expression?

even?
a ternary operator instead and possibly a binary operator
This addition tries to make a few cases compiled with
it. This produced an ugly code that was hard to minify.
expected an expression from if, we always had to closure
There was a particular case in the past, that when we
def should_compile_as_simple_expression?
  expects_expression? && simple?(true_body) && simple?(false_body)
end

def simple?(body)

Let's ensure there are no control flow statements inside.
def simple?(body)
  case body
  when AST::Node
    case body.type
    when :return, :js_return, :break, :next, :redo, :retry
      false
    when :xstr
      XStringNode.single_line?(
        XStringNode.strip_empty_children(body.children)
      )
    else
      body.children.all? { |i| simple?(i) }
    end
  else
    true
  end
end

def truthy

def truthy
  returnify(true_body)
end

def valid_switch_body?(body, check_variable = false)

def valid_switch_body?(body, check_variable = false)
  case body
  when AST::Node
    case body.type
    when :break, :redo, :retry
      false
    when :iter, :while
      # Don't traverse the iters or whiles!
      true
    else
      body.children.all? { |i| valid_switch_body?(i, check_variable) }
    end
  when @switch_variable
    # Perhaps we ended abruptly and we lack a $ret_or variable... but sometimes
    # we can ignore this.
    !check_variable
  else
    true
  end
end