   - `acc_enc_hdr`, the HTTP header `Accept-Encoding`
   - `encs`, the list of supported encoding
   """
-  @spec choose_encoding([String.t],String.t) :: String.t | nil
-  def choose_encoding(encs,acc_enc_hdr), do:
-    choose(encs,acc_enc_hdr,"identity")
+  @spec choose_encoding([String.t], String.t) :: String.t | nil
+  def choose_encoding(encs, acc_enc_hdr) do
+    choose(encs, acc_enc_hdr, "identity")
+  end
 
   @doc """
   HTTP Charset negociation, get the charset to use from : 

  - `acc_char_hdr`, the HTTP header `Accept-Charset`
  - `charsets`, the list of supported charsets
  """
  @spec choose_charset([String.t],String.t) :: String.t | nil
  def choose_charset(charsets,acc_char_hdr), do:
    choose(charsets,acc_char_hdr,"utf8")

  defp choose(choices, header, default) do
    ## sorted set of {prio,value}
    prios = prioritized_values(header)

    # determine if default is ok or any is ok if no match
    default_prio = Enum.find_value(prios, fn {prio, v} -> v == default && prio end)
    start_prio = Enum.find_value(prios, fn {prio, v} -> v == "*" && prio end)
    default_ok = case default_prio do
		   nil -> start_prio !== 0.0
		   0.0 -> false
		   _ -> true
		 end
    any_ok = not start_prio in [nil, 0.0]

    # remove choices where prio == 0.0
    {zero_prios, prios} = Enum.partition(prios, fn {prio, _} -> prio == 0.0 end)
    choices_to_remove = Enum.map(zero_prios, &elem(&1, 1))
    choices = Enum.filter(choices, &!(String.downcase(&1) in choices_to_remove))

    if choices !== [] do
      # find first match, if not found and any_ok, then first choice, else if default_ok, take it
      Enum.find_value(prios, fn {_, val} ->
        Enum.find(choices, &(val == String.downcase(&1)))
      end)
      || (any_ok && hd(choices)
	|| (default_ok && Enum.find(choices, &(&1 == default))
	  || nil))
    else
      # No proposed choice is acceptable by client
      nil
    end
  end

  defp prioritized_values(header) do
    header 
    |> Plug.Conn.Utils.list
    |> Enum.map(fn e->
        {q,v} = case String.split(e,~r"\s;\s", parts: 2) do
          [value,params] ->
             case Float.parse(Plug.Conn.Utils.params(params)["q"] || "1.0") do
               {q,_}->{q,value}
               :error -> {1.0,value}
             end
          [value] -> {1.0,value}
        end
        {q,String.downcase(v)}
      end)
    |> Enum.sort
    |> Enum.reverse
  end

  @doc "get HTTP status label from HTTP code"
  @spec http_label(code :: integer) :: String.t
  def http_label(100), do: "Continue"
  def http_label(101), do: "Switching Protocol"

  def http_label(503), do: "Service Unavailable"
  def http_label(504), do: "Gateway Timeout"
  def http_label(505), do: "HTTP Version Not Supported"

  ###
  ### Priv
  ###
  alias Ewebmachine.Compat
  
  defp choose(choices, header, default) do
    ## sorted set of {prio,value}
    prios = prioritized_values(header)

    # determine if default is ok or any is ok if no match
    default_prio = Enum.find_value(prios, fn {prio, v} -> v == default && prio end)
    start_prio = Enum.find_value(prios, fn {prio, v} -> v == "*" && prio end)
    default_ok = case default_prio do
		   nil -> start_prio !== 0.0
		   0.0 -> false
		   _ -> true
		 end
    any_ok = not start_prio in [nil, 0.0]

    # remove choices where prio == 0.0
    {zero_prios, prios} = Compat.Enum.split_with(prios, fn {prio, _} -> prio == 0.0 end)
    choices_to_remove = Enum.map(zero_prios, &elem(&1, 1))
    choices = Enum.filter(choices, &!(String.downcase(&1) in choices_to_remove))

    if choices !== [] do
      # find first match, if not found and any_ok, then first choice, else if default_ok, take it
      Enum.find_value(prios, fn {_, val} ->
        Enum.find(choices, &(val == String.downcase(&1)))
      end)
      || (any_ok && hd(choices)
	|| (default_ok && Enum.find(choices, &(&1 == default))
	  || nil))
    else
      # No proposed choice is acceptable by client
      nil
    end
  end

  defp prioritized_values(header) do
    header 
    |> Plug.Conn.Utils.list
    |> Enum.map(fn e->
        {q,v} = case String.split(e,~r"\s;\s", parts: 2) do
          [value,params] ->
             case Float.parse(Plug.Conn.Utils.params(params)["q"] || "1.0") do
               {q,_}->{q,value}
               :error -> {1.0,value}
             end
          [value] -> {1.0,value}
        end
        {q,String.downcase(v)}
      end)
    |> Enum.sort
    |> Enum.reverse
  end
end
