package ru.dbotthepony.mc.otm.core.collect

import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap
import java.util.function.BooleanSupplier
import java.util.stream.Stream

class ConditionalSet<E : Any> : Set<E> {
	private val backing = Object2ObjectLinkedOpenHashMap<E, BooleanSupplier>()

	override val size: Int
		get() = backing.values.stream().filter { it.asBoolean }.count().toInt()

	override fun contains(element: E): Boolean {
		return backing[element]?.asBoolean ?: false
	}

	override fun containsAll(elements: Collection<E>): Boolean {
		return elements.all { contains(it) }
	}

	override fun isEmpty(): Boolean {
		return backing.isEmpty() || backing.values.stream().noneMatch { it.asBoolean }
	}

	override fun stream(): Stream<E> {
		return backing.entries.stream().filter { it.value.asBoolean }.map { it.key }
	}

	override fun iterator(): Iterator<E> {
		return backing.entries.iterator().filter { it.value.asBoolean }.map { it.key }
	}

	fun actuallyContains(element: E): Boolean {
		return element in backing
	}

	fun add(element: E, condition: BooleanSupplier): Boolean {
		if (element in backing) return false
		backing[element] = condition
		return true
	}

	fun addFirst(element: E, condition: BooleanSupplier): Boolean {
		if (element in backing) return false
		backing.putAndMoveToFirst(element, condition)
		return true
	}

	fun replace(element: E, condition: BooleanSupplier) {
		backing[element] = condition
	}

	fun replaceFirst(element: E, condition: BooleanSupplier) {
		backing.remove(element)
		backing.putAndMoveToFirst(element, condition)
	}

	fun remove(element: E): Boolean {
		return backing.remove(element) != null
	}
}
