require 'gir_ffi/user_defined/i_object_info'
require 'gir_ffi/user_defined/i_property_info'
require 'gir_ffi/builder/type/object'

module GirFFI
  module Builder
    module Type

      # Implements the creation of GObject subclasses from Ruby.
      class UserDefined < Object
        def initialize klass, &block
          @block = block
          @klass_before_set_up = klass
        end

        def instantiate_class
          @klass = @klass_before_set_up
          @info = GirFFI::UserDefined::IObjectInfo.new

          self.instance_eval(&@block) if @block

          parent_type = @klass.get_gtype
          @parent = gir.find_by_gtype(parent_type)

          query_result = GObject.type_query parent_type
          type_info = GObject::TypeInfo.new
          type_info.class_size = query_result.class_size
          type_info.instance_size = query_result.instance_size
          properties.each do
            type_info.instance_size += FFI.type_size(:int32)
          end

          @gtype = GObject.type_register_static(parent_type, @klass.name,
                                                type_info, 0)

          @structklass = get_or_define_class @klass, :Struct, layout_superclass
          setup_class unless already_set_up
          CACHE[@gtype] = @klass
        end

        def setup_class
          setup_layout
          setup_constants
          #stub_methods
          setup_gtype_getter
          setup_property_accessors
          #setup_vfunc_invokers
          #setup_interfaces
          setup_constructor
        end

        private

        def target_gtype
          @gtype
        end

        def parent
          @parent
        end

        def install_property pspec
          pinfo = GirFFI::UserDefined::IPropertyInfo.new
          pinfo.name = pspec.get_name
          properties << pinfo
        end

        def properties
          info.properties
        end

        def layout_specification
          parent_spec = [:parent, superclass::Struct, 0]
          offset = superclass::Struct.size
          fields_spec = properties.map do |pinfo|
            spec = [pinfo.name.to_sym, :int32, offset]
            offset += FFI.type_size(:int32)
            spec
          end.flatten(1)
          parent_spec + fields_spec
        end

        def setup_property_accessors
          properties.each do |pinfo|
            setup_accessors_for_param_info pinfo
          end
        end

        def setup_accessors_for_param_info pinfo
          code = <<-CODE
          def #{pinfo.name}
            @struct[:#{pinfo.name}]
          end
          def #{pinfo.name}= val
            @struct[:#{pinfo.name}] = val
          end
          CODE

          @klass.class_eval code
        end

        def method_introspection_data method
          nil
        end

        def setup_constructor
          code = <<-CODE
          def self.new
            gptr = GObject::Lib.g_object_newv #{@gtype}, 0, nil
            self.wrap(gptr)
          end
          CODE
          @klass.class_eval code
        end
      end
    end
  end
end

