package ru.dbotthepony.kstarbound.defs

import com.google.common.collect.ImmutableSet
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.annotations.JsonAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kommons.io.readCollection
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeCollection
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import java.io.DataInputStream
import java.io.DataOutputStream
import java.util.function.Predicate

// enum class Type int32_t
// type -> isBlacklist
@JsonAdapter(PhysicsCategoryFilter.Adapter::class)
class PhysicsCategoryFilter(val isBlacklist: Boolean = false, val categories: ImmutableSet<String> = ImmutableSet.of()) : Predicate<String> {
	constructor(stream: DataInputStream, isLegacy: Boolean) : this(if (isLegacy) stream.readInt() > 0 else stream.readBoolean(), ImmutableSet.copyOf(stream.readCollection { readInternedString() }))

	fun write(stream: DataOutputStream, isLegacy: Boolean) {
		if (isLegacy)
			stream.writeInt(if (isBlacklist) 1 else 0)
		else
			stream.writeBoolean(isBlacklist)

		stream.writeCollection(categories) { writeBinaryString(it) }
	}

	fun test(categories: Collection<String>): Boolean {
		if (isBlacklist) {
			return categories.none { it in this.categories }
		} else {
			return categories.any { it in this.categories }
		}
	}

	override fun test(t: String): Boolean {
		if (isBlacklist) {
			return t !in this.categories
		} else {
			return t in this.categories
		}
	}

	@JsonFactory
	data class JsonData(
		val categoryWhitelist: ImmutableSet<String>? = null,
		val categoryBlacklist: ImmutableSet<String>? = null,
	)

	class Adapter(gson: Gson) : TypeAdapter<PhysicsCategoryFilter>() {
		private val data = gson.getAdapter(JsonData::class.java)

		override fun write(out: JsonWriter, value: PhysicsCategoryFilter) {
			data.write(out, JsonData(
				if (value.isBlacklist) null else value.categories,
				if (!value.isBlacklist) null else value.categories,
			))
		}

		override fun read(`in`: JsonReader): PhysicsCategoryFilter {
			val read = data.read(`in`)

			if (read.categoryBlacklist != null && read.categoryWhitelist != null) {
				throw JsonSyntaxException("Both categoryBlacklist and categoryWhitelist are specified")
			} else if (read.categoryBlacklist != null) {
				return PhysicsCategoryFilter(true, read.categoryBlacklist)
			} else if (read.categoryWhitelist != null) {
				return PhysicsCategoryFilter(true, read.categoryWhitelist)
			} else {
				return NEVER
			}
		}
	}

	companion object {
		val NEVER = PhysicsCategoryFilter()
	}
}
