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

import ru.dbotthepony.kommons.io.IntValueCodec
import ru.dbotthepony.kommons.io.StreamCodec
import ru.dbotthepony.kommons.io.koptional
import ru.dbotthepony.kommons.io.map
import ru.dbotthepony.kommons.io.readKOptional
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeKOptional
import ru.dbotthepony.kommons.util.KOptional
import ru.dbotthepony.kommons.util.getValue
import ru.dbotthepony.kommons.util.setValue
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.json.builder.IStringSerializable
import ru.dbotthepony.kstarbound.network.syncher.NetworkedDynamicGroup
import ru.dbotthepony.kstarbound.network.syncher.NetworkedElement
import ru.dbotthepony.kstarbound.network.syncher.NetworkedGroup
import ru.dbotthepony.kstarbound.network.syncher.networkedBoolean
import ru.dbotthepony.kstarbound.network.syncher.networkedData
import ru.dbotthepony.kstarbound.network.syncher.networkedFixedPoint
import ru.dbotthepony.kstarbound.network.syncher.networkedString
import ru.dbotthepony.kstarbound.world.entities.Animator
import java.io.DataInputStream
import java.io.DataOutputStream

// i am very disappointed.
class TechController(val player: PlayerEntity) {
	val networkGroup = NetworkedGroup()

	private val animators = NetworkedDynamicGroup(::TechAnimator, { it }).also { networkGroup.add(it) }

	var state by networkedData(KOptional(), STATE_CODEC, STATE_CODEC_LEGACY).also { networkGroup.add(it) }
		private set
	var directives by networkedString().also { networkGroup.add(it) }
		private set
	var xOffset by networkedFixedPoint(0.003125).also { networkGroup.add(it) }
		private set
	var yOffset by networkedFixedPoint(0.003125).also { networkGroup.add(it) }
		private set
	var isHidden by networkedBoolean().also { networkGroup.add(it) }
		private set
	var isToolUsageSuppressed by networkedBoolean().also { networkGroup.add(it) }
		private set

	enum class State(override val jsonName: String) : IStringSerializable {
		STAND("Stand"),
		FLY("Fly"),
		FALL("Fall"),
		SIT("Sit"),
		LAY("Lay"),
		DUCK("Duck"),
		WALK("Walk"),
		RUN("Run"),
		SWIM("Swim");
	}

	private class TechAnimator(var config: KOptional<String> = KOptional()) : NetworkedElement.Passthrough() {
		val networkGroup = NetworkedGroup()

		var animator: Animator = Animator().also { networkGroup.add(it.networkGroup) }
			private set(value) {
				networkGroup.replace(field.networkGroup, value.networkGroup)
				field = value
			}

		var isVisible by networkedBoolean().also { networkGroup.add(it) }

		override val parentElement: NetworkedElement
			get() = networkGroup

		override fun readInitial(data: DataInputStream, isLegacy: Boolean) {
			val old = config
			config = data.readKOptional { readInternedString() }

			if (old != config)
				animator = config.map { Animator.load(it) }.orElse { Animator() }

			super.readInitial(data, isLegacy)
		}

		override fun writeInitial(data: DataOutputStream, isLegacy: Boolean) {
			data.writeKOptional(config) { writeBinaryString(it) }
			super.writeInitial(data, isLegacy)
		}
	}

	companion object {
		private val STATE_CODEC = StreamCodec.Enum(State::class.java).koptional()
		private val STATE_CODEC_LEGACY = IntValueCodec.map({ State.entries[this] }, { ordinal }).koptional()
	}
}
