module GLib
  load_class :PtrArray

  # Overrides for GPtrArray, GLib's automatically growing array of
  # pointers.
  class PtrArray
    include Enumerable

    attr_accessor :element_type

    POINTER_SIZE = FFI.type_size(:pointer)

    class << self
      remove_method :new
      # Remove stub generated by builder.
      remove_method :add if method_defined? :add
    end

    def self.new type
      wrap(type, Lib.g_ptr_array_new)
    end

    def self.wrap type, ptr
      super(ptr).tap {|it|
        it.element_type = type}
    end

    def self.from type, it
      case it
      when self then it
      when FFI::Pointer then wrap type, it
      else self.new(type).tap {|arr| arr.add_array it}
      end
    end

    def self.add array, data
      array.add data
    end

    def add data
      ptr = GirFFI::InPointer.from element_type, data
      Lib.g_ptr_array_add self, ptr
    end

    def add_array ary
      ary.each {|item| add item}
    end

    # Re-implementation of the g_ptr_array_index macro
    def index idx
      if idx >= length or idx < 0
        raise IndexError, "Index #{idx} outside of bounds 0..#{length - 1}"
      end
      ptr = GirFFI::InOutPointer.new element_type, @struct[:pdata] + idx * POINTER_SIZE
      ptr.to_ruby_value
    end

    def each
      length.times do |idx|
        yield index(idx)
      end
    end

    def length
      @struct[:len]
    end

    def ==(other)
      self.to_a == other.to_a
    end
  end
end
