package ru.dbotthepony.kstarbound.server.world

import ru.dbotthepony.kommons.io.BTreeDB6
import ru.dbotthepony.kommons.io.ByteKey
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.io.BTreeDB5
import ru.dbotthepony.kstarbound.util.CarriedExecutor
import java.io.Closeable
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.function.Supplier

abstract class StorageEngine : Closeable {
	abstract fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>>
	abstract fun write(at: ByteKey, value: ByteArray)

	class Memory(private val get: (ByteKey) -> ByteArray?, private val set: (ByteKey, ByteArray) -> Unit) : StorageEngine() {
		override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
			return CompletableFuture.completedFuture(KOptional.ofNullable(get(at)))
		}

		override fun write(at: ByteKey, value: ByteArray) {
			set(at, value)
		}

		override fun close() {}
	}

	class DB5(private val database: BTreeDB5) : StorageEngine() {
		private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)

		override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
			return CompletableFuture.supplyAsync(Supplier { database.read(at) }, carrier)
		}

		override fun write(at: ByteKey, value: ByteArray) {
			throw UnsupportedOperationException()
		}

		override fun close() {
			carrier.execute { database.close() }
			carrier.wait(300L, TimeUnit.SECONDS)
		}
	}

	class DB6(private val database: BTreeDB6) : StorageEngine() {
		private val carrier = CarriedExecutor(Starbound.IO_EXECUTOR)

		override fun load(at: ByteKey): CompletableFuture<KOptional<ByteArray>> {
			return CompletableFuture.supplyAsync(Supplier { database.read(at) }, carrier)
		}

		override fun write(at: ByteKey, value: ByteArray) {
			carrier.execute { database.write(at, value) }
		}

		override fun close() {
			carrier.execute { database.close() }
			carrier.wait(300L, TimeUnit.SECONDS)
		}
	}
}
