package dev.kkorolyov.killstreek.component;

import dev.kkorolyov.pancake.platform.entity.Component;

/**
 * Applies damage values to an entity.
 */
public class Damage implements Component {
	private int value;
	private float effectiveValue;
	private int remaining;

	private float duration;
	private float elapsed;

	/**
	 * Constructs a new, expired damage.
	 */
	public Damage() {
		this(0, 0);
	}
	/**
	 * Constructs a new damage which applies its entire value at once.
	 * @param value damage value
	 */
	public Damage(int value) {
		setValue(value);
	}
	/**
	 * Constructs a new damage.
	 * @param value damage value
	 * @param duration seconds over which to distribute applied damage, values close to {@code 0} result in effectively immediate damage application
	 */
	public Damage(int value, float duration) {
		setValue(value, duration);
	}

	/**
	 * Applies the negation of this damage's value to some health.
	 * If this damage has a duration, the appropriate portion of its value is applied instead.
	 * If this damage has expired, this method does nothing.
	 * @param health damaged health
	 * @param dt seconds elapsed since previous invocation of this method
	 * @return {@code false} if expired, else {@code true}
	 */
	public boolean apply(Health health, float dt) {
		if (isExpired()) return false;
		elapsed += dt;

		effectiveValue += (isExpired()) ? remaining : value * dt;
		if (Float.compare(Math.abs(effectiveValue), 1) >= 0) {
			remaining -= (int) effectiveValue;

			health.change((int) -effectiveValue);
			effectiveValue = 0;
		}
		return true;
	}

	/**
	 * Resets elapsed damage duration.
	 */
	public void reset() {
		setValue(value, duration);
	}

	/** @return {@code true} if subsequent damage applications will do nothing */
	public boolean isExpired() {
		return elapsed >= duration;
	}

	/** @return damage value */
	public int getValue() {
		return value;
	}
	/**
	 * @param value new damage value
	 * @return {@code this}
	 */
	public Damage setValue(int value) {
		return setValue(value, Float.MIN_NORMAL);
	}
	/**
	 * @param value new damage value
	 * @param duration seconds over which to distribute applied damage, values close to {@code 0} result in effectively immediate damage application
	 * @return {@code this}
	 */
	public Damage setValue(int value, float duration) {
		this.value = value;
		effectiveValue = 0;
		remaining = value;

		this.duration = duration;
		elapsed = Math.min(0, duration);

		return this;
	}

	/** @return seconds over which damage distributed */
	public float getDuration() {
		return duration;
	}
}

@@ -214,21 +214,22 @@
 private 
-float
+double
  effecti
@@ -271,21 +271,20 @@
 private 
-f
 lo
-at
+ng
  duratio
@@ -295,21 +295,20 @@
 private 
-f
 lo
-at
+ng
  elapsed
@@ -628,39 +628,42 @@
 @param duration 
-seconds
+%7B@code ns%7D
  over which to d
@@ -695,32 +695,45 @@
  values close to
+ (but not at)
  %7B@code 0%7D resul
@@ -801,37 +801,36 @@
 mage(int value, 
-f
 lo
-at
+ng
  duration) %7B%0A%09%09s
@@ -1125,23 +1125,26 @@
 aram dt 
-seconds
+%7B@code ns%7D
  elapsed
@@ -1283,21 +1283,20 @@
 health, 
-f
 lo
-at
+ng
  dt) %7B%0A%09
@@ -1405,18 +1405,53 @@
 e * 
-dt
+(Math.min((double) dt / duration, 1))
 ;%0A%09%09if (
 Floa
@@ -1446,21 +1446,22 @@
 ;%0A%09%09if (
-Float
+Double
 .compare
@@ -1505,27 +1505,28 @@
 %0A%09%09%09
-remaining -
+int removed 
 = (int) 
 effe
@@ -1521,16 +1521,17 @@
 = (int) 
+-
 effectiv
@@ -1546,43 +1546,55 @@
 %0A%09%09%09
-health.change((int) -effectiveValue
+remaining += removed;%0A%0A%09%09%09health.change(removed
 );%0A%09
@@ -2091,24 +2091,9 @@
 ue, 
-Float.MIN_NORMAL
+1
 );%0A%09
@@ -2217,16 +2217,29 @@
 close to
+ (but not at)
  %7B@code 
@@ -2357,21 +2357,20 @@
  value, 
-f
 lo
-at
+ng
  duratio

Migrate Damage to ns