package ru.dbotthepony.kstarbound import it.unimi.dsi.fastutil.ints.Int2ObjectMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.IntCollection import it.unimi.dsi.fastutil.ints.IntIterator import it.unimi.dsi.fastutil.ints.IntSet import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectCollection import it.unimi.dsi.fastutil.objects.ObjectIterator import it.unimi.dsi.fastutil.objects.ObjectSet import org.apache.logging.log4j.LogManager import java.util.* class ObjectRegistry(val name: String, val key: (T) -> String, val intKey: ((T) -> Int)? = null) { private val objects = Object2ObjectOpenHashMap() private val intObjects = Int2ObjectOpenHashMap() private val origins = Object2ObjectOpenHashMap() private val intOrigins = Int2ObjectOpenHashMap() val view: Map = Collections.unmodifiableMap(objects) val intView = object : Int2ObjectMap { override fun get(key: Int): T? = intObjects[key] override fun containsKey(key: Int) = intObjects.containsKey(key) override fun defaultReturnValue(rv: T) = throw UnsupportedOperationException() override fun defaultReturnValue(): T = throw UnsupportedOperationException() override fun containsValue(value: T) = intObjects.containsValue(value) override fun isEmpty() = intObjects.isEmpty() override fun putAll(from: Map) = throw UnsupportedOperationException() private val int2ObjectEntrySet: ObjectSet> = object : ObjectSet> { override val size: Int get() = intObjects.int2ObjectEntrySet().size override fun add(element: Int2ObjectMap.Entry) = throw UnsupportedOperationException() override fun addAll(elements: Collection>) = throw UnsupportedOperationException() override fun clear() = throw UnsupportedOperationException() override fun iterator(): ObjectIterator> { return object : ObjectIterator> { val iterator = intObjects.int2ObjectEntrySet().iterator() override fun hasNext() = iterator.hasNext() override fun remove() = throw UnsupportedOperationException() override fun next() = iterator.next() } } override fun contains(element: Int2ObjectMap.Entry) = intObjects.int2ObjectEntrySet().contains(element) override fun containsAll(elements: Collection>) = intObjects.int2ObjectEntrySet().containsAll(elements) override fun isEmpty() = intObjects.int2ObjectEntrySet().isEmpty() override fun remove(element: Int2ObjectMap.Entry) = throw UnsupportedOperationException() override fun removeAll(elements: Collection>) = throw UnsupportedOperationException() override fun retainAll(elements: Collection>) = throw UnsupportedOperationException() } override fun int2ObjectEntrySet(): ObjectSet> { return int2ObjectEntrySet } override val keys: IntSet = object : IntSet { override fun add(key: Int) = throw UnsupportedOperationException() override fun addAll(c: IntCollection) = throw UnsupportedOperationException() override fun addAll(elements: Collection) = throw UnsupportedOperationException() override fun clear() = throw UnsupportedOperationException() override fun iterator(): IntIterator { return object : IntIterator { val iterator = intObjects.keys.iterator() override fun hasNext() = iterator.hasNext() override fun remove() = throw UnsupportedOperationException() override fun nextInt() = iterator.nextInt() } } override fun remove(k: Int) = throw UnsupportedOperationException() override fun removeAll(c: IntCollection) = throw UnsupportedOperationException() override fun removeAll(elements: Collection) = throw UnsupportedOperationException() override fun retainAll(c: IntCollection) = throw UnsupportedOperationException() override fun retainAll(elements: Collection) = throw UnsupportedOperationException() override fun contains(key: Int) = intObjects.keys.contains(key) override fun containsAll(c: IntCollection) = intObjects.keys.containsAll(c) override fun containsAll(elements: Collection) = intObjects.keys.containsAll(elements) override fun isEmpty() = intObjects.keys.isEmpty() override fun toArray(a: IntArray) = intObjects.keys.toArray(a) override fun toIntArray() = intObjects.keys.toIntArray() override val size: Int get() = intObjects.keys.size } override val values: ObjectCollection = object : ObjectCollection { override val size: Int get() = intObjects.values.size override fun add(element: T) = throw UnsupportedOperationException() override fun addAll(elements: Collection) = throw UnsupportedOperationException() override fun clear() = throw UnsupportedOperationException() override fun iterator(): ObjectIterator { return object : ObjectIterator { val iterator = intObjects.values.iterator() override fun hasNext() = iterator.hasNext() override fun next(): T = iterator.next() override fun remove() = throw UnsupportedOperationException() } } override fun contains(element: T) = intObjects.values.contains(element) override fun containsAll(elements: Collection) = intObjects.values.containsAll(elements) override fun isEmpty() = intObjects.values.isEmpty() override fun remove(element: T) = throw UnsupportedOperationException() override fun removeAll(elements: Collection) = throw UnsupportedOperationException() override fun retainAll(elements: Collection) = throw UnsupportedOperationException() } override val size: Int get() = intObjects.size } fun clearOrigins() { origins.clear() intOrigins.clear() } fun clear() { clearOrigins() objects.clear() intObjects.clear() } fun add(value: T, origin: Any? = null): Boolean { val key = this.key.invoke(value) val existing = objects.put(key, value) if (existing != null) { val oldOrigin = origins[key] if (origin == null && oldOrigin == null) LOGGER.warn("Registry $name already has object with key $key! Overwriting.") else if (origin == null) LOGGER.warn("Registry $name already has object with key $key! Overwriting. (old originated from $oldOrigin)") else LOGGER.warn("Registry $name already has object with key $key! Overwriting. (old originated from $oldOrigin, new originate from $origin).") } if (origin == null) origins.remove(key) else origins[key] = origin if (this.intKey == null) return existing != null val intKey = this.intKey.invoke(value) val intExisting = intObjects.put(intKey, value) if (intExisting != null) { val oldOrigin = intOrigins[intKey] if (origin == null && oldOrigin == null) LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting.") else if (origin == null) LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting. (old originated from $oldOrigin)") else LOGGER.warn("Registry $name already has object with ID $intKey (new $key, old ${this.key.invoke(intExisting)})! Overwriting. (old originated from $oldOrigin, new originate from $origin).") } return existing != null || intExisting != null } companion object { private val LOGGER = LogManager.getLogger() } }