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

import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.locks.ReentrantLock
import java.util.function.IntSupplier

/**
 * If synchronization is not required, [CounterInvalidatedLazy] should be used instead.
 */
class AtomicallyInvalidatedLazy<V>(private val invalidator: IntSupplier, private val initializer: () -> V) : InvalidableLazy<V> {
	constructor(invalidator: AtomicInteger, initializer: () -> V) : this(invalidator::get, initializer)

	@Volatile
	private var thisCounter = -1
	@Volatile
	private var stored: Any? = Companion
	private val lock = ReentrantLock()

	override val value: V get() {
		if (thisCounter != invalidator.asInt) {
			invalidate()
		}

		var stored = stored

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

		lock.lock()

		try {
			stored = initializer.invoke()
			this.stored = stored
			return stored
		} finally {
			lock.unlock()
		}
	}

	override fun invalidate() {
		lock.lock()
		this.stored = Companion
		thisCounter = invalidator.asInt
		lock.unlock()
	}

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

	private companion object
}
