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

import java.text.DecimalFormat;
import java.util.List;
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.misc.ColorObject;
import speiger.src.coreengine.math.vector.floats.Vec2f;
import speiger.src.coreengine.rendering.gui.GuiManager;
import speiger.src.coreengine.rendering.gui.base.IKeyComponent;
import speiger.src.coreengine.rendering.gui.components.ButtonComponent;
import speiger.src.coreengine.rendering.gui.components.PieComponent;
import speiger.src.coreengine.rendering.gui.components.PieComponent.IPieIndex;
import speiger.src.coreengine.rendering.gui.components.PieComponent.PieIndex;
import speiger.src.coreengine.rendering.gui.components.TextComponent;
import speiger.src.coreengine.rendering.gui.components.WindowComponent;
import speiger.src.coreengine.rendering.gui.helper.Align;
import speiger.src.coreengine.rendering.gui.helper.constrains.ComponentConstrains;
import speiger.src.coreengine.rendering.gui.helper.constrains.Constrain;
import speiger.src.coreengine.rendering.gui.helper.constrains.DynamicConstrain;
import speiger.src.coreengine.rendering.gui.helper.constrains.ParentConstrain;
import speiger.src.coreengine.rendering.gui.helper.constrains.PixelConstrain;
import speiger.src.coreengine.rendering.gui.helper.constrains.RelativeConstrain;
import speiger.src.coreengine.rendering.input.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 PieProfilerWindowComponent extends WindowComponent
		implements Supplier<List<IPieIndex>>, IKeyComponent
{
	public static final DecimalFormat PERCENT_FORMAT = new DecimalFormat("##0.00");
	
	PieComponent pie = new PieComponent(127, this);
	ButtonComponent[] buttons = new ButtonComponent[3];
	TextComponent[] extraFeatures = new TextComponent[3];
	List<TextComponent[]> entries = new ObjectArrayList<TextComponent[]>();
	int textInUse = 0;
	IProfiler profiler;
	IProfilerEntry currentEntry;
	String entryName;
	List<ProfilerData> lastValues = null;
	
	public PieProfilerWindowComponent(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(pie.setAutoUpdate(true).setComponentPosition(0F, 5F).addChangeListener(minimizedListener), new ComponentConstrains(null, null, new ParentConstrain(), new DynamicConstrain(this::calculatePieHeight)));
		buttons[0] = createButton(0, "Client");
		buttons[1] = createButton(1, "GPU");
		buttons[2] = createButton(2, "Server");
		extraFeatures[0] = new TextComponent(0F, 0F, 18F, 5.5F).setTextScale(0.3F).setText("[0] Back").setAlignment(Align.LEFT_TOP, Align.CENTER);
		extraFeatures[0].addChangeListener(T -> T.setVisible(!isMinimized() && currentEntry != null && currentEntry.getParent() != null));
		addChild(extraFeatures[0], new ComponentConstrains(null, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (-5.5F)), new PixelConstrain(38).setInverted(), null));
		
		extraFeatures[1] = new TextComponent(0F, 0F, 0F, 7F).setTextScale(0.4F).setText("Client Thread");
		addChild(extraFeatures[1].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(8F), new ParentConstrain(), null));
		extraFeatures[2] = new TextComponent(0F, 0F, 0F, 6F).setTextScale(0.33F).setText("Client");
		addChild(extraFeatures[2].addChangeListener(minimizedListener), new ComponentConstrains(null, new PixelConstrain(15F), new ParentConstrain(), null));

	}
	
	@Override
	public void onClosed()
	{
		super.onClosed();
		if(profiler != null)
		{
			profiler.disable();
			profiler = null;
		}
	}
	
	public PieProfilerWindowComponent setProfiler(IProfiler profiler, String root)
	{
		if(this.profiler != null)
		{
			buttons[getProfilerIndex(this.profiler)].setEnabled(true);
			this.profiler.disable();
		}
		this.profiler = profiler;
		if(profiler != null)
		{
			profiler.enable();
			setCurrentEntry(root);
			buttons[getProfilerIndex(this.profiler)].setEnabled(false);
			extraFeatures[1].setText(profiler.getName());
		}
		return this;
	}
	
	public PieProfilerWindowComponent setCurrentEntry(String name)
	{
		entryName = name;
		currentEntry = profiler.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(profiler == null)
		{
			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);
			ColorObject color = data.getColor();
			info[0].setMassChanging(TextComponent.class).setTextColor(color).setText("[" + (i + 1) + "] " + data.getName()).finishMassChanging();
			info[1].setMassChanging(TextComponent.class).setTextColor(color).setText(PERCENT_FORMAT.format(data.getEffect()) + "%").finishMassChanging();
			info[2].setMassChanging(TextComponent.class).setTextColor(color).setText(PERCENT_FORMAT.format(data.getTotalEffect()) + "%").finishMassChanging();
		}
		if(last != textInUse)
		{
			resizeComponent(0F, (textInUse - last) * 5.5F);
		}
		else if(lastEmpty)
		{
			onComponentChanged(true);
		}
		return true;
	}
	
	@Override
	public boolean onClick(int button, int mouseX, int mouseY)
	{
		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 super.onClick(button, mouseX, mouseY);
	}
	
	@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.newVec(80F, 80F + (textInUse * 5.5F));
	}
	
	@Override
	public List<IPieIndex> get()
	{
		if(lastValues != null)
		{
			List<IPieIndex> entries = new ObjectArrayList<IPieIndex>();
			for(int i = 0, m = lastValues.size();i < m;i++)
			{
				entries.add(new PieIndex(MathUtils.floor(lastValues.get(i).getEffect() * 1.28D), lastValues.get(i).getColor()));
			}
			return entries;
		}
		return ObjectLists.singleton(new PieIndex(pie.getMaxSteps(), ColorObject.LIGHT_BLUE));
	}
	
	protected float calculatePieHeight()
	{
		return getBox().getBaseHeight() - ((textInUse * 5.5F) + 9F);
	}
	
	public IProfiler getProfiler(int index)
	{
		GuiManager manager = getGui().getUIManager();
		switch(index)
		{
			case 1:
				return manager.getGPUProfiler();
			case 2:
				return manager.getServerProfiler();
			default:
				return manager.getCPUProfiler();
		}
	}
	
	public int getProfilerIndex(IProfiler prof)
	{
		GuiManager manager = getGui().getUIManager();
		return manager.getGPUProfiler() == prof ? 1 : manager.getServerProfiler() == prof ? 2 : 0;
	}
	
	public static String getRoot(int index)
	{
		return index < 2 ? "Client" : "Server";
	}
	
	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, 5.5F).setTextScale(0.3F).setText(text).setAlignment(column == 0 ? Align.LEFT_TOP : Align.RIGHT_BOTTOM, Align.CENTER);
		comp.addChangeListener(T -> T.setVisible(!isMinimized() && index < textInUse));
		Constrain xPos = column == 0 ? null : (column == 1 ? new PixelConstrain(38F).setInverted() : new PixelConstrain(19F).setInverted());
		addChild(comp, new ComponentConstrains(xPos, new DynamicConstrain(() -> pie.getBox().getBaseHeight() + (index * 5.5F)), column == 0 ? new PixelConstrain(38).setInverted() : null, null));
		return comp;
	}
	
	protected ButtonComponent createButton(int index, String name)
	{
		ButtonComponent button = new ButtonComponent(name, ColorObject.GRAY);
		button.getText().setTextScale(0.3F);
		button.addChangeListener(minimizedListener).addUserActionListener(T -> setProfiler(getProfiler(index), getRoot(index)));
		addChild(button, new ComponentConstrains(new RelativeConstrain(index * 0.3333F), new PixelConstrain(8F).setInverted(), new RelativeConstrain(0.3333F), new PixelConstrain(7F)));
		return button;
	}
}
