package speiger.src.coreengine.assets.manager;

import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;

import org.jspecify.annotations.Nullable;

import speiger.src.collections.objects.sets.ObjectOpenHashSet;
import speiger.src.coreengine.assets.api.IAsset;
import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.IAssetProvider;
import speiger.src.coreengine.assets.api.IAssetProvider.ReloadTracker;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.api.IResourceListener;
import speiger.src.coreengine.assets.api.MultiAsset;
import speiger.src.coreengine.assets.impl.ReloadManager;
import speiger.src.coreengine.assets.providers.BaseProvider;
import speiger.src.coreengine.assets.providers.LayeredProvider;
import speiger.src.coreengine.assets.providers.SingleProvider;

public class AssetManager {
	BaseProvider provider;
	List<IAssetPackage> packages;
	ReloadTracker tracker = new ReloadTracker();
	ReloadManager manager;
	AtomicInteger InUse = new AtomicInteger();
	
	public AssetManager(BaseProvider provider) {
		setPackages(provider);
	}
	
	public static AssetManager single(IAssetPackage pack) {
		return new AssetManager(new SingleProvider(pack));
	}
	
	public static AssetManager mult(List<IAssetPackage> packages) {
		return new AssetManager(new LayeredProvider(packages));
	}
	
	private void setPackages(BaseProvider provider) {
		this.provider = provider;
		this.packages = provider.packages();
		if(manager != null) {
			manager.reset();
			packages.forEach(manager::registerPackage);
		}
	}
	
	public AssetManager withReload(@Nullable Consumer<Path> onNewFound, @Nullable Consumer<IAssetPackage> onRemoval) {
		if(manager != null) throw new IllegalStateException("Can't replace the reload system");
		manager = new ReloadManager(null, onNewFound, onRemoval);
		manager.enableReload();
		packages.forEach(manager::registerPackage);
		return this;
	}
	
	public void register(IResourceListener listener) {
		tracker.register(listener);
	}
	
	public void register(Predicate<ID> filter, IResourceListener listener) {
		tracker.register(filter, listener);
	}
	
	public ReloadTracker listeners() {
		return tracker;
	}
	
	public ReloadTask reload(ReloadBuilder builder) {
		if(builder.packages.isPresent()) setPackages(builder.packages.get());
		if(builder.listeners.isEmpty()) builder.withListener(tracker.listeners());
		return new ReloadTask(get(), builder);
	}
	
	public boolean isReloadable() {
		return manager != null;
	}
	
	@Nullable
	public ReloadTask handleChanges(ReloadBuilder builder) {
		if(manager == null) return null;
		Set<ID> changed = new ObjectOpenHashSet<>();
		List<IResourceListener> reloading = manager.handleFileEvents(tracker, changed);
		if(reloading.isEmpty()) return null;
		return reload(builder.withFilter(changed).withPackages((BaseProvider)null).withListener(reloading));
	}
	
	public IAssetProvider get() {
		InUse.incrementAndGet();
		return new WrappedProvider(provider, this::manageClose);
	}
	
	private void manageClose() throws Exception {
		if(InUse.decrementAndGet() > 0) return;
		provider.close();
	}
	
	public final class WrappedProvider implements IAssetProvider {
		BaseProvider provider;
		AutoCloseable closer;
		
		public WrappedProvider(BaseProvider provider, AutoCloseable closer) {
			this.provider = provider;
			this.closer = closer;
		}
		
		@Override
		public void close() throws Exception {
			if(provider == null) return;
			this.provider = null;
			closer.close();
		}
		
		@Override
		public @Nullable IAsset get(ID id) { return provider.get(id); }
		@Override
		public @Nullable MultiAsset getAll(ID id) { return provider.getAll(id); }
		@Override
		public Map<ID, IAsset> list(String folder, Predicate<ID> filter) { return provider.list(folder, filter); }
		@Override
		public Map<ID, MultiAsset> listAll(String folder, Predicate<ID> filter) { return provider.listAll(folder, filter); }
	}
}
