//go:build !nautilus
// +build !nautilus

package rbd

/*
#cgo LDFLAGS: -lrbd
#include <errno.h>
#include <stdlib.h>
#include <rbd/librbd.h>

extern int sparsifyCallback(uint64_t, uint64_t, uintptr_t);

// inline wrapper to cast uintptr_t to void*
static inline int wrap_rbd_sparsify_with_progress(
		rbd_image_t image, size_t sparse_size, uintptr_t arg) {
	return rbd_sparsify_with_progress(
		image, sparse_size, (librbd_progress_fn_t)sparsifyCallback, (void*)arg);
};
*/
import "C"

import (
	"github.com/ceph/go-ceph/internal/callbacks"
)

// SparsifyCallback defines the function signature needed for the
// SparsifyWithProgress callback.
//
// This callback will be called by SparsifyWithProgress when it wishes to
// report progress on sparse. The callback function will be called with the
// first argument containing the current offset within the image being made
// sparse and the second argument containing the total size of the image. The
// third argument is an opaque value that is passed to the SparsifyWithProgress
// function's data argument and every call to the callback will receive the
// same object. The sparsify operation will be aborted if the progress
// callback returns a non-zero value.
type SparsifyCallback func(uint64, uint64, interface{}) int

var sparsifyCallbacks = callbacks.New()

type sparsifyCallbackCtx struct {
	callback SparsifyCallback
	data     interface{}
}

// SparsifyWithProgress makes an image sparse by deallocating runs of zeros.
// The sparseSize value will be used to find runs of zeros and must be
// a power of two no less than 4096 and no larger than the image size.
// The given progress callback will be called to report on the progress
// of sparse. The operation will be aborted if the progress callback returns
// a non-zero value.
//
// Implements:
//
//	int rbd_sparsify_with_progress(rbd_image_t image, size_t sparse_size,
//								   librbd_progress_fn_t cb, void *cbdata);
func (image *Image) SparsifyWithProgress(
	sparseSize uint, cb SparsifyCallback, data interface{},
) error {
	// the provided callback must be a real function
	if cb == nil {
		return getError(C.EINVAL)
	}

	if err := image.validate(imageIsOpen); err != nil {
		return err
	}

	ctx := sparsifyCallbackCtx{
		callback: cb,
		data:     data,
	}
	cbIndex := sparsifyCallbacks.Add(ctx)
	defer sparsifyCallbacks.Remove(cbIndex)

	ret := C.wrap_rbd_sparsify_with_progress(image.image, C.size_t(sparseSize), C.uintptr_t(cbIndex))

	return getError(ret)
}

//export sparsifyCallback
func sparsifyCallback(
	offset, total C.uint64_t, index uintptr,
) C.int {
	v := sparsifyCallbacks.Lookup(index)
	ctx := v.(sparsifyCallbackCtx)
	return C.int(ctx.callback(uint64(offset), uint64(total), ctx.data))
}
