package mainthread

import (
	"sync"
	"runtime/cgo"
)

/*
#cgo pkg-config: Qt6Core

#include "mainthread.h"
*/
import "C"

// Start runs the callback in the main Qt thread. You should use this whenever
// accessing the main Qt GUI from inside a goroutine.
// This function is non-blocking.
func Start(gofunc func()) {
	h := cgo.NewHandle(gofunc)

	C.mainthread_exec(C.intptr_t(h))
}

// Wait runs the callback in the main Qt thread. You should use this whenever
// accessing the main Qt GUI from inside a goroutine.
// The call blocks until the callback is executed in the main thread's eventloop.
func Wait(gofunc func()) {	
	// It's possible to use Qt::BlockingQueuedConnection to implement the
	// blocking, but it has a deadlock risk
	var wg sync.WaitGroup
	wg.Add(1)
	outerfunc := func() {
		gofunc()
		wg.Done()
	}
	Start(outerfunc)
	wg.Wait()
}

func Wait2[T any](gofunc func() T) (ret T) {
	outerfunc := func() {
		ret = gofunc()
	}
	Wait(outerfunc)
	return ret
}

func Wait3[T any](gofunc func() (T, error)) (ret T, err error) {
	outerfunc := func() {
		ret, err = gofunc()
	}
	Wait(outerfunc)
	return ret, err
}

//export mainthread_exec_handle
func mainthread_exec_handle(u uintptr) {
	h := cgo.Handle(u)

	gofunc, ok := h.Value().(func())
	if !ok {
		panic("miqt: callback of non-callback type (heap corruption?)")
	}

	gofunc()

	// Free handle after use
	h.Delete()
}