package ru.dbotthepony.kstarbound.lua.bindings

import org.classdump.luna.ByteString
import org.classdump.luna.Table
import ru.dbotthepony.kommons.collect.map
import ru.dbotthepony.kommons.collect.toList
import ru.dbotthepony.kommons.vector.Vector2f
import ru.dbotthepony.kstarbound.lua.LuaEnvironment
import ru.dbotthepony.kstarbound.lua.from
import ru.dbotthepony.kstarbound.lua.get
import ru.dbotthepony.kstarbound.lua.iterator
import ru.dbotthepony.kstarbound.lua.luaFunction
import ru.dbotthepony.kstarbound.lua.luaFunctionArray
import ru.dbotthepony.kstarbound.lua.set
import ru.dbotthepony.kstarbound.lua.toAABB
import ru.dbotthepony.kstarbound.lua.toColor
import ru.dbotthepony.kstarbound.lua.toPoly
import ru.dbotthepony.kstarbound.lua.toVector2d
import ru.dbotthepony.kstarbound.lua.toVector2f
import ru.dbotthepony.kstarbound.world.entities.Animator

fun provideAnimatorBindings(self: Animator, lua: LuaEnvironment) {
	val callbacks = lua.newTable()
	lua.globals["animator"] = callbacks

	callbacks["setAnimationState"] = luaFunction { type: ByteString, state: ByteString, alwaysStart: Boolean? ->
		returnBuffer.setTo(self.setActiveState(type.decode(), state.decode(), alwaysStart ?: false))
	}

	callbacks["animationState"] = luaFunction { type: ByteString ->
		returnBuffer.setTo(self.animationState(type.decode()))
	}

	callbacks["animationStateProperty"] = luaFunction { type: ByteString, key: ByteString ->
		returnBuffer.setTo(self.stateProperty(type.decode(), key.decode()))
	}

	callbacks["setGlobalTag"] = luaFunction { key: ByteString, value: ByteString ->
		self.setGlobalTag(key.decode(), value.decode())
	}

	callbacks["setPartTag"] = luaFunction { part: ByteString, key: ByteString, value: ByteString ->
		self.setPartTag(part.decode(), key.decode(), value.decode())
	}

	callbacks["setFlipped"] = luaFunction { isFlipped: Boolean, centerLine: Double? ->
		self.isFlipped = isFlipped
		self.flippedRelativeCenterLine = centerLine ?: 0.0
	}

	callbacks["setAnimationRate"] = luaFunction { rate: Double ->
		self.animationRate = rate
	}

	callbacks["rotateGroup"] = luaFunction { group: ByteString, rotation: Number, immediate: Boolean? ->
		self.rotateGroup(group.decode(), rotation.toDouble(), immediate ?: false)
	}

	callbacks["currentRotationAngle"] = luaFunction { group: ByteString ->
		returnBuffer.setTo(self.currentRotationAngle(group.decode()))
	}

	callbacks["targetRotationAngle"] = luaFunction { group: ByteString ->
		returnBuffer.setTo(self.targetRotationAngle(group.decode()))
	}

	callbacks["hasRotationGroup"] = luaFunction { group: ByteString ->
		returnBuffer.setTo(self.hasRotationGroup(group.decode()))
	}

	callbacks["rotationGroups"] = luaFunction {
		val groups = self.rotationGroups()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["hasTransformationGroup"] = luaFunction { group: ByteString ->
		returnBuffer.setTo(self.hasTransformationGroup(group.decode()))
	}

	callbacks["transformationGroups"] = luaFunction {
		val groups = self.transformationGroups()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["translateTransformGroup"] = luaFunction { group: ByteString, translation: Table ->
		self.translateTransformGroup(group.decode(), toVector2f(translation))
	}

	callbacks["rotateTransformGroup"] = luaFunction { group: ByteString, rotation: Double, center: Table? ->
		self.rotateTransformGroup(group.decode(), rotation.toFloat(), if (center == null) Vector2f.ZERO else toVector2f(center))
	}

	callbacks["scaleTransformationGroup"] = luaFunction { group: ByteString, scale: Any, center: Table? ->
		if (scale is Number) {
			self.scaleTransformationGroup(group.decode(), Vector2f(scale.toFloat(), scale.toFloat()), if (center == null) Vector2f.ZERO else toVector2f(center))
		} else {
			self.scaleTransformationGroup(group.decode(), toVector2f(scale), if (center == null) Vector2f.ZERO else toVector2f(center))
		}
	}

	callbacks["transformTransformationGroup"] = luaFunctionArray { arguments: Array<out Any?> ->
		val group = arguments[0] as ByteString
		val r00 = arguments[1] as Number
		val r01 = arguments[2] as Number
		val r02 = arguments[3] as Number
		val r10 = arguments[4] as Number
		val r11 = arguments[5] as Number
		val r12 = arguments[6] as Number

		self.transformTransformationGroup(group.decode(), r00.toFloat(),
			r01.toFloat(),
			r02.toFloat(),
			r10.toFloat(),
			r11.toFloat(),
			r12.toFloat())
	}

	callbacks["resetTransformationGroup"] = luaFunction { group: ByteString ->
		self.resetTransformationGroup(group.decode())
	}

	callbacks["particleEmitters"] = luaFunction {
		val groups = self.particleEmitters()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["hasParticleEmitter"] = luaFunction { group: ByteString ->
		returnBuffer.setTo(self.hasParticleEmitter(group.decode()))
	}

	callbacks["setParticleEmitterActive"] = luaFunction { emitter: ByteString, state: Boolean ->
		self.setParticleEmitterActive(emitter.decode(), state)
	}

	callbacks["setParticleEmitterEmissionRate"] = luaFunction { emitter: ByteString, rate: Number ->
		self.setParticleEmitterEmissionRate(emitter.decode(), rate.toDouble())
	}

	callbacks["setParticleEmitterBurstCount"] = luaFunction { emitter: ByteString, count: Number ->
		self.setParticleEmitterBurstCount(emitter.decode(), count.toInt())
	}

	callbacks["setParticleEmitterOffsetRegion"] = luaFunction { emitter: ByteString, region: Table ->
		self.setParticleEmitterOffsetRegion(emitter.decode(), toAABB(region))
	}

	callbacks["burstParticleEmitter"] = luaFunction { emitter: ByteString ->
		self.burstParticleEmitter(emitter.decode())
	}

	callbacks["lights"] = luaFunction {
		val groups = self.lights()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["hasLight"] = luaFunction { light: ByteString ->
		returnBuffer.setTo(self.hasLight(light.decode()))
	}

	callbacks["setLightActive"] = luaFunction { light: ByteString, state: Boolean ->
		self.setLightActive(light.decode(), state)
	}

	callbacks["setLightPosition"] = luaFunction { light: ByteString, position: Table ->
		self.setLightPosition(light.decode(), toVector2d(position))
	}

	callbacks["setLightColor"] = luaFunction { light: ByteString, color: Table ->
		self.setLightColor(light.decode(), toColor(color))
	}

	callbacks["setLightPointAngle"] = luaFunction { light: ByteString, angle: Number ->
		self.setLightPointAngle(light.decode(), angle.toDouble())
	}

	callbacks["sounds"] = luaFunction {
		val groups = self.sounds()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["hasSound"] = luaFunction { sound: ByteString ->
		returnBuffer.setTo(self.hasSound(sound.decode()))
	}

	callbacks["setSoundPool"] = luaFunction { sound: ByteString, sounds: Table ->
		self.setSoundPool(sound.decode(), sounds.iterator().map { (it.value as ByteString).decode() }.toList())
	}

	callbacks["setSoundPosition"] = luaFunction { sound: ByteString, position: Table ->
		self.setSoundPosition(sound.decode(), toVector2d(position))
	}

	callbacks["playSound"] = luaFunction { sound: ByteString, loops: Number? ->
		self.playSound(sound.decode(), loops?.toInt() ?: 0)
	}

	callbacks["setSoundVolume"] = luaFunction { sound: ByteString, volume: Number, rampTime: Number? ->
		self.setSoundVolume(sound.decode(), volume.toDouble(), rampTime?.toDouble() ?: 0.0)
	}

	callbacks["setSoundPitch"] = luaFunction { sound: ByteString, pitch: Number, rampTime: Number? ->
		self.setSoundPitch(sound.decode(), pitch.toDouble(), rampTime?.toDouble() ?: 0.0)
	}

	callbacks["stopAllSounds"] = luaFunction { sound: ByteString, rampTime: Number? ->
		self.stopAllSounds(sound.decode(), rampTime?.toDouble() ?: 0.0)
	}

	callbacks["effects"] = luaFunction {
		val groups = self.effects()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["hasEffect"] = luaFunction { effect: ByteString ->
		returnBuffer.setTo(self.hasEffect(effect.decode()))
	}

	callbacks["setEffectActive"] = luaFunction { effect: ByteString, state: Boolean ->
		self.setEffectActive(effect.decode(), state)
	}

	callbacks["parts"] = luaFunction {
		val groups = self.parts()
		val keys = newTable(groups.size, 0)
		groups.withIndex().forEach { (i, v) -> keys[i + 1L] = v }
		returnBuffer.setTo(keys)
	}

	callbacks["partPoint"] = luaFunction { part: ByteString, property: ByteString ->
		returnBuffer.setTo(self.partPoint(part.decode(), property.decode())?.let { from(it) })
	}

	callbacks["partPoly"] = luaFunction { part: ByteString, property: ByteString ->
		returnBuffer.setTo(self.partPoly(part.decode(), property.decode())?.let { from(it) })
	}

	callbacks["partProperty"] = luaFunction { part: ByteString, property: ByteString ->
		returnBuffer.setTo(from(self.partProperty(part.decode(), property.decode())))
	}

	callbacks["transformPoint"] = luaFunction { point: Table, part: ByteString ->
		returnBuffer.setTo(from(toVector2d(point).times(self.partTransformation(part.decode()))))
	}

	callbacks["transformPoly"] = luaFunction { poly: Table, part: ByteString ->
		returnBuffer.setTo(from(toPoly(poly).transform(self.partTransformation(part.decode()))))
	}
}
