package ru.dbotthepony.mc.otm.client.render

import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSyntaxException
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.core.set

enum class SpriteType {
	SINGLE {
		override fun toJson(value: AbstractMatterySprite): JsonObject {
			require(value is MatterySprite) { "Invalid skin element provided, expected SkinElement, got ${value::class.qualifiedName}" }

			return JsonObject().also {
				it["texture"] = JsonPrimitive(value.texture.toString())
				it["x"] = JsonPrimitive(value.x)
				it["y"] = JsonPrimitive(value.y)
				it["width"] = JsonPrimitive(value.width)
				it["height"] = JsonPrimitive(value.height)
				it["imageWidth"] = JsonPrimitive(value.atlasWidth)
				it["imageHeight"] = JsonPrimitive(value.atlasHeight)
				it["winding"] = JsonPrimitive(value.winding.name)
			}
		}

		override fun toNetwork(value: AbstractMatterySprite, buff: FriendlyByteBuf) {
			require(value is MatterySprite) { "Invalid skin element provided, expected SkinElement, got ${value::class.qualifiedName}" }

			buff.writeUtf(value.texture.toString())
			buff.writeFloat(value.x)
			buff.writeFloat(value.y)
			buff.writeFloat(value.width)
			buff.writeFloat(value.height)
			buff.writeFloat(value.atlasWidth)
			buff.writeFloat(value.atlasHeight)
			buff.writeEnum(value.winding)
		}

		override fun fromNetwork(buff: FriendlyByteBuf): AbstractMatterySprite {
			val texture = ResourceLocation(buff.readUtf())
			val x = buff.readFloat()
			val y = buff.readFloat()
			val width = buff.readFloat()
			val height = buff.readFloat()
			val imageWidth = buff.readFloat()
			val imageHeight = buff.readFloat()
			val winding = buff.readEnum(UVWindingOrder::class.java)

			return MatterySprite(texture, x, y, width, height, imageWidth, imageHeight, winding)
		}

		override fun fromJson(element: JsonObject): AbstractMatterySprite {
			val texture = element["texture"]?.asString ?: throw JsonSyntaxException("Missing texture element")
			val x = element["x"]?.asFloat ?: throw JsonSyntaxException("Missing x element")
			val y = element["y"]?.asFloat ?: throw JsonSyntaxException("Missing y element")
			val width = element["width"]?.asFloat ?: throw JsonSyntaxException("Missing width element")
			val height = element["height"]?.asFloat ?: throw JsonSyntaxException("Missing height element")
			val imageWidth = element["imageWidth"]?.asFloat ?: throw JsonSyntaxException("Missing imageWidth element")
			val imageHeight = element["imageHeight"]?.asFloat ?: throw JsonSyntaxException("Missing imageHeight element")
			val winding = element["winding"]?.asString ?: throw JsonSyntaxException("Missing winding element")

			return MatterySprite(ResourceLocation.tryParse(texture) ?: throw JsonSyntaxException("Invalid resource location: $texture"), x, y, width, height, imageWidth, imageHeight, UVWindingOrder.valueOf(winding))
		}
	},
	SUBSPRITE {
		override fun toJson(value: AbstractMatterySprite): JsonObject {
			require(value is SubMatterySprite) { "Invalid skin element provided, expected SubSkinElement, got ${value::class.qualifiedName}" }

			return JsonObject().also {
				it["parent"] = value.parent.toJson()
				it["xOffset"] = JsonPrimitive(value.xOffset)
				it["yOffset"] = JsonPrimitive(value.yOffset)
				it["subWidth"] = JsonPrimitive(value.width)
				it["subHeight"] = JsonPrimitive(value.height)
			}
		}

		override fun toNetwork(value: AbstractMatterySprite, buff: FriendlyByteBuf) {
			require(value is SubMatterySprite) { "Invalid skin element provided, expected SubSkinElement, got ${value::class.qualifiedName}" }

			value.parent.toNetwork(buff)
			buff.writeFloat(value.xOffset)
			buff.writeFloat(value.yOffset)
			buff.writeFloat(value.width)
			buff.writeFloat(value.height)
		}

		override fun fromNetwork(buff: FriendlyByteBuf): AbstractMatterySprite {
			val parent = Companion.fromNetwork(buff)
			val xOffset = buff.readFloat()
			val yOffset = buff.readFloat()
			val subWidth = buff.readFloat()
			val subHeight = buff.readFloat()

			return SubMatterySprite(parent, xOffset, yOffset, subWidth, subHeight)
		}

		override fun fromJson(element: JsonObject): AbstractMatterySprite {
			val parent = element["parent"] as? JsonObject ?: throw JsonSyntaxException("Invalid parent")
			val xOffset = element["xOffset"].asFloat
			val yOffset = element["yOffset"].asFloat
			val subWidth = element["subWidth"].asFloat
			val subHeight = element["subHeight"].asFloat

			return SubMatterySprite(Companion.fromJson(parent), xOffset, yOffset, subWidth, subHeight)
		}
	};

	protected abstract fun toJson(value: AbstractMatterySprite): JsonObject
	protected abstract fun toNetwork(value: AbstractMatterySprite, buff: FriendlyByteBuf)
	protected abstract fun fromNetwork(buff: FriendlyByteBuf): AbstractMatterySprite
	protected abstract fun fromJson(element: JsonObject): AbstractMatterySprite

	fun toActualJson(value: AbstractMatterySprite): JsonObject {
		return toJson(value).also {
			it["type"] = JsonPrimitive(name)
		}
	}

	fun toActualNetwork(value: AbstractMatterySprite, buff: FriendlyByteBuf) {
		buff.writeEnum(this)
		toNetwork(value, buff)
	}

	companion object {
		fun fromNetwork(buff: FriendlyByteBuf): AbstractMatterySprite {
			val type = buff.readEnum(SpriteType::class.java)
			return type.fromNetwork(buff)
		}

		fun fromJson(element: JsonObject): AbstractMatterySprite {
			val type = SpriteType.valueOf(element["type"].asString)
			return type.fromJson(element)
		}
	}
}
