package speiger.src.coreengine.rendering.guiOld.helper.constrains;

import java.util.function.BooleanSupplier;

import speiger.src.collections.floats.functions.FloatSupplier;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.rendering.guiOld.GuiComponent;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrain.Target;

public class Constrains
{
	Constrain[] constrains = new Constrain[4];
	
	private Constrains(Constrain[] array)
	{
		System.arraycopy(array, 0, constrains, 0, 4);
	}
	
	public Constrains(Constrain xPos, Constrain yPos, Constrain width, Constrain height)
	{
		constrains[0] = xPos;
		constrains[1] = yPos;
		constrains[2] = width;
		constrains[3] = height;
	}
	
	public void setOwner(GuiComponent owner, GuiComponent parent)
	{
		for(int i = 0;i<4;i++) {
			if(constrains[i] != null) {
				constrains[i].setComponents(owner, parent, Target.by(i));
			}
		}
	}
	
	public void onComponentChanged()
	{
		for(int i = 0;i<4;i++) {
			if(constrains[i] != null) constrains[i].apply();
		}
	}
	
	public static Constrains parent() { return new Constrains(new ParentConstrain(), new ParentConstrain(), new ParentConstrain(), new ParentConstrain()); }
	public static Constrains parent(float padding) { return new Constrains(new ParentConstrain(padding), new ParentConstrain(padding), new ParentConstrain(padding), new ParentConstrain(padding)); }
	public static Constrains parent(float xPadding, float yPadding) { return new Constrains(new ParentConstrain(xPadding), new ParentConstrain(yPadding), new ParentConstrain(xPadding), new ParentConstrain(yPadding)); }
	public static Constrains parent(float x, float y, float width, float height) { return new Constrains(new ParentConstrain(x), new ParentConstrain(y), new ParentConstrain(width), new ParentConstrain(height)); }
	
	public static Constrains scrollBar(BooleanSupplier supply, boolean horizontal, float barSize) {
		Constrain bounds = new ConditionalConstraint(supply, new ParentConstrain(), new ParentConstrain(barSize / 2F));
		return horizontal ? new Constrains(new PixelConstrain(), new ParentConstrain(barSize).invert(), bounds, new PixelConstrain(barSize)) : new Constrains(new ParentConstrain(barSize).invert(), new PixelConstrain(), new PixelConstrain(barSize), bounds);
	}
	
	public static Constrains verticalScrollBar(BooleanSupplier supply, float offset, float barSize) { return invParent(barSize, Target.X).yPos(offset).width(barSize).height(new ConditionalConstraint(supply, new ParentConstrain(offset * 0.5F), new ParentConstrain((barSize + offset) * 0.5F))).build(); }
	
	public static Constrains border(float size, Facing side, float offset)
	{
		switch(side)
		{
			case NORTH: return new Constrains(new ParentConstrain(), new ParentConstrain(offset), new ParentConstrain(), new PixelConstrain(size));
			case EAST: return new Constrains(new ParentConstrain(size+offset).invert(), new ParentConstrain(), new PixelConstrain(size), new ParentConstrain());
			case SOUTH: return new Constrains(new ParentConstrain(), new ParentConstrain(size+offset).invert(), new ParentConstrain(), new PixelConstrain(size));
			case WEST: return new Constrains(new ParentConstrain(offset), new ParentConstrain(), new PixelConstrain(size), new ParentConstrain());
			default: return null;
		}
	}
	
	public static ConstrainBuilder builder() { return new ConstrainBuilder(); };
	
	public static ConstrainBuilder xPos(Constrain xPos) { return new ConstrainBuilder().set(xPos, Target.X); }
	public static ConstrainBuilder yPos(Constrain yPos) { return new ConstrainBuilder().set(yPos, Target.Y); }
	public static ConstrainBuilder width(Constrain width) { return new ConstrainBuilder().set(width, Target.WIDTH); }
	public static ConstrainBuilder height(Constrain height) { return new ConstrainBuilder().set(height, Target.HEIGHT); }
	
	public static ConstrainBuilder xPos(float xPos) { return new ConstrainBuilder().set(new PixelConstrain(xPos), Target.X); }
	public static ConstrainBuilder yPos(float yPos) { return new ConstrainBuilder().set(new PixelConstrain(yPos), Target.Y); }
	public static ConstrainBuilder width(float width) { return new ConstrainBuilder().set(new PixelConstrain(width), Target.WIDTH); }
	public static ConstrainBuilder height(float height) { return new ConstrainBuilder().set(new PixelConstrain(height), Target.HEIGHT); }
	
	public static ConstrainBuilder xPosR(float xPos) { return new ConstrainBuilder().set(new RelativeConstrain(xPos), Target.X); }
	public static ConstrainBuilder yPosR(float yPos) { return new ConstrainBuilder().set(new RelativeConstrain(yPos), Target.Y); }
	public static ConstrainBuilder widthR(float width) { return new ConstrainBuilder().set(new RelativeConstrain(width), Target.WIDTH); }
	public static ConstrainBuilder heightR(float height) { return new ConstrainBuilder().set(new RelativeConstrain(height), Target.HEIGHT); }
	
	public static ConstrainBuilder parent(Target target) { return new ConstrainBuilder().set(new ParentConstrain(), target); }
	public static ConstrainBuilder invParent(Target target) { return new ConstrainBuilder().set(new ParentConstrain().invert(), target); }
	public static ConstrainBuilder parent(float padding, Target target) { return new ConstrainBuilder().set(new ParentConstrain(padding), target); }
	public static ConstrainBuilder invParent(float padding, Target target) { return new ConstrainBuilder().set(new ParentConstrain(padding).invert(), target); }
	public static ConstrainBuilder pixel(float pixels, Target target) { return new ConstrainBuilder().set(new PixelConstrain(pixels), target); }
	public static ConstrainBuilder invPixel(float pixels, Target target) { return new ConstrainBuilder().set(new PixelConstrain(pixels).setInverted(), target); }
	public static ConstrainBuilder center(Target target) { return new ConstrainBuilder().set(new CenterConstrain(), target); }
	public static ConstrainBuilder dynamic(FloatSupplier provider, Target target) { return new ConstrainBuilder().set(new DynamicConstrain(provider), target); }
	
	public static class ConstrainBuilder
	{
		Constrain[] constrains = new Constrain[4];
		
		public ConstrainBuilder set(Constrain value, Target target) {
			constrains[target.ordinal()] = value;
			return this;
		}
		
		public ConstrainBuilder xPos(Constrain xPos) { return set(xPos, Target.X); }
		public ConstrainBuilder yPos(Constrain yPos) { return set(yPos, Target.Y); }
		public ConstrainBuilder width(Constrain width) { return set(width, Target.WIDTH); }
		public ConstrainBuilder height(Constrain height) { return set(height, Target.HEIGHT); }
		
		public ConstrainBuilder xPos(float xPos) { return set(new PixelConstrain(xPos), Target.X); }
		public ConstrainBuilder yPos(float yPos) { return set(new PixelConstrain(yPos), Target.Y); }
		public ConstrainBuilder width(float width) { return set(new PixelConstrain(width), Target.WIDTH); }
		public ConstrainBuilder height(float height) { return set(new PixelConstrain(height), Target.HEIGHT); }
		
		public ConstrainBuilder xPosR(float xPos) { return set(new RelativeConstrain(xPos), Target.X); }
		public ConstrainBuilder yPosR(float yPos) { return set(new RelativeConstrain(yPos), Target.Y); }
		public ConstrainBuilder widthR(float width) { return set(new RelativeConstrain(width), Target.WIDTH); }
		public ConstrainBuilder heightR(float height) { return set(new RelativeConstrain(height), Target.HEIGHT); }
		
		public ConstrainBuilder parent(Target target) { return set(new ParentConstrain(), target); }
		public ConstrainBuilder invParent(Target target) { return set(new ParentConstrain().invert(), target); }
		public ConstrainBuilder parent(float padding, Target target) { return set(new ParentConstrain(padding), target); }
		public ConstrainBuilder invParent(float padding, Target target) { return set(new ParentConstrain(padding).invert(), target); }
		public ConstrainBuilder pixel(float pixels, Target target) { return set(new PixelConstrain(pixels), target); }
		public ConstrainBuilder invPixel(float pixels, Target target) { return set(new PixelConstrain(pixels).setInverted(), target); }
		public ConstrainBuilder center(Target target) { return set(new CenterConstrain(), target); }
		public ConstrainBuilder dynamic(FloatSupplier provider, Target target) { return set(new DynamicConstrain(provider), target); }
		
		public Constrains build()
		{
			return new Constrains(constrains);
		}
	}
}
