package speiger.src.coreengine.assets.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import speiger.src.collections.objects.lists.ObjectArrayList;
import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.assets.IAsset;
import speiger.src.coreengine.assets.IAssetPackage;
import speiger.src.coreengine.assets.IAssetParser;
import speiger.src.coreengine.utils.collections.iterators.IterableWrapper;

public class ZipAssetPackage implements IAssetPackage
{
	Path baseFolder;
	Function<Class<?>, IAssetParser<?>> parsers;
	FileSystem cache;
	AtomicInteger usedReferences = new AtomicInteger();
	Runnable referenceCleaner = this::cleanReference;
	
	public ZipAssetPackage(File file)
	{
		this(file.toPath());
	}
	
	public ZipAssetPackage(Path baseFolder)
	{
		this.baseFolder = baseFolder;
	}
	
	@Override
	public void setParsers(Function<Class<?>, IAssetParser<?>> parsers)
	{
		this.parsers = parsers;
	}
	
	private void cleanReference()
	{
		int left = usedReferences.decrementAndGet();
		if(left == 0 && cache != null)
		{
			try { cache.close(); }
			catch(Exception e) {e.printStackTrace();}
			cache = null;
		}
		else if(left < 0) usedReferences.set(0);
	}
	
	protected FileSystem getReference() throws IOException
	{
		usedReferences.getAndIncrement();
		if(cache == null) cache = FileSystems.newFileSystem(baseFolder, null);
		return cache;
	}
	
	@Override
	public List<String> getDomains()
	{
		List<String> domains = new ObjectArrayList<>();
		try(FileSystem system = FileSystems.newFileSystem(baseFolder, null)) 
		{
			try(DirectoryStream<Path> dirs = Files.newDirectoryStream(system.getPath("assets")))
			{
				for(Path path : dirs)
				{
					String s = path.getFileName().toString();
					domains.add(s.substring(0, s.length()-1));
				}
			}
			catch(Exception e) { e.printStackTrace(); }
		}
		catch(Exception e) { e.printStackTrace(); }
		return domains;
	}
	
	@Override
	public IAsset getAsset(AssetLocation location)
	{
		try
		{
			Path path = getReference().getPath(location.getActualLocation());
			return Files.exists(path) ? new ZipAsset(referenceCleaner, location, path, parsers) : null;
		}
		catch(Exception e) { return null; }
	}
	
	@Override
	public void getAllAssets(AssetLocation folder, Predicate<String> fileNames, int maxDepth, Collection<AssetLocation> result)
	{
		try(FileSystem system = FileSystems.newFileSystem(baseFolder, null)) 
		{
			Path start = system.getPath(folder.getActualLocation());
			if(Files.notExists(start)) return;
			try(Stream<Path> stream = Files.walk(start, maxDepth).filter(Files::isRegularFile).filter(T -> fileNames.test(T.getFileName().toString())))
			{
				for(Path path : IterableWrapper.wrap(stream.iterator()))
				{
					result.add(folder.subAsset(start.relativize(path).toString().replace("\\", "/")));
				}
			}
			catch(IOException e) { e.printStackTrace(); }
		}
		catch(Exception e) { e.printStackTrace(); }
	}
}
