package speiger.src.coreengine.rendering.gui.layout.constraints;

import java.util.List;
import java.util.function.BooleanSupplier;

import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.gui.layout.constraints.IConstraint.ISimpleConstraint;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;

public class Constraints {
	public static record Pixels(float value, boolean inverted) implements ISimpleConstraint {
		
		public static Pixels of(float value) { return new Pixels(value, false); }
		public static Pixels inverted(float value) { return new Pixels(value, true); }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			target.set(owner, inverted ? target.asArea().get(parent) - value : value);
		}
		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			context.addBound(inverted ? target.asArea().get(parent) - value : value, target.isXAxis());
		}
	}
	
	public static record Parent(float padding, boolean inv) implements ISimpleConstraint {
		
		public static Parent of() { return new Parent(0F, false); }
		public static Parent of(float padding) { return new Parent(padding, false); }
		public static Parent inverted() { return new Parent(0F, true); }
		public static Parent inverted(float padding) { return new Parent(padding, true); }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			if(inv) target.set(owner, target.isPosition() ? target.asArea().get(parent) - padding : padding * 2);
			else target.set(owner, target.isPosition() ? padding : target.get(parent) - padding * 2);
		}
		
		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			if(inv) context.addBound(target.isPosition() ? target.asArea().get(parent) - padding : padding * 2, target.isXAxis());
			else context.addBound(target.isPosition() ? padding : target.get(parent) - padding * 2, target.isXAxis());
		}
	}
	
	public static record Children(float padding, boolean includeChildOffsets) implements ISimpleConstraint {
		
		public static Children onlyBounds() { return new Children(0F, false); }
		public static Children onlyBounds(float padding) { return new Children(padding, false); }
		public static Children withPos() { return new Children(0F, true); }
		public static Children withPos(float padding) { return new Children(padding, true); }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			if(target.isPosition()) return;
			float value = 0F;
			List<IGuiBox> children = owner.children();
			for(int i = 0,m=children.size();i<m;i++) {
				IGuiBox child = children.get(i);
				value = Math.max(value, child.getBaseWidth() + (includeChildOffsets() ? child.getBaseX() : 0));
			}
			target.set(owner, value);
		}
		
		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			float value = 0F;
			List<IGuiBox> children = owner.children();
			for(int i = 0,m=children.size();i<m;i++) {
				IGuiBox child = children.get(i);
				value = Math.max(value, child.getBaseWidth() + (includeChildOffsets() ? child.getBaseX() : 0));
			}
			context.addBound(value, target.isXAxis());
		}
	}
	
	public static record Relative(float value, float padding) implements ISimpleConstraint {
		
		public static Relative of(float value) { return new Relative(value, 0F); }
		public static Relative of(float value, float padding) { return new Relative(value, padding); }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			float result = value * target.asArea().get(parent);
			target.set(owner, target.isPosition() ? result + padding : result - padding * 2F);
		}
		
		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			context.addBound(value * target.asArea().get(parent), target.isXAxis());
		}
	}
	
	public static record Weighted(float weight, float minimum, float padding, boolean inverted) implements ISimpleConstraint {
		public static Weighted of(float weight) { return new Weighted(weight, 0F, 0F, false); }
		public static Weighted padded(float weight, float padding) { return new Weighted(weight, 0F, padding, false); }
		public static Weighted minimum(float weight, float minimized) { return new Weighted(weight, minimized, 0F, false); }
		public static Weighted of(float weight, float minimized, float padding) { return new Weighted(weight, minimized, padding, false); }
		
		public static Weighted inverted(float weight) { return new Weighted(weight, 0F, 0F, true);}
		public static Weighted invertedPadded(float weight, float padding) { return new Weighted(weight, 0F, padding, true); }
		public static Weighted invertedMinimum(float weight, float minimized) { return new Weighted(weight, minimized, 0F, true); }
		public static Weighted inverted(float weight, float minimized, float padding) { return new Weighted(weight, minimized, padding, true); }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			float value = target.get(context);
			if(inverted) target.set(owner, target.isPosition() ? target.asArea().get(parent) - value - padding : value - padding * 2);
			else target.set(owner, target.isPosition() ? value + padding : value - padding * 2);
		}
		
		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			if(target.isPosition()) return;
			context.addWeight(weight, target.isXAxis());
			context.addBound(minimum, target.isXAxis());
		}
	}
	
	public static record Center() implements ISimpleConstraint {
		private static final Center INSTANCE = new Center();
		
		public static Center of() { return INSTANCE; }
		
		@Override
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			Target bounds = target.asArea();
			target.set(owner, (bounds.get(parent) * 0.5F) - (bounds.get(owner) * 0.5F));
		}

		@Override
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context) {
			context.addBound(target.get(owner), target.isXAxis());
		}
	}
	
	public static record Conditional(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) implements IConstraint {
		public static Conditional of(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) { return new Conditional(supplier, onTrue, onFalse); }
		
		@Override
		public void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) {
			(supplier.getAsBoolean() ? onTrue : onFalse).apply(owner, parent, target, context);
		}
		
		@Override
		public void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) {
			(supplier.getAsBoolean() ? onTrue : onFalse).fetch(owner, parent, target, context);
		}
	}
}
