class FFI::StructLayoutBuilder

def add(name, type, offset = nil)

def add(name, type, offset = nil)
  if offset.nil? || offset == -1
    offset = @union ? 0 : align(@size, @packed ? [ @packed, type.alignment ].min : [ @min_alignment, type.alignment ].max)
  end
  #
  # If a FFI::Type type was passed in as the field arg, try and convert to a StructLayout::Field instance
  #
  field = type.is_a?(StructLayout::Field) ? type : field_for_type(name, offset, type)
  @fields << field
  @alignment = [ @alignment, field.alignment ].max unless @packed
  @size = [ @size, field.size + (@union ? 0 : field.offset) ].max
  return self
end

def add_array(name, type, count, offset = nil)

def add_array(name, type, count, offset = nil)
  add(name, Type::Array.new(type, count), offset)
end

def add_field(name, type, offset = nil)

def add_field(name, type, offset = nil)
  add(name, type, offset)
end

def add_struct(name, type, offset = nil)

def add_struct(name, type, offset = nil)
  add(name, Type::Struct.new(type), offset)
end

def align(offset, align)

def align(offset, align)
  align + ((offset - 1) & ~(align - 1));
end

def alignment=(align)

def alignment=(align)
  @alignment = align if align > @alignment
  @min_alignment = align
end

def build

def build
  # Add tail padding if the struct is not packed
  size = @packed ? @size : align(@size, @alignment)
  
  StructLayout.new(@fields, size, @alignment)
end

def field_for_type(name, offset, type)

def field_for_type(name, offset, type)
  field_class = case
  when type.is_a?(Type::Function)
    StructLayout::Function
  when type.is_a?(Type::Struct)
    StructLayout::InnerStruct
  when type.is_a?(Type::Array)
    StructLayout::Array
  when type.is_a?(FFI::Enum)
    StructLayout::Enum
  when NUMBER_TYPES.include?(type)
    StructLayout::Number
  when type == Type::POINTER
    StructLayout::Pointer
  when type == Type::STRING
    StructLayout::String
  when type.is_a?(Class) && type < StructLayout::Field
    type
  when type.is_a?(DataConverter)
    return StructLayout::Mapped.new(name, offset, Type::Mapped.new(type), field_for_type(name, offset, type.native_type))
  when type.is_a?(Type::Mapped)
    return StructLayout::Mapped.new(name, offset, type, field_for_type(name, offset, type.native_type))
  else
    raise TypeError, "invalid struct field type #{type.inspect}"
  end
  field_class.new(name, offset, type)
end

def initialize

def initialize
  @size = 0
  @alignment = 1
  @min_alignment = 1
  @packed = false
  @union = false
  @fields = Array.new
end

def packed=(packed)

def packed=(packed)
  if packed.is_a?(Fixnum)
    @alignment = packed
    @packed = packed
  else
    @packed = packed ? 1 : 0
  end
end

def size=(size)

def size=(size)
  @size = size if size > @size
end

def union=(is_union)

def union=(is_union)
  @union = is_union
end

def union?

def union?
  @union
end