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

import java.util.function.BooleanSupplier;

import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Center;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Children;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Conditional;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Parent;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Pixels;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Relative;
import speiger.src.coreengine.rendering.gui.layout.constraints.Constraints.Weighted;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;	

public interface IConstraint {
	public void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context);
	public void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context);
	
	public static IConstraint pixels(float value) { return Pixels.of(value); }
	public static IConstraint pixelsInv(float value) { return Pixels.inverted(value); }
	
	public static IConstraint parent() { return Parent.of(); }
	public static IConstraint parent(float padding) { return Parent.of(padding); }
	public static IConstraint parentInv() { return Parent.inverted(); }
	public static IConstraint parentInv(float padding) { return Parent.inverted(padding); }
	
	public static IConstraint children() { return Children.onlyBounds(); }
	public static IConstraint children(float padding) { return Children.onlyBounds(padding); }
	public static IConstraint childrenPos() { return Children.withPos(); }
	public static IConstraint childrenPos(float padding) { return Children.withPos(padding); }
	
	public static IConstraint relative(float value) { return Relative.of(value); }
	public static IConstraint relative(float value, float padding) { return Relative.of(value, padding); }
	
	public static IConstraint weightedPos() { return Weighted.of(0F); }
	public static IConstraint weightedInvPos() { return Weighted.inverted(0F); }
	public static IConstraint weighted(float weight) { return Weighted.of(weight); }
	public static IConstraint weightedPad(float weight, float padding) { return Weighted.padded(weight, padding); }
	public static IConstraint weightedMin(float weight, float minimum) { return Weighted.minimum(weight, minimum); }
	public static IConstraint weighted(float weight, float minimum, float padding) { return Weighted.of(weight, minimum, padding); }
	public static IConstraint weightedInv(float weight) { return Weighted.inverted(weight);}
	public static IConstraint weightedInvPad(float weight, float padding) { return Weighted.invertedPadded(weight, padding); }
	public static IConstraint weightedInvMin(float weight, float minimum) { return Weighted.invertedMinimum(weight, minimum); }
	public static IConstraint weightedInv(float weight, float minimum, float padding) { return Weighted.inverted(weight, minimum, padding); }
	
	public static IConstraint center() { return Center.of(); }
	public static IConstraint conditional(BooleanSupplier supplier, IConstraint onTrue, IConstraint onFalse) { return Conditional.of(supplier, onTrue, onFalse); }
	
	public static interface ISimpleConstraint extends IConstraint {
		@Override
		default void apply(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) { apply(owner.getBox(), parent == null ? owner.screen().getBox() : parent.getBox(), target, context); }
		public void apply(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context);
		
		@Override
		default void fetch(GuiComponent owner, GuiComponent parent, Target target, ConstrainedContext context) { fetch(owner.getBox(), parent == null ? owner.screen().getBox() : parent.getBox(), target, context); }
		public void fetch(IGuiBox owner, IGuiBox parent, Target target, ConstrainedContext context);
	}
	
	public static enum Target {
		X,
		Y,
		WIDTH,
		HEIGHT;
		
		static final Target[] BY_INDEX = values();
		
		public static Target by(int index) { return BY_INDEX[index & 3]; }
		public static Target pos(boolean x) { return x ? X : Y; }
		public static Target bounds(boolean x) { return x ? WIDTH : HEIGHT; }
		public boolean isPosition() { return this == X || this == Y; }
		public boolean isXAxis() { return this == X || this == WIDTH; }
		public Target asArea() {
			return switch(this) {
				case X -> WIDTH;
				case Y -> HEIGHT;
				default -> this;
			};
		}
		
		public float get(GuiComponent comp) { return get(comp.getBox()); }
		public void set(GuiComponent comp, float value) { set(comp.getBox(), value); }
		public float get(ConstrainedContext context) { return (isPosition() ? context.getOffset(isXAxis()) : context.get(isXAxis())); }
		
		public float get(IGuiBox box) {
			return switch(this) {
				case X -> box.getBaseX();
				case Y -> box.getBaseY();
				case WIDTH -> box.getBaseWidth();
				case HEIGHT -> box.getBaseHeight();
			};
		}
		
		public void set(IGuiBox box, float value) {
			switch(this) {
				case X -> box.setX(value);
				case Y -> box.setY(value);
				case WIDTH -> box.setWidth(value);
				case HEIGHT -> box.setHeight(value);
			}
		}
	}
}
