package me.eater.threedom.utils

class ObservableSet<T>(private val onChange: (Changed<T>) -> Unit) : MutableSet<T> {
    private val storage: MutableSet<T> = mutableSetOf()

    data class Changed<T>(val action: Action, val elements: Set<T>, val set: ObservableSet<T>)

    enum class Action {
        Removed,
        Added;
    }

    override fun add(element: T): Boolean {
        if (storage.add(element)) {
            onChange(Changed(Action.Added, setOf(element), this))
            return true
        }

        return false
    }

    override fun addAll(elements: Collection<T>): Boolean {
        val items = elements.filter(storage::add)

        if (items.isEmpty()) {
            return false
        }

        onChange(Changed(Action.Added, items.toSet(), this))
        return true
    }

    override fun clear() {
        if (this.isEmpty()) {
            return
        }

        onChange(Changed(Action.Removed, storage, this))
        storage.clear()
    }

    override fun iterator(): MutableIterator<T> = storage.iterator()

    override fun remove(element: T): Boolean {
        if (storage.remove(element)) {
            onChange(Changed(Action.Removed, setOf(element), this))
            return true
        }

        return false
    }

    override fun removeAll(elements: Collection<T>): Boolean {
        val items = elements.filter(storage::remove)

        if (items.isEmpty()) {
            return false
        }

        onChange(Changed(Action.Removed, items.toSet(), this))
        return true
    }

    override fun retainAll(elements: Collection<T>): Boolean {
        val temp = mutableSetOf<T>()
        for (item in storage) {
            if (!elements.contains(item)) {
                temp.add(item)
                storage.remove(item)
            }
        }

        onChange(Changed(Action.Removed, temp, this))
        return temp.size > 0
    }

    override val size: Int
        get() = storage.size

    override fun contains(element: T) = storage.contains(element)

    override fun containsAll(elements: Collection<T>) = storage.containsAll(elements)

    override fun isEmpty(): Boolean = storage.isEmpty()
}
