package ru.dbotthepony.kstarbound.client.gl.vertex

import com.google.common.collect.ImmutableList
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import java.util.Collections
import java.util.EnumMap
import java.util.EnumSet

class VertexAttributes private constructor(
	val attributeList: ImmutableList<Attribute>,
	val attributeMap: Map<VertexAttributeType, Attribute>,
	val vertexStride: Int,
	val allFlags: Long,
) {
	data class Attribute(val type: VertexAttributeType, val index: Int, val relativeOffset: Int, val flag: Long)

	val size get() = attributeList.size
	operator fun get(index: Int): Attribute = attributeList[index]
	operator fun get(index: VertexAttributeType): Attribute? = attributeMap[index]

	operator fun contains(index: VertexAttributeType) = index in attributeMap

	companion object {
		val POSITION = of(VertexAttributeType.POSITION)
		val POSITION_UV = of(VertexAttributeType.POSITION, VertexAttributeType.UV)
		val POSITION_COLOR = of(VertexAttributeType.POSITION, VertexAttributeType.COLOR)

		@JvmStatic
		fun of(vararg attributes: VertexAttributeType): VertexAttributes {
			return of(ObjectArrayList.wrap(attributes))
		}

		@JvmStatic
		fun of(attributes: Collection<VertexAttributeType>): VertexAttributes {
			val dup = EnumSet.noneOf(VertexAttributeType::class.java)

			attributes.forEach {
				if (!dup.add(it)) {
					throw IllegalArgumentException("Duplicate vertex attribute: $it")
				}
			}

			require(attributes.size < 64) { "Vertex attribute list is insanely big: ${attributes.size}; 64 is max" }

			val attributeList = ImmutableList.Builder<Attribute>()
			val attributeMap = EnumMap<VertexAttributeType, Attribute>(VertexAttributeType::class.java)
			var allVertexFlags = 0L

			var stride = 0

			for (attr in attributes) {
				stride += attr.type.byteSize
				allVertexFlags = (allVertexFlags shl 1) or 1L
			}

			var offset = 0
			var index = 0

			for ((i, attr) in attributes.withIndex()) {
				val constructed = Attribute(attr, index, offset, 1L shl i)
				index += attr.type.width
				attributeList.add(constructed)
				offset += attr.type.byteSize
				attributeMap[attr] = constructed
			}

			return VertexAttributes(attributeList.build(), Collections.unmodifiableMap(attributeMap), stride, allVertexFlags)
		}
	}
}
