package de.siphalor.was.content.pack;

import de.siphalor.was.content.resource.CallbackResource;
import de.siphalor.was.content.resource.FileResource;
import de.siphalor.was.content.resource.Resource;
import de.siphalor.was.util.Util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Stream;

public class JarContentPack implements ContentPack {
	private final String id;
	private final String baseLocation;
	private final ClassLoader classLoader;

	public JarContentPack(String id) {
		this(id, "");
	}

	public JarContentPack(String id, String baseLocation) {
		this(id, baseLocation, Thread.currentThread().getContextClassLoader());
	}

	public JarContentPack(String id, String baseLocation, ClassLoader classLoader) {
		this.id = id;
		this.baseLocation = baseLocation;
		this.classLoader = classLoader;
	}

	// Inspired from this: https://stackoverflow.com/a/48190582/7582022
	@Override
	@NotNull
	public Stream<Resource> getResources(@NotNull String location, @NotNull String type) {
		final String extension = "." + type;
		try {
			URL url = classLoader.getResource(baseLocation + "/" + location);

			if (url != null) {
				if ("file".equals(url.getProtocol())) {
					Path basePath = Path.of(url.toURI());
					return Files.find(basePath, Integer.MAX_VALUE, (path, attributes) -> attributes.isRegularFile() && path.getFileName().getName(0).toString().endsWith(extension)).map(path ->
							new FileResource(Util.pathToId(id, basePath.relativize(path).toString()), path.toFile())
					);
				} else if ("jar".equals(url.getProtocol())) {
					// The url path section begins with "file:" and ends with an exclamation mark and the path inside the jar
					// URLs also encode symbols with dollar sign so we need to decode them
					String urlPath = url.getPath();
					String jarPath = URLDecoder.decode(urlPath.substring(5, urlPath.indexOf('!')), StandardCharsets.UTF_8);

					JarFile jarFile = new JarFile(jarPath);
					Enumeration<JarEntry> entries = jarFile.entries();

					final String absLocation = baseLocation + "/" + location + "/";
					final int absLength = absLocation.length();

					ArrayList<Resource> resources = new ArrayList<>();

					while (entries.hasMoreElements()) {
						JarEntry entry = entries.nextElement();
						String name = entry.getName();
						if (name.startsWith(absLocation) && name.length() != absLocation.length()) {
							resources.add(new CallbackResource(Util.pathToId(id, name.substring(absLength)), () -> classLoader.getResourceAsStream(name)));
						}
					}
					return resources.stream();
				}
			}
		} catch (IOException | URISyntaxException e) {
			e.printStackTrace();
		}
		return Stream.empty();
	}

	@Override
	@Nullable
	public Resource getResource(@NotNull String location) {
		URL url = classLoader.getResource(baseLocation + "/" + location);
		if (url != null) {
			return new CallbackResource(Util.pathToId(id, location), () -> classLoader.getResourceAsStream(baseLocation + "/" + location));
		}
		return null;
	}

	@NotNull
	@Override
	public String getId() {
		return id;
	}
}
