package speiger.src.coreengine.rendering.newGui.components.base;

import java.util.List;
import java.util.function.Consumer;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.rendering.gui.helper.box.IGuiBox;
import speiger.src.coreengine.rendering.newGui.renderer.IUIRenderer;
import speiger.src.coreengine.utils.collections.CollectionUtils;
import speiger.src.coreengine.utils.collections.FlagObject;
import speiger.src.coreengine.utils.misc.ICastable;

public non-sealed class GuiComponent extends FlagObject implements ICastable, ILayoutComponent, IListableComponent {
	private static final int FLAG_FOCUSED = 1<<0;
	private static final int FLAG_ENABLED = 1<<1;
	private static final int FLAG_VISIBLE = 1<<2;
	private static final int FLAG_MANUAL_MANAGED = 1<<3;
	private static final int FLAG_SCISSORED = 1<<4;
	
	IGuiBox box;
	GuiComponent parent;
	List<GuiComponent> children = new ObjectArrayList<>();
	InteractionContainer interactions = new InteractionContainer(this::isInteractable);
	List<Consumer<GuiComponent>>[] listeners = CollectionUtils.createList(IListableComponent.MAX_LISTENER_TYPES);
	IComponentRenderer<GuiComponent> renderer;
	
	public GuiComponent(float x, float y, float width, float height) {
		this(IGuiBox.of(x, y, width, height));
	}
	
	public GuiComponent(IGuiBox box) {
		this.box = box;
		if(this instanceof IInteractable actor) {
			interactions.add(actor);
		}
	}
	
	@Override
	public void calculateBounds(ILayoutScanner output) {
		output.accept(getBox());
		for(GuiComponent child : children) {
			if(child.isVisible() || output.acceptsInvisble()) {
				child.calculateBounds(output);
			}
		}
	}
	
	public void updateComponent() {
		children.forEach(GuiComponent::updateComponent);
	}
	
	protected void renderComponent(IUIRenderer renderer, int mouseX, int mouseY, float particalTicks) {
		renderChildren(renderer, mouseX, mouseY, particalTicks);
	}
	
	protected void renderChildren(IUIRenderer renderer, int mouseX, int mouseY, float particalTicks) {
		for(GuiComponent child : children) {
			if(child.isManualManaged()) continue;
			renderComponent(child, renderer, mouseX, mouseY, particalTicks);
		}
	}
	
	public static boolean renderComponent(GuiComponent comp, IUIRenderer renderer, int mouseX, int mouseY, float particalTicks) {
		if(!comp.isVisible() || (comp.isScissored() && !renderer.isInScissors(comp.getBox()))) return false;
		if(comp.isScissored()) {
			renderer.pushScissors(comp.getBox());
			if(comp.renderer != null) {
				comp.renderer.renderComponent(comp, renderer, mouseX, mouseY, particalTicks);
				comp.renderChildren(renderer, mouseX, mouseY, particalTicks);
			}
			else comp.renderComponent(renderer, mouseX, mouseY, particalTicks);
			renderer.popScissors();
		}
		else if(comp.renderer != null) {
			comp.renderer.renderComponent(comp, renderer, mouseX, mouseY, particalTicks);
			comp.renderChildren(renderer, mouseX, mouseY, particalTicks);
		}
		else comp.renderComponent(renderer, mouseX, mouseY, particalTicks);
		return true;
	}
	
	@Override
	public GuiComponent addListener(Consumer<GuiComponent> listener, int index) {
		listeners[index].add(listener);
		return this;
	}
	
	@Override
	public GuiComponent removeListener(Consumer<GuiComponent> listener, int index) {
		listeners[index].remove(listener);
		return this;
	}
	
	@Override
	public void notifyListeners(int index) {
		if(listeners[index].isEmpty()) return;
		for(Consumer<GuiComponent> comp : listeners[index]) {
			comp.accept(this);
		}
	}
	
	@Override
	public IGuiBox getBox() {
		return box;
	}
	
	@Override
	public GuiComponent set(float x, float y) {
		if(box.getBaseX() != x || box.getBaseY() != y) {
			box.setXY(x, y);
			//TODO implement onChanged
		}
		return this;
	}
	
	@Override
	public GuiComponent bounds(float width, float height) {
		if(box.getBaseWidth() != width || box.getBaseHeight() != height) {
			box.setBounds(width, height);
			//TODO implement onChanged
		}
		return this;
	}
	
	@Override
	public GuiComponent resize(float width, float height) {
		if(width != 0F || height != 0F) {
			box.grow(width, height);
			//TODO implement onChanged
		}
		return this;
	}
	
	@Override
	public GuiComponent move(float moveX, float moveY) {
		if(moveX != 0F || moveY != 0F) {
			box.move(moveX, moveY);
			//TODO implement onChanged
		}
		return this;
	}
	
	private final boolean isInteractable() {
		return isFlagSet(FLAG_ENABLED | FLAG_VISIBLE);
	}
	
	public final boolean isFocused() {
		return isFlagSet(FLAG_FOCUSED);
	}
	
	public final boolean isEnabled() {
		return isFlagSet(FLAG_ENABLED);
	}
	
	public final boolean isVisible() {
		return isFlagSet(FLAG_VISIBLE);
	}
	
	public final boolean isManualManaged() {
		return isFlagSet(FLAG_MANUAL_MANAGED);
	}
	
	public final boolean isScissored() {
		return isFlagSet(FLAG_SCISSORED);
	}
	
	public final void setFocused(boolean value) {
		setFlag(FLAG_FOCUSED, value);
	}
	
	public final GuiComponent setEnabled(boolean value) {
		setFlag(FLAG_ENABLED, value);
		return this;
	}
	
	public final GuiComponent setVisible(boolean value) {
		setFlag(FLAG_VISIBLE, value);
		return this;
	}
	
	public final GuiComponent setManualManaged(boolean value) {
		setFlag(FLAG_MANUAL_MANAGED, value);
		return this;
	}
	
	public final GuiComponent setScissored(boolean value) {
		setFlag(FLAG_SCISSORED, value);
		return this;
	}
}