package collections

class ResizingArrayList<T>(
    private var capacity: Int = 16,
): ImperialMutableList<T> {
    override var size = 0
        private set
    private var elements: Array<T?> = arrayOfNulls(capacity)
    init {
        if (capacity < 0) throw IllegalArgumentException("Capacity must be positive")
    }

    override fun get(index: Int): T {
        if (index !in 0..<size) throw IndexOutOfBoundsException("Index: $index, Size: $size")
        return elements[index]!!
    }

    @Suppress("UNCHECKED_CAST")
    fun resize() {
        capacity = maxOf(capacity,1) * 2
        var newElements = arrayOfNulls<Any?>(capacity)
        elements.forEachIndexed { index,_ ->
            newElements[index] = elements[index]
        }
        elements = newElements as Array<T?>
    }

    override fun add(index: Int, element: T) {
        if (index !in 0..size)
            throw IndexOutOfBoundsException("Index: $index, Size: $size")
        if (size == capacity) resize()
        elements.copyInto(elements,index+1,index,size)
        elements[index] = element
        size++
    }

    override fun clear() {
        elements.forEachIndexed { index,_ -> elements[index] = null }
        size = 0
    }

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

    override fun removeAt(index: Int): T {
        if (index !in 0..<size)
            throw IndexOutOfBoundsException("Index: $index, Size: $size")
        return elements[index]!!.also {
            size--
            elements.copyInto(elements,-1,index,size)
        }
    }

    fun find(element: T): Int? {
        elements.forEachIndexed { index,value ->
            if (value == element) return@find index
        }
        return null
    }

    override fun remove(element: T): Boolean {
        val index = find(element)
        return index?.let {
            removeAt(index)
            true
        } ?: false
    }

    override fun set(index: Int, element: T): T {
        if (index !in 0..<size)
            throw IndexOutOfBoundsException("Index: $index, Size: $size")
        return elements[index]!!.also {
            elements[index] = element
        }
    }

    override fun addAll(index: Int, other: ImperialMutableList<T>) {
        while (index + size + other.size !in 0..capacity) resize()
        elements.copyInto(elements,index + other.size,index,size)
        other.forEachIndexed { otherIndex,value -> elements[index + otherIndex] = value }
        size += other.size
        //other.toList().forEachIndexed { otherIndex,value -> add(index + otherIndex,value) }
    }

    inner class ResizingArrayListIterator: Iterator<T> {
        private var index = 0
        override fun hasNext() = elements.size > index && elements[index] != null
        override fun next(): T {
            if (!hasNext()) throw NoSuchElementException()
            return elements[index++]!!
        }
    }

    override fun iterator() = ResizingArrayListIterator()

    override fun toString() = "[${toList().joinToString(", ") { it.toString() }}]"
}