package speiger.src.coreengine.assets.impl;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Stream;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.collections.objects.utils.ObjectLists;
import speiger.src.coreengine.assets.api.IAsset;
import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.api.IFileWatcher;
import speiger.src.coreengine.assets.impl.FolderWatcher.ChangeType;

public class FolderAssetPackage implements IAssetPackage {
	Map<ID, AssetIdentity> knownIdentities = new HashMap<>();
	Path basePath;
	
	public FolderAssetPackage(Path basePath) {
		this.basePath = basePath;
	}
	
	@Override
	public void registerWatchers(IFileWatcher watcher) throws IOException {
		initCache();
		watcher.addDirectory(basePath);
	}
		
	private void initCache() throws IOException {
		for(Path path : Files.walk(basePath).filter(Files::isRegularFile).toList()) {
			knownIdentities.put(ID.of(basePath.relativize(path)), new AssetIdentity(path));
		}
	}
	
	public boolean handleChange(Path path, ChangeType type, Consumer<ID> changedIds) throws IOException {
		if(Files.notExists(basePath)) return false;
		Path target = basePath.relativize(path);
		ID id = ID.of(target);
		switch(type) {
			case ADD:
				if(Files.isRegularFile(path)) {
					knownIdentities.put(id, new AssetIdentity(path));				
					changedIds.accept(id);
				}
				break;
			case MODIFIED:
				AssetIdentity original = knownIdentities.get(id);
				if(original != null && !original.matches(path)) {
					knownIdentities.put(id, new AssetIdentity(path));				
					changedIds.accept(id);
				}
				break;
			case REMOVE: {
				if(knownIdentities.remove(id) != null) {
					changedIds.accept(id);
				}
				break;
			}
		}
		return true;
	}
	
	@Override
	public void close() throws Exception {}
	
	@Override
	public List<String> domains() {
		try(DirectoryStream<Path> dirs = Files.newDirectoryStream(basePath)) {
			List<String> domains = new ObjectArrayList<>();
			dirs.forEach(T -> domains.add(T.getFileName().toString()));
			return domains;
		}
		catch(Exception e) {
			e.printStackTrace();
			return ObjectLists.empty();
		}
	}
	
	@Override
	public IAsset get(ID location) {
		Path path = basePath.resolve(location.fileLocation());
		return Files.notExists(path) ? null : new SimpleAsset(this, location, path);
	}
	
	@Override
	public void getAll(ID folder, BiConsumer<ID, IAsset> assets) {
		Path start = basePath.resolve(folder.fileLocation());
		if(Files.notExists(start)) return;
		try(Stream<Path> stream = Files.walk(start).filter(Files::isRegularFile)) {
			for(Path path : (Iterable<Path>)() -> stream.iterator()) {
				ID id = folder.suffix(start.relativize(path));
				assets.accept(folder, new SimpleAsset(this, id, path));
			}
		}
		catch(Exception e) {
			e.printStackTrace();
		}
	}
	
}
