module Concurrent

  # @!visibility private
  class LockFreeStack < Synchronization::Object

    safe_initialization!

    class Node
      # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?

      attr_reader :value, :next_node
      # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
      attr_writer :value

      def initialize(value, next_node)
        @value     = value
        @next_node = next_node
      end

      singleton_class.send :alias_method, :[], :new
    end

    class Empty < Node
      def next_node
        self
      end
    end

    EMPTY = Empty[nil, nil]

    private(*attr_atomic(:head))

    def self.of1(value)
      new Node[value, EMPTY]
    end

    def self.of2(value1, value2)
      new Node[value1, Node[value2, EMPTY]]
    end

    def initialize(head = EMPTY)
      super()
      self.head = head
    end

    def empty?(head = self.head)
      head.equal? EMPTY
    end

    def compare_and_push(head, value)
      compare_and_set_head head, Node[value, head]
    end

    def push(value)
      while true
        current_head = head
        return self if compare_and_set_head current_head, Node[value, current_head]
      end
    end

    def peek
      head
    end

    def compare_and_pop(head)
      compare_and_set_head head, head.next_node
    end

    def pop
      while true
        current_head = head
        return current_head.value if compare_and_set_head current_head, current_head.next_node
      end
    end

    def compare_and_clear(head)
      compare_and_set_head head, EMPTY
    end

    include Enumerable

    def each(head = nil)
      return to_enum(:each, head) unless block_given?
      it = head || peek
      until it.equal?(EMPTY)
        yield it.value
        it = it.next_node
      end
      self
    end

    def clear
      while true
        current_head = head
        return false if current_head == EMPTY
        return true if compare_and_set_head current_head, EMPTY
      end
    end

    def clear_if(head)
      compare_and_set_head head, EMPTY
    end

    def replace_if(head, new_head)
      compare_and_set_head head, new_head
    end

    def clear_each(&block)
      while true
        current_head = head
        return self if current_head == EMPTY
        if compare_and_set_head current_head, EMPTY
          each current_head, &block
          return self
        end
      end
    end

    # @return [String] Short string representation.
    def to_s
      format '<#%s:0x%x %s>', self.class, object_id << 1, to_a.to_s
    end

    alias_method :inspect, :to_s
  end
end
