package ru.dbotthepony.kstarbound.defs

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kstarbound.json.builder.JsonAlias
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.world.physics.Poly

sealed class BaseMovementParameters {
	abstract val mass: Double?
	abstract val gravityMultiplier: Double?
	abstract val liquidBuoyancy: Double?
	abstract val airBuoyancy: Double?
	abstract val bounceFactor: Double?

	// If set to true, during an update that has more than one internal movement
	// step, the movement will stop on the first bounce.
	abstract val stopOnFirstBounce: Boolean?

	// Cheat when sliding on the ground, by trying to correct upwards before
	// other directions (within a set limit).  Allows smooth sliding along
	// horizontal ground without losing horizontal speed.
	abstract val enableSurfaceSlopeCorrection: Boolean?
	abstract val slopeSlidingFactor: Double?

	abstract val maxMovementPerStep: Double?
	abstract val maximumCorrection: Double?
	abstract val speedLimit: Double?

	abstract val stickyCollision: Boolean?
	abstract val stickyForce: Double?

	abstract val airFriction: Double?
	abstract val liquidFriction: Double?
	abstract val groundFriction: Double?

	abstract val collisionEnabled: Boolean?
	abstract val frictionEnabled: Boolean?
	abstract val gravityEnabled: Boolean?

	abstract val maximumPlatformCorrection: Double?
	abstract val maximumPlatformCorrectionVelocityFactor: Double?

	abstract val physicsEffectCategories: ImmutableSet<String>?
}

@JsonFactory
data class MovementParameters(
	override val mass: Double? = null,
	override val gravityMultiplier: Double? = null,
	override val liquidBuoyancy: Double? = null,
	override val airBuoyancy: Double? = null,
	override val bounceFactor: Double? = null,
	override val stopOnFirstBounce: Boolean? = null,
	override val enableSurfaceSlopeCorrection: Boolean? = null,
	override val slopeSlidingFactor: Double? = null,
	override val maxMovementPerStep: Double? = null,
	override val maximumCorrection: Double? = null,
	override val speedLimit: Double? = null,
	override val stickyCollision: Boolean? = null,
	override val stickyForce: Double? = null,
	override val airFriction: Double? = null,
	override val liquidFriction: Double? = null,
	override val groundFriction: Double? = null,
	override val collisionEnabled: Boolean? = null,
	override val frictionEnabled: Boolean? = null,
	override val gravityEnabled: Boolean? = null,
	override val maximumPlatformCorrection: Double? = null,
	override val maximumPlatformCorrectionVelocityFactor: Double? = null,
	override val physicsEffectCategories: ImmutableSet<String>? = null,

	val discontinuityThreshold: Float? = null,
	val collisionPoly: Either<Poly, ImmutableList<Poly>>? = null,
	val ignorePlatformCollision: Boolean? = null,
	val restDuration: Int? = null,
) : BaseMovementParameters() {
	fun merge(other: MovementParameters): MovementParameters {
		return MovementParameters(
			mass = other.mass ?: mass,
			gravityMultiplier = other.gravityMultiplier ?: gravityMultiplier,
			liquidBuoyancy = other.liquidBuoyancy ?: liquidBuoyancy,
			airBuoyancy = other.airBuoyancy ?: airBuoyancy,
			bounceFactor = other.bounceFactor ?: bounceFactor,
			stopOnFirstBounce = other.stopOnFirstBounce ?: stopOnFirstBounce,
			enableSurfaceSlopeCorrection = other.enableSurfaceSlopeCorrection ?: enableSurfaceSlopeCorrection,
			slopeSlidingFactor = other.slopeSlidingFactor ?: slopeSlidingFactor,
			maxMovementPerStep = other.maxMovementPerStep ?: maxMovementPerStep,
			maximumCorrection = other.maximumCorrection ?: maximumCorrection,
			speedLimit = other.speedLimit ?: speedLimit,
			stickyCollision = other.stickyCollision ?: stickyCollision,
			stickyForce = other.stickyForce ?: stickyForce,
			airFriction = other.airFriction ?: airFriction,
			liquidFriction = other.liquidFriction ?: liquidFriction,
			groundFriction = other.groundFriction ?: groundFriction,
			collisionEnabled = other.collisionEnabled ?: collisionEnabled,
			frictionEnabled = other.frictionEnabled ?: frictionEnabled,
			gravityEnabled = other.gravityEnabled ?: gravityEnabled,
			maximumPlatformCorrection = other.maximumPlatformCorrection ?: maximumPlatformCorrection,
			maximumPlatformCorrectionVelocityFactor = other.maximumPlatformCorrectionVelocityFactor ?: maximumPlatformCorrectionVelocityFactor,
			physicsEffectCategories = other.physicsEffectCategories ?: physicsEffectCategories,
			discontinuityThreshold = other.discontinuityThreshold ?: discontinuityThreshold,
			collisionPoly = other.collisionPoly ?: collisionPoly,
			ignorePlatformCollision = other.ignorePlatformCollision ?: ignorePlatformCollision,
			restDuration = other.restDuration ?: restDuration,
		)
	}

	companion object {
		val EMPTY = MovementParameters()
	}
}

@JsonFactory
data class ActorMovementParameters(
	override val mass: Double? = null,
	override val gravityMultiplier: Double? = null,
	override val liquidBuoyancy: Double? = null,
	override val airBuoyancy: Double? = null,
	override val bounceFactor: Double? = null,
	override val stopOnFirstBounce: Boolean? = null,
	override val enableSurfaceSlopeCorrection: Boolean? = null,
	override val slopeSlidingFactor: Double? = null,
	override val maxMovementPerStep: Double? = null,
	override val maximumCorrection: Double? = null,
	override val speedLimit: Double? = null,
	override val stickyCollision: Boolean? = null,
	override val stickyForce: Double? = null,
	override val airFriction: Double? = null,
	override val liquidFriction: Double? = null,
	override val groundFriction: Double? = null,
	override val collisionEnabled: Boolean? = null,
	override val frictionEnabled: Boolean? = null,
	override val gravityEnabled: Boolean? = null,
	override val maximumPlatformCorrection: Double? = null,
	override val maximumPlatformCorrectionVelocityFactor: Double? = null,
	override val physicsEffectCategories: ImmutableSet<String>? = null,

	@JsonAlias("collisionPoly")
	val standingPoly: Either<Poly, ImmutableList<Poly>>? = null,
	@JsonAlias("collisionPoly")
	val crouchingPoly: Either<Poly, ImmutableList<Poly>>? = null,

	val walkSpeed: Double? = null,
	val runSpeed: Double? = null,
	val flySpeed: Double? = null,

	val minimumLiquidPercentage: Double? = null,
	val liquidImpedance: Double? = null,
	val normalGroundFriction: Double? = null,
	val ambulatingGroundFriction: Double? = null,
	val groundForce: Double? = null,
	val airForce: Double? = null,
	val liquidForce: Double? = null,

	val airJumpProfile: JumpProfile = JumpProfile.EMPTY,
	val liquidJumpProfile: JumpProfile = JumpProfile.EMPTY,

	val fallStatusSpeedMin: Double? = null,
	val fallThroughSustainFrames: Int? = null,

	val groundMovementMinimumSustain: Double? = null,
	val groundMovementMaximumSustain: Double? = null,
	val groundMovementCheckDistance: Double? = null,

	val pathExploreRate: Double? = null,
) : BaseMovementParameters() {
	fun merge(other: ActorMovementParameters): ActorMovementParameters {
		return ActorMovementParameters(
			mass = other.mass ?: mass,
			gravityMultiplier = other.gravityMultiplier ?: gravityMultiplier,
			liquidBuoyancy = other.liquidBuoyancy ?: liquidBuoyancy,
			airBuoyancy = other.airBuoyancy ?: airBuoyancy,
			bounceFactor = other.bounceFactor ?: bounceFactor,
			stopOnFirstBounce = other.stopOnFirstBounce ?: stopOnFirstBounce,
			enableSurfaceSlopeCorrection = other.enableSurfaceSlopeCorrection ?: enableSurfaceSlopeCorrection,
			slopeSlidingFactor = other.slopeSlidingFactor ?: slopeSlidingFactor,
			maxMovementPerStep = other.maxMovementPerStep ?: maxMovementPerStep,
			maximumCorrection = other.maximumCorrection ?: maximumCorrection,
			speedLimit = other.speedLimit ?: speedLimit,
			stickyCollision = other.stickyCollision ?: stickyCollision,
			stickyForce = other.stickyForce ?: stickyForce,
			airFriction = other.airFriction ?: airFriction,
			liquidFriction = other.liquidFriction ?: liquidFriction,
			groundFriction = other.groundFriction ?: groundFriction,
			collisionEnabled = other.collisionEnabled ?: collisionEnabled,
			frictionEnabled = other.frictionEnabled ?: frictionEnabled,
			gravityEnabled = other.gravityEnabled ?: gravityEnabled,
			maximumPlatformCorrection = other.maximumPlatformCorrection ?: maximumPlatformCorrection,
			maximumPlatformCorrectionVelocityFactor = other.maximumPlatformCorrectionVelocityFactor ?: maximumPlatformCorrectionVelocityFactor,
			physicsEffectCategories = other.physicsEffectCategories ?: physicsEffectCategories,
			standingPoly = other.standingPoly ?: standingPoly,
			crouchingPoly = other.crouchingPoly ?: crouchingPoly,
			walkSpeed = other.walkSpeed ?: walkSpeed,
			runSpeed = other.runSpeed ?: runSpeed,
			flySpeed = other.flySpeed ?: flySpeed,
			minimumLiquidPercentage = other.minimumLiquidPercentage ?: minimumLiquidPercentage,
			liquidImpedance = other.liquidImpedance ?: liquidImpedance,
			normalGroundFriction = other.normalGroundFriction ?: normalGroundFriction,
			ambulatingGroundFriction = other.ambulatingGroundFriction ?: ambulatingGroundFriction,
			groundForce = other.groundForce ?: groundForce,
			airForce = other.airForce ?: airForce,
			liquidForce = other.liquidForce ?: liquidForce,
			airJumpProfile = airJumpProfile.merge(other.airJumpProfile),
			liquidJumpProfile = liquidJumpProfile.merge(other.liquidJumpProfile),
			fallStatusSpeedMin = other.fallStatusSpeedMin ?: fallStatusSpeedMin,
			fallThroughSustainFrames = other.fallThroughSustainFrames ?: fallThroughSustainFrames,
			groundMovementMinimumSustain = other.groundMovementMinimumSustain ?: groundMovementMinimumSustain,
			groundMovementMaximumSustain = other.groundMovementMaximumSustain ?: groundMovementMaximumSustain,
			groundMovementCheckDistance = other.groundMovementCheckDistance ?: groundMovementCheckDistance,
		)
	}

	companion object {
		val EMPTY = ActorMovementParameters()
	}
}
