package speiger.src.coreengine.rendering.gui.animation;

import java.util.Objects;
import java.util.function.BiConsumer;

import speiger.src.collections.objects.functions.consumer.ObjectFloatConsumer;
import speiger.src.collections.objects.functions.function.ToFloatFunction;
import speiger.src.collections.objects.maps.impl.misc.LinkedEnum2ObjectMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap.Entry;
import speiger.src.collections.objects.utils.maps.Object2ObjectMaps;
import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;

public class GuiAnimation {
	Object2ObjectMap<Target, IAction> actions;
	BiConsumer<GuiAnimation, GuiComponent> listener;
	float duration;
	
	public GuiAnimation(Object2ObjectMap<Target, IAction> actions, BiConsumer<GuiAnimation, GuiComponent> listener, float duration) {
		this.actions = actions;
		this.listener = listener;
		this.duration = duration;
	}
	
	public Builder copy() { return new Builder(this); }
	public static Builder of() { return new Builder(); }
	
	@Override
	public boolean equals(Object obj) { return obj instanceof GuiAnimation animation && animation.actions.equals(actions); }
	@Override
	public int hashCode() { return actions.hashCode(); }
	public BiConsumer<GuiAnimation, GuiComponent> listener() { return listener; }
	public float duration() { return duration; }
	
	public void apply(ToFloatFunction<Target> getter, ObjectFloatConsumer<Target> setter, float progress) {
		for(Entry<Target, IAction> entry : Object2ObjectMaps.fastIterable(actions)) {
			IAction action = entry.getValue();
			action.apply(entry.getKey(), getter, setter, Math.min(progress, action.duration()));
		}
	}
	
	public static class Builder {
		Object2ObjectMap<Target, IAction> actions = new LinkedEnum2ObjectMap<>(Target.class);
		BiConsumer<GuiAnimation, GuiComponent> listener;
		
		private Builder() {}
		private Builder(GuiAnimation owner) {
			this.actions.putAll(owner.actions);
		}
		
		public Builder add(Target target, IAction action) {
			this.actions.put(target, Objects.requireNonNull(action));
			return this;
		}
		
		public Builder withListener(BiConsumer<GuiAnimation, GuiComponent> listener) {
			this.listener = listener;
			return this;
		}
		
		public GuiAnimation build() {
			float duration = 0F;
			for(IAction action : actions.values()) {
				duration = Math.max(duration, action.duration());
			}
			return new GuiAnimation(actions, listener, duration);
		}
	}
	
	public static enum Target {
		X(IGuiBox::getBaseX),
		Y(IGuiBox::getBaseY),
		WIDTH(IGuiBox::getBaseWidth),
		HEIGHT(IGuiBox::getBaseHeight),
		SCALE(IGuiBox::getBaseScale);
		
		ToFloatFunction<IGuiBox> provider;
		
		private Target(ToFloatFunction<IGuiBox> provider) {
			this.provider = provider;
		}
		
		public int changeState() { return this == X || this == Y ? 1 : 2; }
		public float get(GuiComponent component) { return provider.applyAsFloat(component.getBox()); }
	}
}
