package ru.dbotthepony.mc.otm.core.util

import java.util.concurrent.atomic.AtomicInteger
import java.util.function.IntConsumer
import java.util.function.IntSupplier

/**
 * This lazy is not synchronized, so in event of two or more threads accessing value behavior of this implementation is undefined
 *
 * For thread synchronized use case, [AtomicallyInvalidatedLazy] should be used
 */
fun <V> countingLazy(invalidator: IntSupplier, initializer: () -> V): InvalidableLazy<V> {
	if (invalidator is IntCounter) {
		return SmartCounterInvalidatedLazy(invalidator, initializer)
	}

	return CounterInvalidatedLazy(invalidator, initializer)
}

/**
 * This lazy is not synchronized, so in event of two or more threads accessing value behavior of this implementation is undefined
 *
 * For thread synchronized use case, [AtomicallyInvalidatedLazy] should be used
 */
fun <V> countingLazy(invalidator: AtomicInteger, initializer: () -> V): InvalidableLazy<V> {
	return CounterInvalidatedLazy(invalidator, initializer)
}

/**
 * This lazy is not synchronized, so in event of two or more threads accessing value behavior of this implementation is undefined
 *
 * For synchronized use case, [AtomicallyInvalidatedLazy] should be used
 */
class CounterInvalidatedLazy<V>(private val invalidator: IntSupplier, private val initializer: () -> V) : InvalidableLazy<V> {
	constructor(invalidator: AtomicInteger, initializer: () -> V) : this(invalidator::get, initializer)

	private var thisCounter = -1
	private var stored: Any? = Companion

	override val value: V get() {
		var stored = stored

		if (stored !== Companion && thisCounter == invalidator.asInt)
			return stored as V

		stored = initializer.invoke()
		this.stored = stored
		this.thisCounter = invalidator.asInt
		return stored
	}

	override fun isInitialized(): Boolean {
		return stored !== Companion && thisCounter == invalidator.asInt
	}

	override fun invalidate() {
		this.stored = Companion
	}

	private companion object
}

/**
 * This lazy is not synchronized, so in event of two or more threads accessing value behavior of this implementation is undefined
 *
 * For synchronized use case, [AtomicallyInvalidatedLazy] should be used
 */
class SmartCounterInvalidatedLazy<V>(invalidator: IntCounter, private val initializer: () -> V) : InvalidableLazy<V> {
	init {
		invalidator.addListener(IntConsumer {
			stored = Companion
		})
	}

	private var stored: Any? = Companion

	override val value: V get() {
		var stored = stored

		if (stored !== Companion)
			return stored as V

		stored = initializer.invoke()
		this.stored = stored
		return stored
	}

	override fun invalidate() {
		this.stored = Companion
	}

	override fun isInitialized(): Boolean {
		return stored !== Companion
	}

	private companion object
}
