#            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
#                    Version 2, December 2004
#
#            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
#   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
#
#  0. You just DO WHAT THE FUCK YOU WANT TO.

defmodule Data.Queue.Standard do
  alias Data.Protocol, as: P
  alias __MODULE__, as: T

  defstruct [:queue]

  def new do
    %T{queue: :queue.new()}
  end

  def new(enum_or_queue) do
    if :queue.is_queue(enum_or_queue) do
      %T{queue: enum_or_queue}
    else
      %T{queue: Data.list(enum_or_queue) |> :queue.from_list}
    end
  end

  def enq(%T{queue: queue}, value) do
    %T{queue: :queue.in(value, queue)}
  end

  def reverse_enq(%T{queue: queue}, value) do
    %T{queue: :queue.in_r(value, queue)}
  end

  def deq(%T{queue: queue}) do
    case :queue.out(queue) do
      { :empty, queue } ->
        { :empty, %T{queue: queue} }

      { value, queue } ->
        { value, %T{queue: queue} }
    end
  end

  def reverse_deq(%T{queue: queue}) do
    case :queue.out_r(queue) do
      { :empty, queue } ->
        { :empty, %T{queue: queue} }

      { value, queue } ->
        { value, %T{queue: queue} }
    end
  end

  def peek(%T{queue: queue}) do
    :queue.peek(queue)
  end

  def reverse_peek(%T{queue: queue}) do
    :queue.peek_r(queue)
  end

  def reverse(%T{queue: queue}) do
    %T{queue: :queue.reverse(queue)}
  end

  def empty?(%T{queue: queue}) do
    :queue.is_empty(queue)
  end

  def clear(_) do
    new()
  end

  def member?(%T{queue: queue}, value) do
    :queue.member(value, queue)
  end

  def size(%T{queue: queue}) do
    :queue.len(queue)
  end

  def foldl(%T{queue: queue}, acc, fun) do
    List.foldl(:queue.to_list(queue), acc, fun)
  end

  def foldr(%T{queue: queue}, acc, fun) do
    List.foldr(:queue.to_list(queue), acc, fun)
  end

  def to_list(%T{queue: queue}) do
    :queue.to_list(queue)
  end

  defimpl P.Queue do
    defdelegate enq(self, value), to: T
    defdelegate deq(self), to: T
  end

  defimpl P.Count do
    defdelegate count(self), to: T, as: :size
  end

  defimpl P.Peek do
    defdelegate peek(self), to: T
  end

  defimpl P.Sequence do
    def first(self) do
      Data.peek(self)
    end

    def next(self) do
      if T.size(self) > 1 do
        { _, next } = T.deq(self)

        next
      end
    end
  end

  defimpl P.Reverse do
    defdelegate reverse(self), to: T
  end

  defimpl P.Empty do
    defdelegate empty?(self), to: T
    defdelegate clear(self), to: T
  end

  defimpl P.Reduce do
    defdelegate reduce(self, acc, fun), to: T, as: :foldl
  end

  defimpl P.ToList do
    defdelegate to_list(self), to: T
  end

  defimpl P.Stack do
    defdelegate push(self, value), to: T, as: :reverse_enq
    defdelegate pop(self), to: T, as: :reverse_deq
  end

  defimpl P.Contains do
    defdelegate contains?(self, key), to: T, as: :member?
  end

  defimpl P.Into do
    def into(self, value) do
      self |> T.enq(value)
    end
  end

  defimpl Enumerable do
    use Data.Enumerable
  end

  defimpl Collectable do
    use Data.Collectable
  end

  defimpl Inspect do
    import Inspect.Algebra

    def inspect(queue, opts) do
      concat ["#Queue<", to_doc(T.to_list(queue), opts), ">"]
    end
  end
end
