package ru.dbotthepony.kstarbound.world.entities.tile

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.reflect.TypeToken
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.defs.InteractAction
import ru.dbotthepony.kstarbound.defs.InteractRequest
import ru.dbotthepony.kstarbound.defs.actor.StatModifier
import ru.dbotthepony.kstarbound.defs.`object`.LoungeOrientation
import ru.dbotthepony.kstarbound.defs.`object`.ObjectDefinition
import ru.dbotthepony.kstarbound.util.coalesceNull
import ru.dbotthepony.kstarbound.util.valueOf
import ru.dbotthepony.kstarbound.world.Direction
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNIT
import java.lang.Math.toRadians

class LoungeableObject(config: Registry.Entry<ObjectDefinition>) : WorldObject(config) {
	init {
		isInteractive = true
	}

	private val sitPositions = ArrayList<Vector2d>()
	private var sitFlipDirection = false
	private var sitOrientation = LoungeOrientation.NONE
	private var sitAngle = 0.0
	private var sitCoverImage = ""
	private var sitFlipImages = false
	private val sitStatusEffects = ObjectArraySet<Either<StatModifier, String>>()
	private val sitEffectEmitters = ObjectArraySet<String>()
	private var sitEmote: String? = null
	private var sitDance: String? = null
	private var sitArmorCosmeticOverrides: JsonObject = JsonObject()
	private var sitCursorOverride: String? = null

	private fun updateSitParams() {
		orientation ?: return

		sitPositions.clear()
		val sitPosition = lookupProperty("sitPosition")
		val sitPositions = lookupProperty("sitPositions")

		if (!sitPosition.isJsonNull) {
			this.sitPositions.add(vectors.fromJsonTree(sitPosition) / PIXELS_IN_STARBOUND_UNIT)
		} else if (!sitPositions.isJsonNull) {
			vectorsList.fromJsonTree(sitPositions).forEach { this.sitPositions.add(it / PIXELS_IN_STARBOUND_UNIT) }
		}

		sitFlipDirection = lookupProperty("sitFlipDirection") { JsonPrimitive(false) }.asBoolean
		sitOrientation = LoungeOrientation.entries.valueOf(lookupProperty("sitOrientation") { JsonPrimitive("sit") }.asString)
		sitAngle = toRadians(lookupProperty("sitAngle") { JsonPrimitive(0.0) }.asDouble)
		sitCoverImage = lookupProperty("sitCoverImage") { JsonPrimitive("") }.asString
		sitFlipImages = lookupProperty("flipImages") { JsonPrimitive(false) }.asBoolean

		sitStatusEffects.clear()
		sitStatusEffects.addAll(statusEffects.fromJsonTree(lookupProperty("sitStatusEffects") { JsonArray() }))

		sitEffectEmitters.clear()
		lookupProperty("sitEffectEmitters") { JsonArray() }.asJsonArray.forEach { sitEffectEmitters.add(it.asString) }
		sitEmote = lookupProperty("sitEmote").coalesceNull?.asString
		sitDance = lookupProperty("sitDance").coalesceNull?.asString
		sitArmorCosmeticOverrides = lookupProperty("sitArmorCosmeticOverrides") { JsonObject() } as JsonObject
		sitCursorOverride = lookupProperty("sitCursorOverride").coalesceNull?.asString
	}

	override fun parametersUpdated() {
		super.parametersUpdated()
		updateSitParams()
	}

	override fun orientationUpdated() {
		super.orientationUpdated()
		updateSitParams()
	}

	override fun interact(request: InteractRequest): InteractAction {
		val upstream = super.interact(request)

		if (upstream.type == InteractAction.Type.NONE && sitPositions.isNotEmpty()) {
			val interactOffset = if (direction.isRight) position - request.targetPos else request.targetPos - position
			var index = 0

			for (i in 1 until sitPositions.size) {
				if ((sitPositions[i] + interactOffset).length < (sitPositions[index] + interactOffset).length) {
					index = i
				}
			}

			return InteractAction(InteractAction.Type.SIT_DOWN, entityID, JsonPrimitive(index))
		}

		return upstream
	}

	companion object {
		private val vectors by lazy { Starbound.gson.getAdapter(Vector2d::class.java) }
		private val vectorsList by lazy { Starbound.gson.getAdapter(object : TypeToken<ImmutableList<Vector2d>>() {}) }
		private val statusEffects by lazy { Starbound.gson.getAdapter(object : TypeToken<ImmutableSet<Either<StatModifier, String>>>() {}) }
	}
}
