require 'thread'

class PriorityQueue

  def initialize (*classes)
    @order = classes
    @queues = Hash::new
    classes.each { |c| @queues[c] = Queue::new }
    @ilimit = @order.size
    @waiting = []
  end

  def push (obj)
    Thread.critical = true
    begin
      @queues[obj.class.to_s].push obj
      t = @waiting.shift
      t.wakeup if t
    rescue ThreadError
      retry
    rescue NameError
      raise NameError, "unknown class queue"
    ensure
      Thread.critical = false
    end
    begin
      t.run if t
    rescue ThreadError
    end
  end
  alias << push
  alias enq push

  def pop(non_block=false)
    Thread.critical = true
    begin
      loop do
	i = 0
	while (i < @ilimit) and (@queues[@order[i]].empty?)
	  i = i + 1
	end
	if i == @ilimit
	  if non_block
	    raise ThreadError, "queue empty"
	  end
	  @waiting.push Thread.current
	  Thread.stop
	else
	  return @queues[@order[i]].shift
	end
      end
    ensure
      Thread.critical = false
    end
  end
  alias shift pop
  alias deq pop

  def empty?
    epty = true
    @queues.each_value { |q| epty = epty and q.empty? }
    return epty
  end

  def clear
    @queues.each_value { |q| q.clear }
  end

  def length
    l = 0
    @queues.each_value { |q| l += q.length }
    return l
  end
  
  def size
    length
  end

  def num_waiting
    @waiting.size
  end

end