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

import java.text.DecimalFormat;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.lwjgl.glfw.GLFW;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.utils.ObjectLists;
import speiger.src.coreengine.math.MathUtils;
import speiger.src.coreengine.math.color.ColorUtils;
import speiger.src.coreengine.math.misc.Facing;
import speiger.src.coreengine.math.vector.floats.Vec2f;
import speiger.src.coreengine.rendering.guiOld.base.IKeyComponent;
import speiger.src.coreengine.rendering.guiOld.components.PieComponent;
import speiger.src.coreengine.rendering.guiOld.components.PieComponent.IPieIndex;
import speiger.src.coreengine.rendering.guiOld.components.PieComponent.PieIndex;
import speiger.src.coreengine.rendering.guiOld.components.SingleTabPanelComponent;
import speiger.src.coreengine.rendering.guiOld.components.TextComponent;
import speiger.src.coreengine.rendering.guiOld.components.WindowComponent;
import speiger.src.coreengine.rendering.guiOld.helper.Align;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.Constrain;
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;
import speiger.src.coreengine.rendering.guiOld.helper.constrains.PixelConstrain;
import speiger.src.coreengine.rendering.inputOld.Keyboard;
import speiger.src.coreengine.utils.profiler.IProfiler;
import speiger.src.coreengine.utils.profiler.IProfiler.IProfilerEntry;
import speiger.src.coreengine.utils.profiler.IProfiler.ProfilerData;

public class PieProfilerWindow extends WindowComponent implements Supplier<List<IPieIndex>>, IKeyComponent
{
	public static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("##0.00");
	
	SingleTabPanelComponent panel = new SingleTabPanelComponent(Facing.SOUTH).onAction(this::onProfilerChanged).cast();
	PieComponent pie = panel.getPanel().addChild(new PieComponent(127, this).setAutoUpdate(true), Constrains.parent(Target.WIDTH).dynamic(this::calculatePieHeight, Target.HEIGHT).build());
	TextComponent[] extraFeatures = new TextComponent[] {
			new TextComponent(0F, 0F, 18F, 7F),
			new TextComponent(0F, 0F, 0F, 8F),
			new TextComponent(0F, 8F, 0F, 7F)
	};
	List<TextComponent[]> entries = new ObjectArrayList<>();
	List<ProfilerTab> tabs = new ObjectArrayList<>();
	int previouseTab = -1;
	int textInUse = 0;
	IProfilerEntry currentEntry;
	String entryName;
	List<ProfilerData> lastValues = null;
	
	public PieProfilerWindow(float x, float y, float width, float height, String name)
	{
		super(x, y, width, height, DEFAULT_FLAGS, name);
	}
	
	@Override
	public boolean canMoveIntoForground()
	{
		return true;
	}
	
	@Override
	public void init()
	{
		super.init();
		addChild(panel.set(0F, 7.5F).onChange(minimizedListener), Constrains.parent(0.25F, Target.WIDTH).parent(4F, Target.HEIGHT).build());
		extraFeatures[0].setTextScale(0.3F).setText("[0] Back").horizontal(Align.LEFT_TOP);
		extraFeatures[0].onChange(T -> T.setVisible(currentEntry != null && currentEntry.getParent() != null));
		panel.getPanel().addChild(extraFeatures[0], Constrains.dynamic(() -> pie.getBox().getBaseHeight() - 7F, Target.Y).invPixel(38F, Target.WIDTH).build());
		
		panel.getPanel().addChild(extraFeatures[1].setTextScale(0.4F), Constrains.parent(0F, Target.Y).parent(Target.WIDTH).build());
		panel.getPanel().addChild(extraFeatures[2].setTextScale(0.33F), Constrains.pixel(8F, Target.Y).parent(Target.WIDTH).build());
	}
	
	public PieProfilerWindow addProfiler(IProfiler profiler, String root)
	{
		String name = profiler.getName();
		panel.addTab(name);
		tabs.add(new ProfilerTab(name, profiler, root));
		if(tabs.size() == 1)
		{
			onProfilerChanged();
		}
		return this;
	}
	
	protected IProfiler getActive()
	{
		return tabs.get(panel.getActiveIndex()).getProfiler();
	}
	
	protected void applyProfiler(boolean prev, Consumer<IProfiler> action)
	{
		int index = prev ? previouseTab : panel.getActiveIndex();
		if(index >= 0) action.accept(tabs.get(index).getProfiler());
	}
	
	@Override
	public void onClosed()
	{
		super.onClosed();
		applyProfiler(false, IProfiler::disable);
	}
	
	protected void onProfilerChanged()
	{
		if(previouseTab == panel.getActiveIndex()) return;
		applyProfiler(true, IProfiler::disable);
		applyProfiler(false, IProfiler::enable);
		previouseTab = panel.getActiveIndex();
		ProfilerTab tab = tabs.get(previouseTab);
		extraFeatures[1].setText(tab.getName());
		setCurrentEntry(tab.getRoot());
	}
	
	public SingleTabPanelComponent getTab()
	{
		return panel;
	}
		
	public PieProfilerWindow setCurrentEntry(String name)
	{
		entryName = name;
		currentEntry = getActive().getEntry(name);
		lastValues = null;
		extraFeatures[2].setText(currentEntry == null ? "Unknown" : currentEntry.getName());
		return this;
	}
	
	@Override
	protected boolean updateSelf(int mouseX, int mouseY, float particalTicks)
	{
		for(int i = 0,m=entries.size();i<m;i++)
		{
			TextComponent[] comp = entries.get(i);
			float brightness = comp[0].isTopHovered(mouseX, mouseY) || comp[1].isTopHovered(mouseX, mouseY) || comp[2].isTopHovered(mouseX, mouseY) ? 0.7F : 1F;
			comp[0].setBrightness(brightness);
			comp[1].setBrightness(brightness);
			comp[2].setBrightness(brightness);
		}
		extraFeatures[0].setBrightness(extraFeatures[0].isHovered(mouseX, mouseY) ? 0.7F : 1F);
		return super.updateSelf(mouseX, mouseY, particalTicks);
	}
	
	@Override
	protected boolean fixedUpdateSelf()
	{
		if(panel.getActiveIndex() < 0)
		{
			return true;
		}
		if(currentEntry == null)
		{
			setCurrentEntry(entryName);
			return true;
		}
		if(currentEntry.getNanoTime() == 0L || (Keyboard.isAltDown() && lastValues != null) || (lastValues != null && getGlobalClock() % 2 != 0))
		{
			return true;
		}
		while(entries.size() < currentEntry.getChildCount() + 1)
		{
			entries.add(new TextComponent[] {createComponent(0), createComponent(1), createComponent(2) });
		}
		boolean lastEmpty = lastValues == null;
		lastValues = currentEntry.getData();
		int last = textInUse;
		textInUse = lastValues.size();
		for(int i = 0;i < textInUse;i++)
		{
			ProfilerData data = lastValues.get(i);
			TextComponent[] info = entries.get(i);
			int color = data.color();
			info[0].setMassChanging(TextComponent.class).textColor(color).setText("[" + (i + 1) + "] " + data.name()).finishMassChanging();
			info[1].setMassChanging(TextComponent.class).textColor(color).setText(PERCENT_FORMAT.format(data.effect()) + "%").finishMassChanging();
			info[2].setMassChanging(TextComponent.class).textColor(color).setText(PERCENT_FORMAT.format(data.totalEffect()) + "%").finishMassChanging();
		}
		if(last != textInUse)
		{
			float diff = 80F + (textInUse * 7F) - this.getBox().getHeight();
			if(diff > 0) {
				resize(0F, diff);
			}
			onChanged(true);
		}
		else if(lastEmpty)
		{
			onChanged(true);
		}
		return true;
	}
	
	@Override
	public boolean onClick(int button, int mouseX, int mouseY)
	{
		if(super.onClick(button, mouseX, mouseY)) 
		{
			return true;
		}
		if(currentEntry != null && currentEntry.getParent() != null && extraFeatures[0].isHovered(mouseX, mouseY))
		{
			setCurrentEntry(currentEntry.getParent().getPathName());
		}
		if(lastValues != null)
		{
			for(int i = 0;i<textInUse;i++)
			{
				TextComponent[] comp = entries.get(i);
				if(comp[0].isHovered(mouseX, mouseY) || comp[1].isHovered(mouseX, mouseY) || comp[2].isHovered(mouseX, mouseY))
				{
					String s = comp[0].getText();
					s = s.substring(s.indexOf("] ")+2);
					if(!s.equalsIgnoreCase("Nameless"))
					{
						setCurrentEntry(currentEntry.getPathName() + "/" + s);
						return true;
					}
					return false;
				}
			}
		}
		return false;
	}
	
	@Override
	public boolean isAcceptingInput()
	{
		return isEnabled() && isVisible() && !isMinimized() && currentEntry != null && hasFocus();
	}
	
	@Override
	public boolean onKeyPressed(int key)
	{
		if(key == GLFW.GLFW_KEY_0 && currentEntry != null && currentEntry.getParent() != null)
		{
			setCurrentEntry(currentEntry.getParent().getPathName());
			return true;
		}
		else if(key >= GLFW.GLFW_KEY_1 && key <= GLFW.GLFW_KEY_9)
		{
			key -= GLFW.GLFW_KEY_1;
			if(key < textInUse)
			{
				String s = entries.get(key)[0].getText();
				s = s.substring(s.indexOf("] ")+2);
				if(!s.equalsIgnoreCase("Nameless"))
				{
					setCurrentEntry(currentEntry.getPathName() + "/" + s);
					return true;
				}
			}
			return false;
		}
		return false;
	}
	
	@Override
	public Vec2f getMinimumBounds()
	{
		return Vec2f.of(80F, 80F + (textInUse * 7F));
	}
	
	@Override
	public List<IPieIndex> get()
	{
		if(lastValues != null)
		{
			List<IPieIndex> entries = new ObjectArrayList<>();
			for(int i = 0, m = lastValues.size();i < m;i++)
			{
				entries.add(new PieIndex(MathUtils.floor(lastValues.get(i).effect() * 1.28D), lastValues.get(i).color()));
			}
			return entries;
		}
		return ObjectLists.singleton(new PieIndex(pie.getMaxSteps(), ColorUtils.LIGHT_BLUE));
	}
	
	protected float calculatePieHeight()
	{
		return getBox().getBaseHeight() - ((textInUse * 7) + 17F);
	}
	
	protected TextComponent createComponent(int column)
	{
		final int index = entries.size();
		String text = column == 0 ? "[" + (entries.size() + 1) + "] Unknown" : PERCENT_FORMAT.format(0D) + "%";
		TextComponent comp = new TextComponent(0F, 0F, 18F, 7F).setTextScale(0.33F).setText(text).align(column == 0 ? Align.LEFT_TOP : Align.RIGHT_BOTTOM, Align.CENTER);
		comp.onChange(T -> T.setVisible(index < textInUse));
		Constrain xPos = column == 0 ? null : (column == 1 ? new PixelConstrain(38F).setInverted() : new PixelConstrain(19F).setInverted());
		panel.getPanel().addChild(comp, new Constrains(xPos, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (index * 7F)), column == 0 ? new PixelConstrain(38).setInverted() : null, null));
		return comp;
	}
	
	private static class ProfilerTab
	{
		String name;
		IProfiler profiler;
		String root;
		
		public ProfilerTab(String name, IProfiler profiler, String root)
		{
			this.name = name;
			this.profiler = profiler;
			this.root = root;
		}

		public String getName()
		{
			return name;
		}
		
		public IProfiler getProfiler()
		{
			return profiler;
		}
		
		public String getRoot()
		{
			return root;
		}
	}
}
