package ru.dbotthepony.kstarbound.server.world

import ru.dbotthepony.kommons.arrays.Object2DArray
import ru.dbotthepony.kommons.collect.chainOptionalFutures
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.json.VersionedJson
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.WorldGeometry
import ru.dbotthepony.kstarbound.world.api.AbstractCell
import ru.dbotthepony.kstarbound.world.api.ImmutableCell
import ru.dbotthepony.kstarbound.world.entities.AbstractEntity
import java.io.Closeable
import java.util.concurrent.CompletableFuture

abstract class WorldStorage : Closeable {
	data class Metadata(val geometry: WorldGeometry, val data: VersionedJson)

	abstract fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Object2DArray<out AbstractCell>>>
	abstract fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>>
	abstract fun loadMetadata(): CompletableFuture<KOptional<Metadata>>

	open fun saveEntities(pos: ChunkPos, data: Collection<AbstractEntity>): Boolean {
		return false
	}

	open fun saveCells(pos: ChunkPos, data: Object2DArray<out AbstractCell>): Boolean {
		return false
	}

	open fun saveMetadata(data: Metadata): Boolean {
		return false
	}

	override fun close() {

	}

	private class Fixed(private val cell: ImmutableCell) : WorldStorage() {
		override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Object2DArray<out AbstractCell>>> {
			return CompletableFuture.completedFuture(KOptional.of(Object2DArray(CHUNK_SIZE, CHUNK_SIZE, cell)))
		}

		override fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>> {
			return CompletableFuture.completedFuture(KOptional.of(emptyList()))
		}

		override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
			return CompletableFuture.completedFuture(KOptional())
		}
	}

	object Nothing : WorldStorage() {
		override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Object2DArray<out AbstractCell>>> {
			return CompletableFuture.completedFuture(KOptional())
		}

		override fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>> {
			return CompletableFuture.completedFuture(KOptional())
		}

		override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
			return CompletableFuture.completedFuture(KOptional())
		}
	}

	companion object {
		val NULL: WorldStorage = Fixed(AbstractCell.NULL)
		val EMPTY: WorldStorage = Fixed(AbstractCell.EMPTY)
	}

	class Dispatch(vararg storage: WorldStorage) : WorldStorage() {
		private val children = ArrayList<WorldStorage>()

		init {
			storage.forEach { children.add(it) }
		}

		override fun loadCells(pos: ChunkPos): CompletableFuture<KOptional<Object2DArray<out AbstractCell>>> {
			return chainOptionalFutures(children) { it.loadCells(pos) }
		}

		override fun loadEntities(pos: ChunkPos): CompletableFuture<KOptional<Collection<AbstractEntity>>> {
			return chainOptionalFutures(children) { it.loadEntities(pos) }
		}

		override fun loadMetadata(): CompletableFuture<KOptional<Metadata>> {
			return chainOptionalFutures(children) { it.loadMetadata() }
		}

		override fun saveEntities(pos: ChunkPos, data: Collection<AbstractEntity>): Boolean {
			return children.any { it.saveEntities(pos, data) }
		}

		override fun saveCells(pos: ChunkPos, data: Object2DArray<out AbstractCell>): Boolean {
			return children.any { it.saveCells(pos, data) }
		}

		override fun saveMetadata(data: Metadata): Boolean {
			return children.any { it.saveMetadata(data) }
		}

		override fun close() {
			children.forEach { it.close() }
		}
	}
}
