package speiger.src.coreengine.rendering.guiOld.components;

import speiger.src.collections.floats.functions.FloatSupplier;
import speiger.src.collections.objects.functions.function.ToFloatFunction;
import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.lists.ObjectList;
import speiger.src.coreengine.math.color.ColorUtils;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.rendering.guiOld.GuiComponent;
import speiger.src.coreengine.rendering.guiOld.base.IButtonComponent;
import speiger.src.coreengine.rendering.guiOld.helper.Align;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrain.Target;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrains;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.DynamicConstrain;

public class TabbedPanelComponent extends PanelComponent
{
	PanelComponent selection = new PanelComponent();
	ObjectList<Tab> tabs = new ObjectArrayList<>();
	int selectedTab = -1;
	float tabScale = 1F;
	
	Facing facing;
	int backgroundColor = ColorUtils.GRAY;
	int unselectedColor = ColorUtils.GRAY;
	int selectedColor = ColorUtils.DARK_GRAY;
	float padding = 30F;
	
	public TabbedPanelComponent()
	{
		this(Facing.NORTH);
	}
	
	public TabbedPanelComponent(Facing facing)
	{
		this(0F, 0F, 0F, 0F, facing);
	}
	
	public TabbedPanelComponent(float x, float y, float width, float height)
	{
		this(x, y, width, height, Facing.NORTH);
	}
		
	public TabbedPanelComponent(float x, float y, float width, float height, Facing facing)
	{
		super(x, y, width, height);
		this.facing = facing;
	}
	
	public TabbedPanelComponent setFacing(Facing facing)
	{
		this.facing = facing;
		updateConstrains(selection, createView());
		for(Tab tab : tabs)
		{
			updateConstrains(tab.align(facing.isZAxis() ? Align.CENTER : (facing == Facing.WEST ? Align.LEFT_TOP : Align.RIGHT_BOTTOM)), createConstraint(tab));
			updateConstrains(tab.getPanel(), createPanel());
		}
		onChanged(true);
		return this;
	}
	
	public TabbedPanelComponent setPadding(float padding)
	{
		this.padding = padding;
		for(Tab tab : tabs)
		{
			tab.updatePadding();
		}
		onChanged(true);
		return this;
	}
	
	public Facing getFacing()
	{
		return facing;
	}
	
	public PanelComponent addTab(String name)
	{
		Tab newTab = new Tab(name).align(facing.isZAxis() ? Align.CENTER : (facing == Facing.WEST ? Align.LEFT_TOP : Align.RIGHT_BOTTOM));
		tabs.add(newTab);
		if(selectedTab == -1) {
			selectedTab = tabs.size()-1;
			newTab.getPanel().setVisible(true);
		}
		newTab.onAction(() -> selectTab(newTab));
		selection.addChild(newTab, createConstraint(newTab));
		addChild(newTab.getPanel(), createPanel());
		onChanged(true);
		return newTab.getPanel();
	}
	
	public int findTab(String name)
	{
		for(int i = 0,m=tabs.size();i<m;i++) {
			if(tabs.get(i).name.equals(name)) return i;
		}
		return -1;
	}
	
	public int getActiveIndex()
	{
		return selectedTab;
	}
	
	public PanelComponent getActiveTab()
	{
		return selectedTab == -1 ? null : tabs.get(selectedTab).getPanel();
	}
	
	@Override
	protected boolean renderSelf(int mouseX, int mouseY, float particalTicks)
	{
		getRenderer().drawQuad(selection.getBox(), backgroundColor);
		return true;
	}
	
	@Override
	public void init()
	{
		addChild(selection, createView());
		super.init();
	}
	
	@Override
	protected void repaint()
	{
		if(facing.isZAxis())
		{
			float room = getBox().getBaseWidth();
			float requiredSpace = 0F;
			for(int i = 0,m=tabs.size();i<m;i++)
			{
				Tab tab = tabs.get(i);
				float width = tab.getWidth() * tab.getTextScale();
				if(i == selectedTab) room -= width;
				else requiredSpace += width;
			}
			tabScale = Math.min(1F, room / requiredSpace);
		}
		else
		{
			tabScale = 0F;
			for(int i = 0,m=tabs.size();i<m;i++)
			{
				Tab tab = tabs.get(i);
				tabScale = Math.max(tabScale, tab.getWidth() * tab.getTextScale());
			}
			tabScale *= getBox().getScale();
			tabScale += 1F;
		}
	}
	
	protected Constrains createView()
	{
		switch(facing)
		{
			case NORTH: return Constrains.parent(Target.X).parent(Target.Y).parent(Target.WIDTH).height(7.5F).build();
			case SOUTH: return Constrains.parent(Target.X).invParent(7.7F, Target.Y).parent(Target.WIDTH).height(7.5F).build();
			case EAST: return Constrains.xPos(new DynamicConstrain(this::getXOffset).setInverted(true)).parent(Target.Y).dynamic(this::getPanelWidth, Target.WIDTH).parent(Target.HEIGHT).build();
			case WEST: return Constrains.parent(Target.X).parent(Target.Y).dynamic(this::getPanelWidth, Target.WIDTH).parent(Target.HEIGHT).build();
			default: return Constrains.parent();
		}
	}
	
	protected Constrains createPanel()
	{
		switch(facing)
		{
			case NORTH: return Constrains.parent(0F, 7.2F, 0F, 3.6F);
			case SOUTH: return Constrains.parent(0F, 0F, 0F, 3.6F);
			case EAST: return Constrains.parent(Target.X).parent(Target.Y).width(new DynamicConstrain(this::getPanelWidth).setInverted(true)).parent(Target.HEIGHT).build();
			case WEST: return Constrains.dynamic(this::getXOffset, Target.X).parent(0F, Target.Y).width(new DynamicConstrain(this::getPanelWidth).setInverted(true)).parent(Target.HEIGHT).build();
			default: return Constrains.parent();
		}
	}
	
	protected Constrains createConstraint(Tab tab)
	{
		if(facing.isZAxis()) return Constrains.parent(Target.Y).parent(Target.HEIGHT).dynamic(new DynamicTab(tab, this::getOffset), Target.X).dynamic(new DynamicTab(tab, this::getWidth), Target.WIDTH).build();
		return Constrains.parent(Target.X).dynamic(new DynamicTab(tab, this::getOffset), Target.Y).parent(Target.WIDTH).height(7.5F).build();
	}
	
	protected void selectTab(Tab tab)
	{
		selectTab(tabs.indexOf(tab));
	}
	
	public boolean selectTab(int index)
	{
		if(index < 0 || index >= tabs.size()) return false;
		if(selectedTab == index) return false;
		if(selectedTab != -1) tabs.get(selectedTab).getPanel().setVisible(false);
		selectedTab = index;
		tabs.get(selectedTab).getPanel().setVisible(true);
		onChanged(true);
		return true;
	}
	
	protected float getOffset(Tab tab)
	{
		int index = tabs.indexOf(tab);
		float offset = 0F;
		for(int i = 0;i<index;i++)
		{
			Tab entry = tabs.get(i);
			if(facing.isZAxis()) offset += entry.getWidth() * (i == selectedTab ? 1F : tabScale) * entry.getTextScale();
			else offset += tab.getBox().getBaseHeight();
		}
		return offset;
	}
	
	protected float getPanelOffset()
	{
		return facing == Facing.EAST ? 0F : tabScale + 0.25F;
	}
	
	protected float getPanelWidth()
	{
		return tabScale + 0.25F;
	}
	
	protected float getXOffset()
	{
		return tabScale + 0.25F;
	}
	
	protected float getWidth(Tab tab)
	{
		return facing.isZAxis() ? tab.getWidth() * (selectedTab == tabs.indexOf(tab) ? 1F : tabScale) * tab.getTextScale() : tabScale;
	}
	
	private static class DynamicTab implements FloatSupplier
	{
		Tab tab;
		ToFloatFunction<Tab> function;
		
		public DynamicTab(Tab tab, ToFloatFunction<Tab> function)
		{
			this.tab = tab;
			this.function = function;
		}
		
		@Override
		public float getAsFloat()
		{
			return function.applyAsFloat(tab);
		}
	}
	
	private class Tab extends GuiComponent implements IButtonComponent
	{
		String name;
		float width = -1F;
		TextComponent comp;
		PanelComponent panel = new PanelComponent().setScissors(true).setVisible(false).cast();
		
		public Tab(String name)
		{
			super(0F, 0F, 100F, 7.5F);
			this.name = name;
			comp = new TextComponent(name).setTextScale(0.4F).singleLine(true).horizontal(Align.LEFT_TOP);
			setFlag(FLAG_SUPPORT_BINDING);
		}
		
		public Tab align(Align align)
		{
			comp.horizontal(align);
			return this;
		}
		
		public void updatePadding()
		{
			if(getGui() != null) width = getFont().width(name)+padding;				
		}
		
		@Override
		public void init()
		{
			addChild(comp, Constrains.parent());
			updatePadding();
		}
		
		public float getWidth()
		{
			return width;
		}
		
		public float getTextScale()
		{
			return comp.getTextScale();
		}
		
		@Override
		protected void repaint()
		{
			String s = name;
			float scale = comp.getTextScale();
			float width = (this.width-padding)*scale;
			float desiredWidth = getBox().getBaseWidth();
			if(width > desiredWidth) {
				while(s.length() >= 1 && getFont().width(s+"...") * scale > desiredWidth) {
					s = s.substring(0, s.length()-1);
				}
				comp.setText(s+"...");
				return;
			}
			comp.setText(s);
		}
		
		public PanelComponent getPanel()
		{
			return panel;
		}
		
		@Override
		protected boolean renderSelf(int mouseX, int mouseY, float particalTicks)
		{
			boolean notSelected = tabs.indexOf(this) != selectedTab;
			getRenderer().drawQuad(getBox(), notSelected ? unselectedColor : selectedColor);
			if(notSelected) getRenderer().drawFrame(getBox(), selectedColor);
			return true;
		}
		
		@Override
		public void onRelease(int button, int mouseX, int mouseY)
		{
			notifyListeners(LISTENER_USER_ACTION);
		}
		
		@Override
		protected boolean onUserKey()
		{
			notifyListeners(LISTENER_USER_ACTION);
			return true;
		}
	}
}