defmodule Blanks do
  def replace(ast, replacements) do
    replacements = List.wrap(replacements)
    ast
    |> Macro.prewalk(replacements, &pre/2)
    |> elem(0)
  end

  defp pre({:assert_receive, _, args} = node, replacements) do
    {args, replacements} = Macro.prewalk(args, replacements, &pre_pin/2)
    {put_elem(node, 2, args), replacements}
  end
  defp pre({:___, _, _}, [first | remainder]), do: {first, remainder}
  defp pre(node, acc), do: {node, acc}

  defp pre_pin({:___, _, _}, [first | remainder]), do: {pin(first), remainder}
  defp pre_pin(node, acc), do: {node, acc}

  defp pin(var) when is_tuple(var) do
    quote do
      ^unquote(var)
    end
  end
  defp pin(var), do: var

  def count(ast) do
    ast
    |> Macro.prewalk(0, &count/2)
    |> elem(1)
  end

  defp count({:___, _, _} = node, acc), do: {node, acc+1}
  defp count(node, acc), do: {node, acc}

  def replace_line({:__block__, meta, lines}, replacement_fn) do
    replaced_lines = Enum.map(lines, fn(line) ->
      replace_line(line, replacement_fn)
    end)

    {:__block__, meta, replaced_lines}
  end
  def replace_line(line, replacement_fn) do
    if count(line) > 0 do
      replacement_fn.(line)
    else
      line
    end
  end
end
