package ru.dbotthepony.kstarbound.defs.actor

import ru.dbotthepony.kommons.io.readVector2d
import ru.dbotthepony.kommons.io.readVector2f
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeStruct2d
import ru.dbotthepony.kommons.io.writeStruct2f
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.vector.Vector2d
import ru.dbotthepony.kstarbound.io.readColor
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.io.writeColor
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.network.syncher.legacyCodec
import ru.dbotthepony.kstarbound.network.syncher.nativeCodec
import java.io.DataInputStream
import java.io.DataOutputStream

@JsonFactory
data class HumanoidData(
	val name: String = "Humanoid",
	val species: String = "human",
	val gender: Gender = Gender.MALE,

	val hairGroup: String = "hair",
	// Must have :normal and :climb
	val hairType: String = "male1",
	val hairDirectives: String = "",
	val bodyDirectives: String = "",
	val emoteDirectives: String = bodyDirectives,
	val facialHairGroup: String = "",
	val facialHairType: String = "",
	val facialHairDirectives: String = "",
	val facialMaskGroup: String = "",
	val facialMaskType: String = "",
	val facialMaskDirectives: String = "",

	val color: RGBAColor = RGBAColor.BLACK,

	val personalityIdle: String = "",
	val personalityArmIdle: String = "",
	val personalityHeadOffset: Vector2d = Vector2d.ZERO,
	val personalityArmOffset: Vector2d = Vector2d.ZERO,

	val imagePath: String? = null,
) : IPersonality {
	override val idle: String
		get() = personalityIdle
	override val armIdle: String
		get() = personalityArmIdle
	override val handOffset: Vector2d
		get() = personalityHeadOffset
	override val armOffset: Vector2d
		get() = personalityArmOffset

	fun write(stream: DataOutputStream, isLegacy: Boolean) {
		stream.writeBinaryString(name)
		stream.writeBinaryString(species)
		stream.writeByte(gender.ordinal)
		stream.writeBinaryString(hairGroup)
		stream.writeBinaryString(hairType)
		stream.writeBinaryString(hairDirectives)
		stream.writeBinaryString(bodyDirectives)
		stream.writeBinaryString(emoteDirectives)
		stream.writeBinaryString(facialHairGroup)
		stream.writeBinaryString(facialHairType)
		stream.writeBinaryString(facialHairDirectives)
		stream.writeBinaryString(facialMaskGroup)
		stream.writeBinaryString(facialMaskType)
		stream.writeBinaryString(facialMaskDirectives)
		stream.writeBinaryString(personalityIdle)
		stream.writeBinaryString(personalityArmIdle)

		if (isLegacy) {
			stream.writeStruct2f(personalityHeadOffset.toFloatVector())
			stream.writeStruct2f(personalityArmOffset.toFloatVector())
			stream.writeByte(color.redInt)
			stream.writeByte(color.greenInt)
			stream.writeByte(color.blueInt)
			stream.writeByte(color.alphaInt)
		} else {
			stream.writeStruct2d(personalityHeadOffset)
			stream.writeStruct2d(personalityArmOffset)
			stream.writeColor(color)
		}

		stream.writeBoolean(imagePath != null)

		if (imagePath != null)
			stream.writeBinaryString(imagePath)
	}

	companion object {
		val CODEC = nativeCodec(::read, HumanoidData::write)
		val LEGACY_CODEC = legacyCodec(::read, HumanoidData::write)

		fun read(stream: DataInputStream, isLegacy: Boolean): HumanoidData {
			val name: String = stream.readInternedString()
			val species: String = stream.readInternedString()
			val gender: Gender = Gender.entries[stream.readUnsignedByte()]

			val hairGroup = stream.readInternedString()
			val hairType = stream.readInternedString()
			val hairDirectives = stream.readInternedString()
			val bodyDirectives = stream.readInternedString()
			val emoteDirectives = stream.readInternedString()
			val facialHairGroup = stream.readInternedString()
			val facialHairType = stream.readInternedString()
			val facialHairDirectives = stream.readInternedString()
			val facialMaskGroup = stream.readInternedString()
			val facialMaskType = stream.readInternedString()
			val facialMaskDirectives = stream.readInternedString()

			val personalityIdle: String = stream.readInternedString()
			val personalityArmIdle: String = stream.readInternedString()
			val personalityHeadOffset: Vector2d = if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d()
			val personalityArmOffset: Vector2d = if (isLegacy) stream.readVector2f().toDoubleVector() else stream.readVector2d()

			val color: RGBAColor = if (isLegacy) RGBAColor(stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte(), stream.readUnsignedByte()) else stream.readColor()
			val imagePath: String? = if (stream.readBoolean()) stream.readInternedString() else null

			return HumanoidData(
				name, species, gender, hairGroup, hairType, hairDirectives, bodyDirectives,
				emoteDirectives, facialHairGroup, facialHairType, facialHairDirectives,
				facialMaskGroup, facialMaskType, facialMaskDirectives, color, personalityIdle,
				personalityArmIdle, personalityHeadOffset, personalityArmOffset, imagePath)
		}
	}
}
