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

import java.util.Iterator;
import java.util.List;

import speiger.src.collections.ints.sets.IntOpenHashSet;
import speiger.src.collections.ints.sets.IntSet;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.abstracts.AbstractObject2BooleanMap.BasicEntry;
import speiger.src.collections.objects.maps.interfaces.Object2BooleanMap;
import speiger.src.collections.objects.maps.interfaces.Object2BooleanMap.Entry;
import speiger.src.coreengine.rendering.gui.components.base.GuiComponent;
import speiger.src.coreengine.rendering.gui.components.base.IComponentScreen;
import speiger.src.coreengine.rendering.gui.components.base.IInteractable;
import speiger.src.coreengine.rendering.gui.components.base.IInteractableContainer;
import speiger.src.coreengine.rendering.gui.layout.constraints.ConstraintContainer;
import speiger.src.coreengine.rendering.gui.renderer.IUIRenderer;
import speiger.src.coreengine.rendering.guiOld.helper.box.IGuiBox;

public class GuiContainer implements IComponentScreen, IInteractableContainer {
	LayeredContainer container = new LayeredContainer();
	IntSet activeButtons = new IntOpenHashSet();
	IInteractable focused;
	IGuiBox box = IGuiBox.of(0F, 0F, 0F, 0F);
	long tick;
	
	@Override
	public IGuiBox getBox() { return box; }
	@Override
	public long clock() { return tick; }
	@Override
	public boolean isFocused() { return focused != null; }
	@Override
	public void setFocused(boolean value) { if(!value) setFocusedChild(null); }
	@Override
	public List<? extends IInteractable> getChildren() { return container.interactables(); }
	@Override
	public void setFocusedChild(IInteractable child) {
		if(focused != null) focused.setFocused(false);
		this.focused = child;
		if(focused != null) focused.setFocused(true);
	}
	@Override
	public IInteractable getFocusedChild() { return focused; }
	@Override
	public IntSet getActiveButtons() { return activeButtons; }
	
	@Override
	public void pushLayer() { container.pushLayer(); }
	@Override
	public void popLayer() { container.popLayer(); }
	@Override
	public GuiComponent addComponent(GuiComponent component, ConstraintContainer constraints) {
		if(component.screen() != null) throw new IllegalArgumentException("A Child can not have multiple Screens");
		container.add(component);
		component.setScreen(this);
		component.init();
		component.onChanged(true);
		return component;
	}
	
	@Override
	public boolean hasComponent(GuiComponent component) {
		return component.screen() == this && container.contains(component);
	}
	
	@Override
	public boolean removeComponent(GuiComponent component) {
		if(component.screen() == this && container.remove(component)) {
			component.close();
			component.setScreen(null);
			return true;
		}
		return false;
	}
	
	public void render(IUIRenderer renderer, int mouseX, int mouseY, float partialTicks) {
		for(Object2BooleanMap.Entry<GuiComponent> entry : container) {
			boolean keepMouse = entry.getBooleanValue();
			GuiComponent.renderComponent(entry.getKey(), renderer, keepMouse ? mouseX : -1, keepMouse ? mouseY : -1, partialTicks);
		}
	}
	
	public void tick() {
		for(Object2BooleanMap.Entry<GuiComponent> entry : container) {
			GuiComponent.tickComponent(entry.getKey());
		}
	}
	
	public static class LayeredContainer implements Iterable<Object2BooleanMap.Entry<GuiComponent>> {
		List<List<GuiComponent>> components = new ObjectArrayList<>();
		List<List<IInteractable>> interactables = new ObjectArrayList<>();
		int layer = -1;
		
		public LayeredContainer() { pushLayer(); }
		
		public void pushLayer() {
			layer++;
			components.add(new ObjectArrayList<>());
			interactables.add(new ObjectArrayList<>());
		}
		
		public void popLayer() {
			if(layer == 0) return;
			layer--;
			components.removeLast();
			interactables.removeLast();
		}
		
		public void add(GuiComponent component) {
			children().add(component);
			interactables().add(component.interactContainer());
		}
		
		public boolean contains(GuiComponent component) {
			for(int i = layer;i>=0;i--) {
				if(components.get(i).contains(component)) return true;
			}
			return false;
		}
		
		public boolean remove(GuiComponent component) {
			for(int i = layer;i>=0;i--) {
				if(components.get(i).remove(component)) {
					interactables.get(i).remove(component.interactContainer());
					clearEmptyLayers();
					return true;
				}
			}
			return false;
		}
		
		public void clearEmptyLayers() {
			while(components.getLast().isEmpty() && layer > 0) {
				popLayer();
			}
		}
		
		@Override
		public Iterator<Object2BooleanMap.Entry<GuiComponent>> iterator() {
			return new Iterator<>() {
				BasicEntry<GuiComponent> entry = new BasicEntry<>(); 
				int myLayer = 0;
				Iterator<GuiComponent> childIterator = components.getLast().iterator();
				
				@Override
				public boolean hasNext() {
					return childIterator.hasNext();
				}
				
				@Override
				public Entry<GuiComponent> next() {
					if(!hasNext()) throw new IllegalStateException("Out of Elements to poll");
					entry.set(childIterator.next(), myLayer == layer);
					if(!childIterator.hasNext() && myLayer < layer) {
						childIterator = components.get(++myLayer).iterator();
					}
					return entry;
				}
				
			};
		}
		
		public List<GuiComponent> children() {
			return components.get(layer);
		}
		
		public List<IInteractable> interactables() {
			return interactables.get(layer);
		}
	}
}
