package ru.dbotthepony.kstarbound.defs.actor.player

import com.google.common.collect.ImmutableSet
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import ru.dbotthepony.kommons.guava.immutableSet
import ru.dbotthepony.kommons.io.readCollection
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeCollection
import ru.dbotthepony.kstarbound.io.readDouble
import ru.dbotthepony.kstarbound.io.readInternedString
import ru.dbotthepony.kstarbound.io.writeDouble
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 ShipUpgrades(
	val shipLevel: Int = 0,
	val maxFuel: Int = 0,
	val crewSize: Int = 0,
	val fuelEfficiency: Double = 1.0,
	val shipSpeed: Double = 30.0,
	val capabilities: ImmutableSet<String> = ImmutableSet.of()
) {
	constructor(stream: DataInputStream, isLegacy: Boolean) : this(
		stream.readInt(),
		stream.readInt(),
		stream.readInt(),
		stream.readDouble(isLegacy),
		stream.readDouble(isLegacy),
		ImmutableSet.copyOf(stream.readCollection { readInternedString() })
	)

	fun apply(upgrades: ShipUpgrades): ShipUpgrades {
		return ShipUpgrades(
			shipLevel = shipLevel.coerceAtLeast(upgrades.shipLevel),
			maxFuel = maxFuel.coerceAtLeast(upgrades.maxFuel),
			crewSize = crewSize.coerceAtLeast(upgrades.crewSize),
			fuelEfficiency = fuelEfficiency.coerceAtLeast(upgrades.fuelEfficiency),
			shipSpeed = shipSpeed.coerceAtLeast(upgrades.shipSpeed),
			capabilities = immutableSet { capabilities.forEach(::accept); upgrades.capabilities.forEach(::accept) }
		)
	}

	fun addCapability(capability: String): ShipUpgrades {
		val copy = ObjectArraySet(capabilities)
		copy.add(capability)
		return copy(capabilities = ImmutableSet.copyOf(copy))
	}

	fun removeCapability(capability: String): ShipUpgrades {
		val copy = ObjectArraySet(capabilities)
		copy.remove(capability)
		return copy(capabilities = ImmutableSet.copyOf(copy))
	}

	fun write(stream: DataOutputStream, isLegacy: Boolean) {
		stream.writeInt(shipLevel)
		stream.writeInt(maxFuel)
		stream.writeInt(crewSize)
		stream.writeDouble(fuelEfficiency, isLegacy)
		stream.writeDouble(shipSpeed, isLegacy)
		stream.writeCollection(capabilities) { writeBinaryString(it) }
	}

	companion object {
		val CODEC = nativeCodec(::ShipUpgrades, ShipUpgrades::write)
		val LEGACY_CODEC = legacyCodec(::ShipUpgrades, ShipUpgrades::write)
	}
}
