package aQute.bnd.plugin.spi;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import aQute.bnd.header.Parameters;
import aQute.bnd.osgi.Analyzer;
import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.EmbeddedResource;
import aQute.bnd.osgi.Processor;
import aQute.bnd.osgi.Resource;
import aQute.bnd.service.verifier.VerifierPlugin;
import aQute.lib.io.IO;
import aQute.lib.strings.Strings;

/**
 * Plugin that executes during the verify phase in order to generate SPI
 * descriptor files (a.k.a. {@code META-INF/services/*}) from
 * {@code osgi.serviceloader} capabilities.
 */
public class SPIDescriptorGenerator implements VerifierPlugin {
	@Override
	public void verify(Analyzer analyzer) throws Exception {
		Parameters provideCapabilities = new Parameters(analyzer.getJar()
			.getManifest()
			.getMainAttributes()
			.getValue(Constants.PROVIDE_CAPABILITY), analyzer, true);

		Map<String, ArrayList<String>> providerTypes = new HashMap<>();

		provideCapabilities.stream()
			.filterKey(key -> Processor.removeDuplicateMarker(key)
				.equals("osgi.serviceloader"))
			.values()
			.forEachOrdered(attrs -> {
				String serviceType = attrs.get("osgi.serviceloader");
				String serviceImpl = attrs.get("register:");

				ArrayList<String> list = providerTypes.computeIfAbsent(serviceType, k -> new ArrayList<>());

				if (serviceImpl != null) {
					list.add(serviceImpl);
				}
			});

		for (Entry<String, ArrayList<String>> entry : providerTypes.entrySet()) {
			String key = "META-INF/services/" + entry.getKey();
			ArrayList<String> list = entry.getValue();

			Resource resource = analyzer.getJar()
				.getResource(key);

			if (resource == null && list.isEmpty()) {
				analyzer.warning(
					"osgi.serviceloader capability found with no 'register:' directive. Descriptor 'META-INF/services/%s' cannot be generated unless the osgi.serviceloader capability specifies the 'register:' directive",
					entry.getKey());

				continue;
			}

			String value = "";

			if (resource != null) {
				value = IO.collect(resource.openInputStream());

				try (BufferedReader br = new BufferedReader(new StringReader(value))) {
					String line;
					while ((line = br.readLine()) != null) {
						line = line.trim();
						if (line.isEmpty() || (line.charAt(0) == '#')) {
							continue;
						}
						list.remove(line);
					}
				}
			}

			// Only write if we have something to add
			if (!list.isEmpty()) {
				list.add(0, "# Generated by bnd");

				if (!value.isEmpty()) {
					value += "\n\n";
				}

				value += Strings.join("\n", list);

				analyzer.getJar()
					.putResource(key, new EmbeddedResource(value, analyzer.lastModified()));
			}
		}
	}

}
