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.Attrs;
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<>();

		for (Entry<String, Attrs> entry : provideCapabilities.entrySet()) {
			String key = Processor.removeDuplicateMarker(entry.getKey());
			if ("osgi.serviceloader".equals(key)) {
				Attrs attrs = entry.getValue();

				String serviceType = attrs.get("osgi.serviceloader");
				String serviceImpl = attrs.get("register:");

				if (serviceImpl == null) {
					analyzer.warning(
						"osgi.serviceloader capability found with no 'register:' directive. Descriptor cannot be managed for %s;%s",
						key, attrs);

					continue;
				}

				providerTypes.computeIfAbsent(serviceType, k -> new ArrayList<>())
					.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);

			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()));
			}
		}
	}

}
