# frozen_string_literal: true

require 'gir_ffi_test_helper'

describe GirFFI::Builders::CallbackBuilder do
  let(:builder) { GirFFI::Builders::CallbackBuilder.new callback_info }

  describe '#mapping_method_definition' do
    describe 'for a callback with arguments and return value' do
      let(:callback_info) { get_introspection_data 'Regress', 'TestCallbackFull' }
      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc, foo, bar, path)
          _v1 = foo
          _v2 = bar
          _v3 = path.to_utf8
          _v4 = _proc.call(_v1, _v2, _v3)
          return _v4
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end

    describe 'for a callback with no arguments or return value' do
      let(:callback_info) { get_introspection_data 'Regress', 'TestSimpleCallback' }
      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc)
          _proc.call()
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end

    describe 'for a callback with a closure argument' do
      let(:callback_info) { get_introspection_data 'Regress', 'TestCallbackUserData' }
      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc, user_data)
          _v1 = GirFFI::ArgHelper::OBJECT_STORE.fetch(user_data)
          _v2 = _proc.call(_v1)
          return _v2
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end

    describe 'for a callback with one out argument' do
      let(:callback_info) do
        get_introspection_data('GIMarshallingTests',
                               'CallbackOneOutParameter')
      end
      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc, a)
          _v1 = a
          _v2 = _proc.call()
          _v1.put_float 0, _v2
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end

    describe 'for a callback with an inout array argument' do
      let(:callback_info) do
        get_introspection_data('Regress',
                               'TestCallbackArrayInOut')
      end
      it 'returns a valid mapping method' do
        skip unless callback_info
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc, ints, length)
          _v1 = length
          _v2 = _v1.get_int32(0)
          _v3 = ints
          _v4 = GirFFI::SizedArray.wrap(:gint32, _v2, _v3.get_pointer(0))
          _v5 = _proc.call(_v4)
          _v1.put_int32 0, _v5.length
          _v3.put_pointer 0, GirFFI::SizedArray.from(:gint32, -1, _v5)
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end

    describe 'for a callback with a value in argument' do
      let(:field_info) do
        get_field_introspection_data('GObject',
                                     'ObjectClass', 'get_property')
      end
      let(:callback_info) do
        field_info.field_type.interface
      end
      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.call_with_argument_mapping(_proc, object, property_id, value, pspec)
          _v1 = GObject::Object.wrap(object)
          _v2 = property_id
          _v3 = GObject::Value.wrap(value)
          _v4 = GObject::ParamSpec.wrap(pspec)
          _proc.call(_v1, _v2, _v3, _v4)
        end
        CODE

        builder.mapping_method_definition.must_equal expected
      end
    end
  end
end
