package org.osgi.impl.bundle.repoindex.ant;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.osgi.framework.BundleContext;
import org.osgi.framework.launch.Framework;
import org.osgi.service.indexer.ResourceAnalyzer;
import org.osgi.service.indexer.ResourceIndexer;
import org.osgi.service.indexer.impl.KnownBundleAnalyzer;
import org.osgi.util.tracker.ServiceTracker;

import de.kalpatec.pojosr.framework.PojoServiceRegistryFactoryImpl;
import de.kalpatec.pojosr.framework.launch.ClasspathScanner;
import de.kalpatec.pojosr.framework.launch.PojoServiceRegistryFactory;

@SuppressWarnings("restriction")
public class RepoIndexTask extends Task {

	private final List<FileSet>			fileSets			= new LinkedList<FileSet>();
	private final Map<String,String>	config				= new HashMap<String,String>();

	private File						repositoryFile		= null;
	private boolean						knownBundles;
	private boolean						builtInknownBundles	= true;
	private String						additionalKnownBundles;

	public void setName(String name) {
		config.put(ResourceIndexer.REPOSITORY_NAME, name);
	}

	public void setVerbose(boolean verbose) {
		config.put(ResourceIndexer.VERBOSE, Boolean.toString(verbose));
	}

	public void setPretty(boolean pretty) {
		config.put(ResourceIndexer.PRETTY, Boolean.toString(pretty));
	}

	public void setCompressed(boolean compressed) {
		config.put(ResourceIndexer.COMPRESSED, Boolean.toString(compressed));
	}

	public void setRootURL(String root) {
		config.put(ResourceIndexer.ROOT_URL, root);
	}

	public void setOut(String outFile) {
		this.repositoryFile = new File(outFile);
	}

	public void addFileset(FileSet fs) {
		fileSets.add(fs);
	}

	public void setKnownBundles(boolean knownBundles) {
		this.knownBundles = knownBundles;
	}

	public void setBuiltInKnownBundles(boolean builtInknownBundles) {
		this.builtInknownBundles = builtInknownBundles;
	}

	public void setAdditionalKnownBundles(String additionalKnownBundles) {
		this.additionalKnownBundles = additionalKnownBundles;
	}

	@Override
	public void execute() throws BuildException {
		printCopyright(System.err);

		if (repositoryFile == null)
			throw new BuildException("Output file not specified");

		try {
			// Configure PojoSR
			Map<String,Object> pojoSrConfig = new HashMap<String,Object>();
			pojoSrConfig.put(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS, new ClasspathScanner());

			// Start PojoSR 'framework'
			Framework framework = new PojoServiceRegistryFactoryImpl().newFramework(pojoSrConfig);
			framework.init();
			framework.start();

			if (knownBundles) {
				registerKnownBundles(framework.getBundleContext());
			}

			// Look for indexer and run index generation
			ServiceTracker<ResourceIndexer,ResourceIndexer> tracker = new ServiceTracker<ResourceIndexer,ResourceIndexer>(
					framework.getBundleContext(), ResourceIndexer.class, null);
			tracker.open();
			ResourceIndexer index = tracker.waitForService(1000);
			if (index == null)
				throw new IllegalStateException("Timed out waiting for ResourceIndexer service.");

			// Flatten the file sets into a single list
			Set<File> fileList = new LinkedHashSet<File>();
			for (FileSet fileSet : fileSets) {
				DirectoryScanner ds = fileSet.getDirectoryScanner(getProject());
				File basedir = ds.getBasedir();
				String[] files = ds.getIncludedFiles();
				for (int i = 0; i < files.length; i++)
					fileList.add(new File(basedir, files[i]));
			}

			// Run
			try (OutputStream fos = Files.newOutputStream(repositoryFile.toPath())) {
				index.index(fileList, fos, config);
			}
		} catch (Exception e) {
			throw new BuildException(e);
		}
	}

	private void registerKnownBundles(BundleContext bundleContext) {
		KnownBundleAnalyzer kba = builtInknownBundles ? new KnownBundleAnalyzer()
				: new KnownBundleAnalyzer(new Properties());

		if (additionalKnownBundles != null) {
			File extras = new File(additionalKnownBundles);
			if (extras.exists()) {
				Properties props = new Properties();
				try (Reader r = new FileReader(extras)) {
					props.load(r);
					kba.setKnownBundlesExtra(props);
				} catch (IOException e) {
					throw new BuildException("Unable to load the additional known bundles " + additionalKnownBundles,
							e);
				}
			} else {
				throw new BuildException(
						"The additional known bundles file " + additionalKnownBundles + " does not exist.");
			}
		}

		bundleContext.registerService(ResourceAnalyzer.class, kba, null);
	}

	public static void printCopyright(PrintStream out) {
		out.println("Bindex2 | Resource Indexer v1.0");
		out.println("(c) 2012 OSGi, All Rights Reserved");
	}

}
