/**
 * Variants of "standard" Kotlin functions.
 */
package theorycrafter.utils

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract


/**
 * A variant of [kotlin.with] that takes two arguments.
 */
@OptIn(ExperimentalContracts::class)
@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS")
inline fun <A, B, R> with(a: A, b: B, block: context(A, B) () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b)
}


/**
 * A variant of [kotlin.with] that takes three arguments.
 */
@OptIn(ExperimentalContracts::class)
@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS")
inline fun <A, B, C, R> with(a: A, b: B, c: C, block: context(A, B, C) () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c)
}


/**
 * A variant of [kotlin.with] that takes four arguments.
 */
@OptIn(ExperimentalContracts::class)
@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS")
inline fun <A, B, C, D, R> with(a: A, b: B, c: C, d: D, block: context(A, B, C, D) () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(a, b, c, d)
}

/**
 * If [condition] holds, returns the value of [block] on [this]. Otherwise just returns [this].
 */
fun <T: R, R> T.applyIf(condition: Boolean, block: T.() -> R): R = if (condition) block() else this
