package theorycrafter.utils

import kotlinx.coroutines.*
import java.util.concurrent.Executors
import kotlin.reflect.KProperty


/**
 * Runs the given block, catching any exception that is not a [CancellationException].
 */
inline fun <R> runCatchingExceptCancellation(block: () -> R): Result<R>{
    return try {
        Result.success(block())
    } catch (e: Throwable) {
        if (e is CancellationException)
            throw e
        Result.failure(e)
    }
}


/**
 * Returns a [CoroutineDispatcher] backed by a single thread executor with a thread with the given name.
 */
@Suppress("unused")
fun newSingleThreadCoroutineDispatcher(name: String): CoroutineDispatcher {
    return Executors.newSingleThreadExecutor { Thread(it, name) }.asCoroutineDispatcher()
}


/**
 * If the [Deferred] is already completed, returns its value; otherwise waits for it to complete and then returns its
 * value
 */
@OptIn(ExperimentalCoroutinesApi::class)
fun <T> Deferred<T>.getOrWaitForCompleted(): T =
    if (isCompleted) getCompleted() else runBlocking { await() }


/**
 * A version of [getOrWaitForCompleted] that allows using [Deferred] as a delegate.
 */
operator fun <T> Deferred<T>.getValue(thisRef: Any?, property: KProperty<*>): T = getOrWaitForCompleted()


/**
 * Runs the given block on a background thread, returning a [Deferred] for its value.
 */
@OptIn(DelicateCoroutinesApi::class)
fun <R> onBackgroundThread(block: () -> R): Deferred<R> = GlobalScope.async(Dispatchers.Default) { block() }
