# frozen_string_literal: true

require "gir_ffi_test_helper"

GirFFI.setup :GIMarshallingTests

describe GirFFI::StructLikeBase do
  describe "wrap_copy" do
    it "returns a wrapped owned copy of structs" do
      original = GIMarshallingTests::SimpleStruct.new
      copy = GIMarshallingTests::SimpleStruct.wrap_copy(original.to_ptr)
      _(copy.to_ptr).wont_be :==, original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).must_be :owned?
    end

    it "returns a wrapped owned copy of unions" do
      original = GIMarshallingTests::Union.new
      copy = GIMarshallingTests::Union.wrap_copy(original.to_ptr)
      _(copy.to_ptr).wont_be :==, original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).must_be :owned?
    end

    it "returns a wrapped owned copy of boxed types" do
      original = GIMarshallingTests::BoxedStruct.new
      copy = GIMarshallingTests::BoxedStruct.wrap_copy(original.to_ptr)
      ptr = copy.to_ptr
      _(ptr).wont_be :==, original.to_ptr
      _(ptr).wont_be :autorelease? if ptr.respond_to? :autorelease
      _(copy.struct).must_be :owned?
    end

    it "returns nil when passed nil" do
      _(GIMarshallingTests::SimpleStruct.wrap_copy(nil)).must_be_nil
    end
  end

  describe "copy_from" do
    it "returns an unowned copy of unions" do
      original = GIMarshallingTests::Union.new
      original.long_ = 42
      copy = GIMarshallingTests::Union.copy_from(original)
      _(copy.long_).must_equal 42
      _(copy.to_ptr).wont_be :==, original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).wont_be :owned?
    end

    it "returns an unowned copy of structs" do
      original = GIMarshallingTests::SimpleStruct.new
      copy = GIMarshallingTests::SimpleStruct.copy_from(original)
      _(copy.to_ptr).wont_be :==, original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).wont_be :owned?
    end

    it "returns nil when passed nil" do
      _(GIMarshallingTests::SimpleStruct.copy_from(nil)).must_be_nil
    end

    it "converts its argument if that is possible" do
      _(GObject::Value.copy_from(4)).must_be_instance_of GObject::Value
    end
  end

  describe "wrap_own" do
    it "wraps and owns the supplied pointer for structs" do
      original = GIMarshallingTests::SimpleStruct.new
      original.struct.owned = false

      copy = GIMarshallingTests::SimpleStruct.wrap_own(original.to_ptr)
      _(copy.to_ptr).must_equal original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).must_be :owned?
    end

    it "wraps and owns the supplied pointer for unions" do
      original = GIMarshallingTests::Union.new
      original.struct.owned = false

      copy = GIMarshallingTests::Union.wrap_own(original.to_ptr)
      _(copy.to_ptr).must_equal original.to_ptr
      _(copy.to_ptr).wont_be :autorelease?
      _(copy.struct).must_be :owned?
    end

    it "returns nil when passed nil" do
      _(GIMarshallingTests::SimpleStruct.wrap_own(nil)).must_be_nil
    end
  end

  describe ".native_type" do
    let(:type) { GIMarshallingTests::SimpleStruct.native_type }

    it "is a struct by value" do
      _(type).must_be_instance_of FFI::StructByValue
    end

    it "wraps the types nested struct class" do
      _(type.struct_class).must_equal GIMarshallingTests::SimpleStruct::Struct
    end
  end

  describe ".to_native" do
    it "returns the supplied value's struct" do
      object = GIMarshallingTests::SimpleStruct.new
      result = object.class.to_native(object, "some-context")
      _(result).must_equal object.struct
    end
  end

  describe ".to_ffi_type" do
    it "returns the class itself" do
      struct_class = GIMarshallingTests::SimpleStruct
      ffi_type = struct_class.to_ffi_type
      _(ffi_type).must_equal struct_class
    end
  end

  describe ".get_value_from_pointer" do
    let(:struct_class) { GIMarshallingTests::SimpleStruct }

    it "returns just a pointer" do
      object = struct_class.new
      ptr = object.to_ptr
      result = struct_class.get_value_from_pointer(ptr, 0)
      _(result).must_be :==, ptr
    end

    it "uses offset correctly" do
      struct1 = struct_class.new.tap { |it| it.long_ = 42 }
      struct2 = struct_class.new.tap { |it| it.long_ = 24 }
      array_ptr = GirFFI::InPointer.from_array(struct_class, [struct1, struct2])
      ptr = struct_class.get_value_from_pointer(array_ptr, struct_class.size)
      result = struct_class.wrap(ptr)
      _(result.long_).must_equal 24
    end
  end

  describe ".copy_value_to_pointer" do
    let(:struct_class) { GIMarshallingTests::SimpleStruct }
    let(:struct) { struct_class.new }

    it "copies data correctly" do
      struct.long_ = 42
      target = FFI::MemoryPointer.new struct_class.size
      struct_class.copy_value_to_pointer(struct, target)
      result = struct_class.wrap(target)
      _(result.long_).must_equal 42
    end

    it "uses offset correctly" do
      struct.long_ = 42
      target = FFI::MemoryPointer.new struct_class.size + 10
      struct_class.copy_value_to_pointer(struct, target, 10)
      result = struct_class.wrap(target + 10)
      _(result.long_).must_equal 42
    end
  end

  it "adds its class methods to classes that include it" do
    struct_class = Class.new
    struct_class.send :include, GirFFI::StructLikeBase
    _(struct_class.singleton_class).must_include GirFFI::StructLikeBase::ClassMethods
  end
end
