defmodule Outlook.InternalTree.InternalTree do

  alias Outlook.InternalTree.{InternalNode,TranslationUnit}

  def garnish([%TranslationUnit{} = node | rest], options) do
    [ set_attributes(node, options.tunits, options.tu_ids)
      | garnish(rest, options) ]
  end

  def garnish([%InternalNode{type: :element} = node | rest], options) do
    node = set_attributes(node, options.elements, options.el_ids, options.el_names)
    [ %InternalNode{node |
      content: garnish(node.content, options)
      } | garnish(rest, options) ]
  end

  def garnish([node | rest], options) do
    [ node | garnish(rest, options) ]
  end

  def garnish([], _), do: []


  defp set_attributes(node, atts, ids, []) do
    set_attributes(node, atts, ids)
  end

  defp set_attributes(node, atts, ids, names) do
    if node.name in names do
      set_attributes(node, atts, [])
    else
      set_attributes(node, atts, ids)
    end
  end

  defp set_attributes(node, atts, []) do
    set_attributes(node, atts)
  end

  defp set_attributes(node, atts, ids) do
    if node.nid in ids do
      set_attributes(node, atts)
    else
      node
    end
  end

  defp set_attributes(node, atts) do
    node_atts = Map.get(node, :attributes, %{})
    |> Map.merge(Map.get(node.eph, :attributes, %{}))
    attributes = Enum.map(atts, fn {k,v} ->
      nv = case is_function(v) do
          true -> v.(node)
          false -> v
        end
        {k,nv}
      end)
    |> Enum.into(node_atts)
    %{node | eph: Map.put(node.eph, :attributes, attributes)}
  end


  def collect_tunit_ids([%TranslationUnit{} = node | rest]) do
    [node.nid | collect_tunit_ids(rest)]
  end

  def collect_tunit_ids([%{type: :element} = node | rest]) do
    collect_tunit_ids(node.content) ++ collect_tunit_ids(rest)
  end

  def collect_tunit_ids([node | rest]) do
    collect_tunit_ids(rest)
  end

  def collect_tunit_ids([]), do: []
end
