defmodule Slime.Parser.TextBlock do
  @moduledoc "
  Utilities for parsing text blocks.
  "

  @doc """
  Given a text block and its declaration indentation level (see below),
  produces a string (or a dynamic EEx tuple) that can be inserted into the tree.

  nested
    | Text block
       that spans over multiple lines
  ---
   ^
  declaration indent
  """
  def render_content(lines, declaration_indent) do
    lines =
      case lines do
        [{_, []} | rest] ->
          normalize_indent(rest)

        [first_line | rest] ->
          [first_line | normalize_indent(rest, declaration_indent)]
      end

    insert_line_spacing(lines)
  end

  @doc """
  Given a text block, returns the text without indentation.
  """
  def render_without_indentation(lines) do
    concat_lines(
      lines,
      fn {_line_indent, line_contents}, content ->
        ["\n" | line_contents ++ content]
      end
    )
  end

  defp normalize_indent([]), do: []

  defp normalize_indent(lines) do
    indents =
      lines
      |> Enum.map(fn {indent, _} -> indent end)
      |> Enum.filter(&(&1 != 0))

    min_indent =
      case indents do
        [] -> 0
        _ -> Enum.min(indents)
      end

    {leading, rest} = Enum.split_while(lines, fn {indent, _} -> indent != min_indent end)

    result =
      leading
      |> Enum.map(fn {_, content} -> {min_indent, content} end)
      |> Enum.concat(rest)

    normalize_indent(result, min_indent - 1)
  end

  defp normalize_indent(lines, declaration_indent) do
    Enum.map(lines, fn {indent, content} ->
      {indent - declaration_indent, content}
    end)
  end

  defp insert_line_spacing(lines) do
    concat_lines(
      lines,
      fn {line_indent, line_contents}, content ->
        if 1 < line_indent do
          ["\n" | [String.duplicate(" ", line_indent - 1) | line_contents ++ content]]
        else
          ["\n" | line_contents ++ content]
        end
      end
    )
  end

  defp concat_lines([], _), do: []

  defp concat_lines(lines, concat_function) do
    [_leading_newline | content] = List.foldr(lines, [], concat_function)
    content
  end
end
