package speiger.src.coreengine.utils.profiler;

import java.util.Map;
import java.util.Set;
import java.util.function.ObjIntConsumer;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap;
import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet;
import speiger.src.collections.utils.Stack;
import speiger.src.coreengine.rendering.utils.GLStamper;

public class GPUProfiler implements IProfiler {
	IProfiler cpuSide;
	String name;
	Map<String, GPUProfilerEntry> cache = new Object2ObjectLinkedOpenHashMap<>();
	Stack<GPUProfilerEntry> activeProfilers = new ObjectArrayList<>();
	Set<ObjIntConsumer<IProfiler>> listeners = new ObjectLinkedOpenHashSet<>();
	long ticksRan = 0;
	int enableRequest = 0;
	
	public GPUProfiler(String name, IProfiler cpu) {
		this.name = name;
		cpuSide = cpu;
	}
	
	@Override
	public String getName() { return name; }
	@Override
	public IProfilerEntry getEntry(String name) { return cache.get(name); }
	@Override
	public long getTicksRan() { return ticksRan; }
	
	@Override
	public void enable() {
		enableRequest++;
		if(enableRequest == 1) sendState(0);
	}
	
	@Override
	public void disable() {
		enableRequest = Math.max(0, enableRequest - 1);
		if(enableRequest > 0) return;
		GLStamper.INSTANCE.removeOwner(name);
		cache.clear();
		while(!activeProfilers.isEmpty()) {
			activeProfilers.pop();
		}
		sendState(1);
	}
	
	@Override
	public boolean isEnabled() { return enableRequest > 0; }
	
	@Override
	public void addListener(ObjIntConsumer<IProfiler> listener) {
		listeners.add(listener);
		if(enableRequest > 0) listener.accept(this, 0);
	}
	
	@Override
	public void removeListener(ObjIntConsumer<IProfiler> listener) {
		listeners.remove(listener);
	}
	
	@Override
	public IProfiler start(String name) {
		cpuSide.start(name);
		if(enableRequest == 0) return this;
		String entry = (activeProfilers.isEmpty() ? "" : activeProfilers.top().getPathName()+"/") + name;
		GPUProfilerEntry active = cache.get(entry);
		if(active == null) {
			active = new GPUProfilerEntry(name, this);
			if(!activeProfilers.isEmpty()) activeProfilers.top().addChild(active);
			cache.put(entry, active);
			sendState(2);
		}
		activeProfilers.push(active);
		active.start();
		return this;
	}
	
	@Override
	public IProfiler start(Class<?> clz) {
		cpuSide.start(clz);
		return enableRequest == 0 ? this : start(clz.getSimpleName());
	}
	
	@Override
	public IProfiler stop() {
		cpuSide.stop();
		if(enableRequest == 0 || activeProfilers.isEmpty()) return this;
		activeProfilers.pop().stop();
		return this;
	}
	
	@Override
	public IProfiler next(String name) {
		return stop().start(name);
	}
	
	private void sendState(int state) {
		if(listeners.isEmpty()) return;
		for(ObjIntConsumer<IProfiler> entry : listeners) {
			entry.accept(this, state);
		}
	}
	
	@Override
	public void onFrameEnded(boolean end) {
		cpuSide.onFrameEnded(end);
		if(enableRequest == 0) return;
		ticksRan++;
		for(GPUProfilerEntry entry : cache.values()) {
			entry.onFrameFinished(end);
		}
	}
	
}
