package ru.dbotthepony.kstarbound.item

import com.google.gson.JsonArray
import com.google.gson.JsonElement
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor

interface IContainer {
	var size: Int
	operator fun get(index: Int): ItemStack
	operator fun set(index: Int, value: ItemStack)

	fun ageItems(by: Double): Boolean {
		var any = false

		for (i in 0 until size) {
			val (newItem, hasAdvanced) = this[i].advanceAge(by)
			any = hasAdvanced || any

			if (newItem != null) {
				this[i] = newItem
			}
		}

		return any
	}

	// puts item into container, returns remaining not put items
	fun add(item: ItemStack, simulate: Boolean = false): ItemStack {
		val copy = item.copy()

		// first, try to put into not empty slots first
		for (i in 0 until size) {
			val itemThere = this[i]

			if (itemThere.isStackable(copy)) {
				val newCount = (itemThere.size + copy.size).coerceAtMost(itemThere.maxStackSize)
				val diff = newCount - itemThere.size

				copy.size -= diff

				if (!simulate)
					itemThere.size += diff

				if (copy.isEmpty)
					return ItemStack.EMPTY
			}
		}

		// then try to move into empty slots
		for (i in 0 until size) {
			val itemThere = this[i]

			if (itemThere.isEmpty) {
				if (copy.size > copy.maxStackSize) {
					if (!simulate)
						this[i] = copy.copy(copy.maxStackSize)

					copy.size -= copy.maxStackSize
				} else {
					if (!simulate)
						this[i] = copy

					return ItemStack.EMPTY
				}
			}
		}

		return copy
	}

	fun clear()

	fun toJson(store: Boolean = false): JsonArray {
		val result = JsonArray(size)

		if (store) {
			Starbound.storeJson {
				for (i in 0 until size) {
					result.add(this[i].toJson())
				}
			}
		} else {
			for (i in 0 until size) {
				result.add(this[i].toJson())
			}
		}

		return result
	}

	fun fromJson(json: JsonElement, resize: Boolean = false): List<ItemStack> {
		clear()

		val read = ArrayList<ItemStack>()
		val array = json.asJsonArray
		var nonEmpty = 0

		for (i in 0 until array.size()) {
			val item = ItemDescriptor(array[i])

			if (item.isNotEmpty)
				nonEmpty++

			read.add(item.build())
		}

		if (read.size > size) {
			// we have a problem

			if (resize) {
				// no problem
				size = read.size

				for ((i, item) in read.withIndex()) {
					this[i] = item
				}

				return emptyList()
			} else {
				// first, put items to their place if they are within container size

				for (i in 0 until size) {
					this[i] = read[i]
				}

				// second, try to move remaining item into empty or stackable slots
				val lost = ArrayList<ItemStack>()

				for (i in size until read.size) {
					val remaining = add(read[i])

					if (remaining.isNotEmpty) {
						lost.add(remaining)
					}
				}

				// if we weren't capable of putting excess items into container, then what a shame.
				return lost
			}
		} else {
			if (read.size < size && resize)
				size = read.size

			for ((i, item) in read.withIndex()) {
				this[i] = item
			}

			return emptyList()
		}
	}
}
