# frozen_string_literal: true

require 'gir_ffi_test_helper'

describe GirFFI::Builders::SignalClosureBuilder do
  let(:builder) { GirFFI::Builders::SignalClosureBuilder.new signal_info }

  describe '#build_class' do
    let(:signal_info) do
      get_signal_introspection_data 'Regress', 'TestObj', 'test'
    end

    it 'builds a descendant of RubyClosure' do
      klass = builder.build_class
      klass.ancestors.must_include GObject::RubyClosure
    end
  end

  describe '#marshaller_definition' do
    describe 'for a signal with no arguments or return value' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'TestObj', 'test'
      end

      it 'returns a valid marshaller converting only the receiver' do
        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance = param_values.first.get_value_plain
          _v1 = _instance
          wrap(closure.to_ptr).invoke_block(_v1)
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal with an argument and a return value' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'TestObj', 'sig-with-int64-prop'
      end

      it 'returns a valid mapping method' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, i = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = i
          _v3 = wrap(closure.to_ptr).invoke_block(_v1, _v2)
          return_value.set_value _v3
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal with an enum argument' do
      let(:signal_info) do
        get_signal_introspection_data 'Gio', 'MountOperation', 'reply'
      end

      it 'returns a valid mapping method' do
        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, result = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = result
          wrap(closure.to_ptr).invoke_block(_v1, _v2)
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal with a array plus length arguments' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'TestObj', 'sig-with-array-len-prop'
      end

      it 'returns a valid mapping method' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, arr, len = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = len
          _v3 = GirFFI::SizedArray.wrap(:guint32, _v2, arr)
          wrap(closure.to_ptr).invoke_block(_v1, _v3)
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal with a struct argument' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'TestObj', 'test-with-static-scope-arg'
      end

      it 'returns a valid mapping method' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, object = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = Regress::TestSimpleBoxedA.wrap(object)
          wrap(closure.to_ptr).invoke_block(_v1, _v2)
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal returning an array of integers' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'TestObj', 'sig-with-intarray-ret'
      end

      it 'returns a valid mapping method' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, i = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = i
          _v3 = wrap(closure.to_ptr).invoke_block(_v1, _v2)
          _v4 = GLib::Array.from(:gint32, _v3)
          return_value.set_value _v4
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal returning an string' do
      let(:signal_info) do
        get_signal_introspection_data 'Gtk', 'Scale', 'format-value'
      end

      it 'returns a mapping method that passes the string result to return_value directly' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, value = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = value
          _v3 = wrap(closure.to_ptr).invoke_block(_v1, _v2)
          return_value.set_value _v3
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end

    describe 'for a signal with GList argument' do
      let(:signal_info) do
        get_signal_introspection_data 'Regress', 'AnnotationObject', 'list-signal'
      end

      it 'returns a valid mapping method' do
        skip unless signal_info

        expected = <<-CODE.reset_indentation
        def self.marshaller(closure, return_value, param_values, _invocation_hint, _marshal_data)
          _instance, list = param_values.map(&:get_value_plain)
          _v1 = _instance
          _v2 = GLib::List.wrap(:utf8, list)
          wrap(closure.to_ptr).invoke_block(_v1, _v2)
        end
        CODE

        builder.marshaller_definition.must_equal expected
      end
    end
  end
end
