package ru.dbotthepony.mc.otm.core

import it.unimi.dsi.fastutil.booleans.BooleanConsumer
import it.unimi.dsi.fastutil.floats.FloatConsumer
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
import java.util.function.Consumer
import java.util.function.DoubleConsumer
import java.util.function.IntConsumer
import java.util.function.LongConsumer

interface ISubscriptable<V> {
	/**
	 * Listener token, allows to remove listener from subscriber list
	 */
	fun interface L {
		/**
		 * Removes this listener
		 */
		fun remove()
	}

	fun addListener(listener: Consumer<V>): L

	class Impl<V> : ISubscriptable<V>, Consumer<V> {
		private inner class L(val callback: Consumer<V>) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: Consumer<V>): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: V) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}

	companion object : ISubscriptable<Nothing>, L {
		@Suppress("unchecked_cast")
		fun <T> empty(): ISubscriptable<T> {
			return this as ISubscriptable<T>
		}

		override fun remove() {}

		override fun addListener(listener: Consumer<Nothing>): L {
			return this
		}
	}
}

interface IFloatSubcripable : ISubscriptable<Float> {
	@Deprecated("Use type specific listener")
	override fun addListener(listener: Consumer<Float>): ISubscriptable.L {
		return addListener(listener::accept)
	}

	fun addListener(listener: FloatConsumer): ISubscriptable.L

	class Impl : IFloatSubcripable, Consumer<Float>, FloatConsumer {
		private inner class L(val callback: FloatConsumer) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: FloatConsumer): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: Float) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}
}

interface IDoubleSubcripable : ISubscriptable<Double> {
	@Deprecated("Use type specific listener")
	override fun addListener(listener: Consumer<Double>): ISubscriptable.L {
		return addListener(DoubleConsumer { listener.accept(it) })
	}

	fun addListener(listener: DoubleConsumer): ISubscriptable.L

	class Impl : IDoubleSubcripable, Consumer<Double>, DoubleConsumer {
		private inner class L(val callback: DoubleConsumer) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: DoubleConsumer): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: Double) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}
}

interface IIntSubcripable : ISubscriptable<Int> {
	@Deprecated("Use type specific listener")
	override fun addListener(listener: Consumer<Int>): ISubscriptable.L {
		return addListener(IntConsumer { listener.accept(it) })
	}

	fun addListener(listener: IntConsumer): ISubscriptable.L

	class Impl : IIntSubcripable, Consumer<Int>, IntConsumer {
		private inner class L(val callback: IntConsumer) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: IntConsumer): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: Int) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}
}

interface ILongSubcripable : ISubscriptable<Long> {
	@Deprecated("Use type specific listener")
	override fun addListener(listener: Consumer<Long>): ISubscriptable.L {
		return addListener(LongConsumer { listener.accept(it) })
	}

	fun addListener(listener: LongConsumer): ISubscriptable.L

	class Impl : ILongSubcripable, Consumer<Long>, LongConsumer {
		private inner class L(val callback: LongConsumer) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: LongConsumer): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: Long) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}
}

interface IBooleanSubscriptable : ISubscriptable<Boolean> {
	@Deprecated("Use type specific listener")
	override fun addListener(listener: Consumer<Boolean>): ISubscriptable.L {
		return addListener(listener::accept)
	}

	fun addListener(listener: BooleanConsumer): ISubscriptable.L

	class Impl : IBooleanSubscriptable, Consumer<Boolean>, BooleanConsumer {
		private inner class L(val callback: BooleanConsumer) : ISubscriptable.L {
			private var isRemoved = false

			init {
				subscribers.add(this)
			}

			override fun remove() {
				if (!isRemoved) {
					isRemoved = true
					queue.add(this)
				}
			}
		}

		private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
		private val queue = ReferenceArraySet<L>(0)

		override fun addListener(listener: BooleanConsumer): ISubscriptable.L {
			return L(listener)
		}

		override fun accept(t: Boolean) {
			queue.forEach { subscribers.remove(it) }
			queue.clear()
			subscribers.forEach { it.callback.accept(t) }
		}
	}
}
