package ru.dbotthepony.kstarbound.client.render

import com.google.common.collect.ImmutableMap
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.world.api.AbstractTileState
import ru.dbotthepony.kstarbound.world.api.TileColor

enum class RenderLayer {
	BackgroundOverlay,
	BackgroundTile,
	BackgroundTileMod,
	Platform,
	Plant,
	PlantDrop,
	Object,
	PreviewObject,
	BackParticle,
	Vehicle,
	Effect,
	Projectile,
	Monster,
	Npc,
	Player,
	ItemDrop,
	Liquid,
	MiddleParticle,
	ForegroundTile,
	ForegroundTileMod,
	ForegroundEntity,
	ForegroundOverlay,
	FrontParticle,
	Overlay;

	private val base = Point(this)

	fun point(offset: Long = 0L, index: Long = 0L, hueShift: Float = 0f, colorVariant: TileColor = TileColor.DEFAULT): Point {
		return if (offset == 0L && index == 0L && hueShift == 0f && colorVariant === TileColor.DEFAULT)
			base
		else
			Point(this, offset, index, hueShift, colorVariant)
	}

	fun point(): Point {
		return base
	}

	data class Point(val base: RenderLayer, val offset: Long = 0L, val index: Long = 0L, val hueShift: Float = 0f, val colorVariant: TileColor = TileColor.DEFAULT) : Comparable<Point> {
		override fun compareTo(other: Point): Int {
			if (this === other) return 0
			var cmp = base.compareTo(other.base)
			if (cmp == 0) cmp = offset.compareTo(other.offset)
			if (cmp == 0) cmp = index.compareTo(other.index)
			if (cmp == 0) cmp = hueShift.compareTo(other.hueShift)
			if (cmp == 0) cmp = colorVariant.compareTo(other.colorVariant)
			return cmp
		}
	}

	companion object {
		fun tileLayer(isBackground: Boolean, isModifier: Boolean, offset: Long = 0L, index: Long = 0L, hueShift: Float = 0f, colorVariant: TileColor = TileColor.DEFAULT): Point {
			if (isBackground && isModifier) {
				return BackgroundTileMod.point(offset, index, hueShift, colorVariant)
			} else if (isBackground) {
				return BackgroundTile.point(offset, index, hueShift, colorVariant)
			} else if (isModifier) {
				return ForegroundTileMod.point(offset, index, hueShift, colorVariant)
			} else {
				return ForegroundTile.point(offset, index, hueShift, colorVariant)
			}
		}

		fun tileLayer(isBackground: Boolean, isModifier: Boolean, tile: AbstractTileState): Point {
			if (isModifier) {
				return tileLayer(isBackground, true, tile.modifier?.value?.renderParameters?.zLevel ?: 0L, tile.modifier?.value?.modId?.toLong() ?: 0L, tile.modifierHueShift)
			} else {
				return tileLayer(isBackground, false, tile.material.value.renderParameters.zLevel, tile.material.value.materialId.toLong(), tile.hueShift)
			}
		}

		private val logger = LogManager.getLogger()
		private val lowercase: ImmutableMap<String, RenderLayer>

		init {
			val builder = ImmutableMap.Builder<String, RenderLayer>()

			for (value in entries) {
				builder.put(value.name.lowercase(), value)
			}

			lowercase = builder.build()
		}

		private fun perform(value: String, symbol: Char): Point {
			val before = value.substringBefore(symbol).lowercase()
			val after = value.substring(before.length)

			val enum = lowercase[before]

			if (enum == null) {
				logger.error("Unknown render layer: $before in $value; assuming BackgroundOverlay")
				return BackgroundOverlay.base
			}

			val num = after.toLongOrNull()

			if (num == null) {
				logger.error("Invalid render layer string: $value; assuming BackgroundOverlay")
				return BackgroundOverlay.base
			}

			return enum.point(num)
		}

		fun parse(value: String): Point {
			if ('+' in value) {
				return perform(value, '+')
			} else if ('-' in value) {
				return perform(value, '-')
			} else {
				val enum = lowercase[value.lowercase()]

				if (enum == null) {
					logger.error("Unknown render layer: $value; assuming BackgroundOverlay")
					return BackgroundOverlay.base
				}

				return enum.base
			}
		}
	}
}

