module ObjectSpace
    include System::Runtime::InteropServices

    NIL_OBJECT_ID = 4.0
    def self.wrapped_ids
        @wrapped_ids ||= {}
    end

    def self.id2ref(object_id,ignore_error=false)
        object_id = object_id.to_f if (!object_id.is_a?(Float))
        if self.wrapped_ids.has_key?(object_id) then
            object_handle = self.wrapped_ids[object_id]

            if object_handle.is_allocated && (object_id == NIL_OBJECT_ID || object_handle.target === false || object_handle.target) then
                object_handle.target
            else
                self.wrapped_ids.delete(object_id)
                raise Exception.new("Object with id '#{object_id}' doesn't exist anymore.") if !ignore_error
            end
        else
            raise Exception.new("Object with id '#{object_id}' doesn't exist or not wrapped.") if !ignore_error
        end
    end

    def self.ref2id(object,object_id)
        object_id = object_id.to_f if (!object_id.is_a?(Float))
        if self.wrapped_ids.has_key?(object_id) then
            old_object = id2ref(object_id, true)
            raise Exception.new("There are two objects with id '#{object_id}': '#{object}' ? '#{old_object}'") unless old_object.equal?(object)
        end

        unless self.wrapped_ids.has_key?(object_id) then
            object_handle = GCHandle.alloc(object, GCHandleType.Weak)
            if object_handle.is_allocated then
                self.wrapped_ids[object_id] = object_handle
            else
                raise Exception.new("Object with id '#{object_id}' couldn't be wrapped.")
            end
        end
        object_id
    end

    # MEMO: adding non-changing object id's
    ref2id(nil, nil.object_id)
    ref2id(true, true.object_id)
    ref2id(false, false.object_id)
end
