package ru.dbotthepony.kstarbound.world.entities.player

import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kstarbound.defs.actor.EssentialSlot
import ru.dbotthepony.kstarbound.item.ItemStack
import java.io.DataInputStream
import java.io.DataOutputStream

// same story as InventoryIndex
sealed class HotbarIndex {
	abstract fun get(inventory: PlayerInventory): ItemStack
	abstract fun write(stream: DataOutputStream)

	private object NoSelection : HotbarIndex() {
		override fun get(inventory: PlayerInventory): ItemStack {
			return ItemStack.EMPTY
		}

		override fun write(stream: DataOutputStream) {
			stream.writeByte(0)
		}
	}

	private class Essential(val index: EssentialSlot) : HotbarIndex() {
		override fun get(inventory: PlayerInventory): ItemStack {
			return inventory[index]
		}

		override fun write(stream: DataOutputStream) {
			stream.writeByte(2)
			stream.writeByte(index.ordinal)
		}
	}

	private class Index(val index: Int) : HotbarIndex() {
		init {
			require(index in 0 .. 255) { "Hotbar index out of range: $index"}
		}

		override fun get(inventory: PlayerInventory): ItemStack {
			TODO("Not yet implemented")
		}

		override fun write(stream: DataOutputStream) {
			stream.writeByte(1)
			stream.writeByte(index)
		}
	}

	companion object {
		val CODEC = StreamCodec.Impl(::read, { a, b -> b.write(a) })

		private val indices = Array(256) { Index(it) }
		private val essentials = Array(EssentialSlot.entries.size) { Essential(EssentialSlot.entries[it]) }

		fun nothing(): HotbarIndex {
			return NoSelection
		}

		fun essential(slot: EssentialSlot): HotbarIndex {
			return essentials[slot.ordinal]
		}

		fun slot(slot: Int): HotbarIndex {
			return indices[slot]
		}

		fun read(stream: DataInputStream): HotbarIndex {
			return when (val type = stream.readUnsignedByte()) {
				0 -> NoSelection
				1 -> indices[stream.readUnsignedByte()]
				2 -> essentials[stream.readUnsignedByte()]
				else -> throw IndexOutOfBoundsException("Unknown HotbarIndex type: $type")
			}
		}
	}
}
