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

import java.util.List;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.gui.components.base.ILayoutComponent.ArrayFetcher;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;

public class FlowLayout implements ILayout<Object> {
	private static final int ALIGN_DISABLED = -1;
	public static final int ALIGN_START = 0;
	public static final int ALIGN_CENTER = 1;
	public static final int ALIGN_END = 2;
	
	private List<GuiComponent> comps = new ObjectArrayList<>();
	protected float xGap;
	protected float yGap;
	protected int direction = ALIGN_END;
	protected int verticalAlignement = ALIGN_DISABLED;
	protected boolean includeInvisible = false;
	protected boolean isVertical = false;
	
	
	public FlowLayout(int direction) {
		this.direction = direction;
	}
	
	public FlowLayout(float xGap, float yGap) {
		this.xGap = xGap;
		this.yGap = yGap;
	}
	
	public FlowLayout(float xGap, float yGap, int direction) {
		this.xGap = xGap;
		this.yGap = yGap;
		this.direction = direction;
	}
	
	public FlowLayout verticalAlign(int alignment) {
		verticalAlignement = Math.clamp(alignment, -1, 2);
		return this;
	}
	
	public FlowLayout includeInvisible() {
		includeInvisible = true;
		return this;
	}
	
	public FlowLayout vertical(boolean value) {
		isVertical = value;
		return this;
	}
	
	@Override
	public FlowLayout add(GuiComponent comp, Object value) {
		comps.add(comp);
		return this;
	}
	
	@Override
	public FlowLayout remove(GuiComponent comp) {
		comps.remove(comp);
		return this;
	}
	
	@Override
	public void apply(GuiComponent owner) {
		int size = comps.size();
		float max = isVertical ? owner.getBox().getBaseHeight() : owner.getBox().getBaseWidth();
		float x = xGap;
		float y = yGap;
		float[] heights = new float[size];
		float[] widths = new float[size];
		int startIndex = 0;
		ArrayFetcher fetcher = new ArrayFetcher(includeInvisible);
		for(int i = 0,m=comps.size();i<m;i++) {
			comps.get(i).calculateBounds(fetcher);
			float[] bounds = fetcher.unscaledResult(owner.getBox());
			float width = bounds[2] - bounds[0];
			float height = bounds[3] - bounds[1];
			widths[i] = width;
			heights[i] = height;
			if(isVertical) {
				if(y + height + yGap > max) {
					x += processComponents(startIndex, i, x, max, y, widths, heights);
					y = yGap;
					startIndex = i;
				}
				y += height + yGap;
				fetcher.reset();
				continue;
			}
			else if(x + width + xGap > max) {
				y += processComponents(startIndex, i, y, max, x, widths, heights);
				x = xGap;
				startIndex = i;
			}
			x += width + xGap;
			fetcher.reset();
		}
		processComponents(startIndex, size, y, max, x, widths, heights);
	}
	
	private void setX(IGuiBox box, float x) {
		if(isVertical) box.setY(x);
		else box.setX(x);
	}
	
	private void setY(IGuiBox box, float y) {
		if(isVertical) box.setX(y);
		else box.setY(y);
	}
	
	private float processComponents(int start, int end, float yStart, float maxWidth, float widthSum, float[] widths, float[] heights) {
		float x = calculateStartPos(start, end, maxWidth, widthSum);
		float yAligner = 0F;
		if(verticalAlignement > 0) {
			for(int i = start;i<end;i++) {
				yAligner = Math.max(yAligner, isVertical ? (verticalAlignement == 2 ? widths[i] * 0.5F  : widths[i]) : (verticalAlignement == 2 ? heights[i] * 0.5F  : heights[i]));
			}
		}
		float jump = yAligner;
		yAligner += yStart;
		
		for(int i = start;i<end;i++) {
			IGuiBox box = comps.get(i).getBox();
			setX(box, x);
			switch(verticalAlignement) {
				case ALIGN_DISABLED: break;
				case ALIGN_START: {
					setY(box, yStart);
					break;
				}
				case ALIGN_CENTER: {
					setY(box, yAligner - heights[i] * 0.5F);
					break;
				}
				case ALIGN_END: {
					setY(box, yAligner);
					break;
				}
			}
			if(direction == ALIGN_END) x -= widths[i] + (isVertical ? yGap : xGap);
			else x += widths[i] + (isVertical ? yGap : xGap);
		}
		return jump;
	}
	
	private float calculateStartPos(int start, int end, float ownerWidth, float widthSum) {
		return switch(direction) {
			case ALIGN_CENTER -> ownerWidth * 0.5F - widthSum * 0.5F;
			case ALIGN_END -> ownerWidth - xGap;
			default -> xGap;
		};
	}
}
