package org.bndtools.core.ui.wizards.shared;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.bndtools.templating.Template;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.widgets.Control;

import aQute.lib.io.IO;
import bndtools.Plugin;

/*
 * Loads icons from the templates in a background job, and updates the UI in batches when icons become available.
 */
public class IconLoaderJob extends Job {

	private final ILog					log	= Plugin.getDefault()
		.getLog();

	private final Collection<Template>	templates;
	private final int					batchLimit;
	private final StructuredViewer		viewer;

	private final Map<Template, Image>	loadedImageMap;

	public IconLoaderJob(Collection<Template> templates, StructuredViewer viewer, Map<Template, Image> loadedImageMap,
		int batchLimit) {
		super("load template icons");
		this.templates = templates;
		this.viewer = viewer;
		this.loadedImageMap = loadedImageMap;
		this.batchLimit = batchLimit;
	}

	@Override
	protected IStatus run(IProgressMonitor monitor) {
		SubMonitor progress = SubMonitor.convert(monitor, templates.size());

		Map<Template, byte[]> batch = new IdentityHashMap<>();

		for (Template template : templates) {
			InputStream iconStream = null;
			try {
				URI iconUri = template.getIcon();
				if (iconUri != null) {
					iconStream = iconUri.toURL()
						.openStream();
					byte[] bytes = IO.read(iconStream);
					batch.put(template, bytes);

					if (batch.size() >= batchLimit) {
						processBatch(batch);
						batch = new IdentityHashMap<>();
					}
				}
			} catch (Exception e) {
				log.log(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0,
					"Error reading icon for template '" + template.getName() + "'", e));
			} finally {
				IO.close(iconStream);
			}

			progress.worked(1);
		}
		processBatch(batch);
		return Status.OK_STATUS;
	}

	private void processBatch(final Map<Template, byte[]> batch) {
		if (batch.isEmpty())
			return;
		final Control control = viewer.getControl();
		if (control != null && !control.isDisposed()) {
			control.getDisplay()
				.asyncExec(() -> {
					if (control.isDisposed())
						return;
					List<Object> toUpdate = new ArrayList<>(batch.size());

					for (Entry<Template, byte[]> entry : batch.entrySet()) {
						Template template = entry.getKey();
						byte[] imgBytes = entry.getValue();

						try {
							ImageData imgData = new ImageData(new ByteArrayInputStream(imgBytes));
							Image image = new Image(control.getDisplay(), imgData);

							Image old = loadedImageMap.put(template, image);
							if (old != null && !old.isDisposed())
								old.dispose();
							toUpdate.add(template);
						} catch (Exception e) {
							log.log(new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0,
								"Error loading image data for template icon: " + template.getName(), e));
						}
					}

					viewer.update(toUpdate.toArray(), null);
				});
		}
	}

}
