package speiger.src.coreengine.assets.impl;

import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import org.jspecify.annotations.Nullable;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap;
import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.collections.objects.utils.ObjectLists;
import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.IAssetProvider.ReloadTracker;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.api.IFileWatcher.FileWatcher;
import speiger.src.coreengine.assets.api.IResourceListener;
import speiger.src.coreengine.assets.impl.FolderWatcher.ChangeType;

public class ReloadManager {
	private static final ScopedValue<IAssetPackage> CURRENT_PACKAGE = ScopedValue.newInstance();
	private static final ScopedValue<Set<ID>> TRACKED = ScopedValue.newInstance();
	Map<Path, Map<Path, IAssetPackage>> trackers;
	FolderWatcher<IAssetPackage> watcher;
	Runnable onStart;
	Consumer<Path> onNewFound;
	Consumer<IAssetPackage> onRemoved;
	
	public ReloadManager(Runnable onStart, Consumer<Path> onNewFound, Consumer<IAssetPackage> onRemoved) {
		this.onStart = onStart;
		this.onNewFound = onNewFound;
		this.onRemoved = onRemoved;
	}

	public void enableReload() {
		if(trackers != null) return;
		try {
			watcher = new FolderWatcher<>();			
			trackers = Object2ObjectMap.builder().map();
			if(onStart != null) onStart.run();
		}
		catch(Exception e) { e.printStackTrace(); }
	}
	
	public void disableReload() {
		if(trackers == null) return;
		try { watcher.close(); }
		catch(Exception e) { e.printStackTrace(); }
		watcher = null;
		trackers = null;
	}
	
	public void reset() {
		if(trackers == null) return;
		trackers.clear();
		watcher.reset();
	}
	
	public void registerPackage(IAssetPackage pack) {
		try { ScopedValue.where(CURRENT_PACKAGE, pack).call(this::registerWatchers); }
		catch(Exception e) { e.printStackTrace(); }
	}
	
	public List<IResourceListener> handleFileEvents(ReloadTracker listeners, @Nullable Set<ID> changedIds) {
		if(watcher == null) return ObjectLists.empty();
		if(changedIds == null) changedIds = new ObjectOpenHashSet<>();
		ScopedValue.where(TRACKED, changedIds).run(() -> watcher.pollEvents(this::handleEvent));
		return changedIds.isEmpty() ? ObjectLists.empty() : listeners.findByFilter(changedIds);
	}
	
	protected void handleEvent(Path path, ChangeType type, IAssetPackage owner) {
		if(owner == null) {
			owner = findOwner(path);
			if(owner == null) {
				if(type == ChangeType.ADD && onNewFound != null) onNewFound.accept(path);
				return;
			}
		}
		try {
			List<ID> changes = new ObjectArrayList<>();
			if(owner.handleChange(path, type, changes::add)) {
				TRACKED.get().addAll(changes);
				return;			
			}
		}
		catch(Exception e) { e.printStackTrace(); }
		if(onRemoved != null) onRemoved.accept(owner);
	}
	
	protected IAssetPackage findOwner(Path path) {
		Map<Path, IAssetPackage> packages = trackers.get(path.getParent());
		return packages == null ? null : packages.get(path.getFileName());
	}
	
	private Void registerWatchers() throws IOException {
		if(!CURRENT_PACKAGE.isBound()) throw new IllegalStateException("Current Package is required!");
		CURRENT_PACKAGE.get().registerWatchers(new FileWatcher(this::onFolderChanged, this::onFileChange));
		return null;
	}
	
	private void onFileChange(Path file) throws IOException {
		if(!CURRENT_PACKAGE.isBound()) throw new IllegalStateException("Current Package is required!");
		Map<Path, IAssetPackage> packages = trackers.get(file.getParent());
		if(packages == null) {
			packages = Object2ObjectMap.builder().map();
			trackers.put(file.getParent(), packages);
			watcher.register(file, null, false);
		}
		packages.put(file.getFileName(), CURRENT_PACKAGE.get());
	}
	
	private void onFolderChanged(Path folder) throws IOException {
		if(!CURRENT_PACKAGE.isBound()) throw new IllegalStateException("Current Package is required!");
		watcher.register(folder, CURRENT_PACKAGE.get(), true);
	}
}
