package speiger.src.coreengine.assets.manager;

import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.coreengine.assets.api.IAssetProvider;
import speiger.src.coreengine.assets.api.IResourceListener;
import speiger.src.coreengine.assets.api.IResourceListener.ISyncer;
import speiger.src.coreengine.assets.api.ReloadContext;

public class ReloadTask {
	AtomicInteger started = new AtomicInteger();
	AtomicInteger finished = new AtomicInteger();
	CompletableFuture<?> future;
	
	public ReloadTask(IAssetProvider provider, ReloadBuilder builder) {
		future = reload(new ReloadContext(provider, builder.filter, new CountingExecutor(builder.mainRunner, started, finished), new CountingExecutor(builder.taskRunner, started, finished), new CountingExecutor(builder.syncRunner, started, finished)), new ObjectArrayList<>(builder.listeners.get()));
	}
	
	public int started() { return started.get(); }
	public int stopped() { return finished.get(); }
	public float progress() { return finished.floatValue() / started.floatValue(); }
	public CompletableFuture<?> future() { return future; }
	public void join() { future.join(); }
	
	private CompletableFuture<?> reload(ReloadContext context, List<IResourceListener> listeners) {
		started.getAndIncrement();
		Set<IResourceListener> todo = new ObjectOpenHashSet<>(listeners);
		CompletableFuture<Void> syncFuture = new CompletableFuture<>();
		CompletableFuture<Void> chainedTask = CompletableFuture.completedFuture(null);
		List<CompletableFuture<Void>> tasks = new ObjectArrayList<>();
		for(IResourceListener listener : listeners) {
			CompletableFuture<Void> current = chainedTask;
			chainedTask = listener.reload(context, new Syncer(context.tasks(), current, syncFuture, listener, todo));
			tasks.add(chainedTask);
		}
		return CompletableFuture.allOf(tasks.toArray(CompletableFuture[]::new)).thenRunAsync(() -> {
			finished.getAndIncrement();
			try { context.provider().close(); }
			catch(Exception e) { e.printStackTrace(); }
		}, context.tasks());
	}

	
	private record Syncer(Executor main, CompletableFuture<?> currentStep, CompletableFuture<?> syncStep, IResourceListener listener, Set<IResourceListener> toComplete) implements ISyncer {
		@Override
		public <T> CompletableFuture<T> sync(T value) {
			main.execute(() -> {
				toComplete.remove(listener);
				if(toComplete.isEmpty()) syncStep.complete(null);
			});
			return syncStep.thenCombine(currentStep, (_, _) -> value);
		}
		
	}
	
	private record CountingExecutor(Executor wrapper, AtomicInteger started, AtomicInteger finished) implements Executor {
		@Override
		public void execute(Runnable command) {
			started.getAndIncrement();
			wrapper.execute(() -> {
				command.run();
				finished.getAndIncrement();
			});
		}
	}
}
