module RuboCop::Cop::Layout::EmptyLinesAroundBody

def check(node, body, adjusted_first_line: nil)

def check(node, body, adjusted_first_line: nil)
  return if valid_body_style?(body)
  return if node.single_line?
  first_line = adjusted_first_line || node.source_range.first_line
  last_line = node.source_range.last_line
  case style
  when :empty_lines_except_namespace
    check_empty_lines_except_namespace(body, first_line, last_line)
  when :empty_lines_special
    check_empty_lines_special(body, first_line, last_line)
  else
    check_both(style, first_line, last_line)
  end
end

def check_beginning(style, first_line)

def check_beginning(style, first_line)
  check_source(style, first_line, 'beginning')
end

def check_both(style, first_line, last_line)

def check_both(style, first_line, last_line)
  case style
  when :beginning_only
    check_beginning(:empty_lines, first_line)
    check_ending(:no_empty_lines, last_line)
  when :ending_only
    check_beginning(:no_empty_lines, first_line)
    check_ending(:empty_lines, last_line)
  else
    check_beginning(style, first_line)
    check_ending(style, last_line)
  end
end

def check_deferred_empty_line(body)

def check_deferred_empty_line(body)
  node = first_empty_line_required_child(body)
  return unless node
  line = previous_line_ignoring_comments(node.first_line)
  return if processed_source[line].empty?
  range = source_range(processed_source.buffer, line + 2, 0)
  add_offense(
    [:empty_lines, range],
    location: range,
    message: deferred_message(node)
  )
end

def check_empty_lines_except_namespace(body, first_line, last_line)

def check_empty_lines_except_namespace(body, first_line, last_line)
  if namespace?(body, with_one_child: true)
    check_both(:no_empty_lines, first_line, last_line)
  else
    check_both(:empty_lines, first_line, last_line)
  end
end

def check_empty_lines_special(body, first_line, last_line)

def check_empty_lines_special(body, first_line, last_line)
  return unless body
  if namespace?(body, with_one_child: true)
    check_both(:no_empty_lines, first_line, last_line)
  else
    if first_child_requires_empty_line?(body)
      check_beginning(:empty_lines, first_line)
    else
      check_beginning(:no_empty_lines, first_line)
      check_deferred_empty_line(body)
    end
    check_ending(:empty_lines, last_line)
  end
end

def check_ending(style, last_line)

def check_ending(style, last_line)
  check_source(style, last_line - 2, 'end')
end

def check_line(style, line, msg)

def check_line(style, line, msg)
  return unless yield(processed_source.lines[line])
  offset = style == :empty_lines && msg.include?('end.') ? 2 : 1
  range = source_range(processed_source.buffer, line + offset, 0)
  add_offense([style, range], location: range, message: msg)
end

def check_source(style, line_no, desc)

def check_source(style, line_no, desc)
  case style
  when :no_empty_lines
    check_line(style, line_no, message(MSG_EXTRA, desc), &:empty?)
  when :empty_lines
    check_line(style, line_no, message(MSG_MISSING, desc)) do |line|
      !line.empty?
    end
  end
end

def deferred_message(node)

def deferred_message(node)
  format(MSG_DEFERRED, type: node.type)
end

def first_child_requires_empty_line?(body)

def first_child_requires_empty_line?(body)
  if body.begin_type?
    empty_line_required?(body.children.first)
  else
    empty_line_required?(body)
  end
end

def first_empty_line_required_child(body)

def first_empty_line_required_child(body)
  if body.begin_type?
    body.children.find { |child| empty_line_required?(child) }
  elsif empty_line_required?(body)
    body
  end
end

def message(type, desc)

def message(type, desc)
  format(type, kind: self.class::KIND, location: desc)
end

def namespace?(body, with_one_child: false)

def namespace?(body, with_one_child: false)
  if body.begin_type?
    return false if with_one_child
    body.children.all? { |child| constant_definition?(child) }
  else
    constant_definition?(body)
  end
end

def previous_line_ignoring_comments(send_line)

def previous_line_ignoring_comments(send_line)
  (send_line - 2).downto(0) do |line|
    return line unless comment_line?(processed_source[line])
  end
  0
end

def valid_body_style?(body)

def valid_body_style?(body)
  # When style is `empty_lines`, if the body is empty, we don't enforce
  # the presence OR absence of an empty line
  # But if style is `no_empty_lines`, there must not be an empty line
  body.nil? && style != :no_empty_lines
end