package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.io.*;
import ij.util.Tools;
import java.awt.*;
import java.io.*;
import java.util.*;

/** This plugin opens images specified by list of file paths as a virtual stack.
	It implements the File/Import/Stack From List command. */
public class ListVirtualStack extends VirtualStack implements PlugIn {
	private static boolean virtual;
	private String[] list;
	private String[] labels;
	private int nImages;
	private int imageWidth, imageHeight;
	private ImagePlus imp2;

	public void run(String arg) {
		OpenDialog  od = new OpenDialog("Open Image List", arg);
		String name = od.getFileName();
		if (name==null) return;
		String  dir = od.getDirectory();
		//IJ.log("ListVirtualStack: "+dir+"   "+name);
		list = open(dir+name);
		if (list==null) return;
		nImages = list.length;
		labels = new String[nImages];
		//for (int i=0; i<list.length; i++)
		//	IJ.log(i+"  "+list[i]);
		if (list.length==0) {
			IJ.error("Stack From List", "The file path list is empty");
			return;
		}
		if (!list[0].startsWith("http://")) {
			File f = new File(list[0]);
			if (!f.exists()) {
				IJ.error("Stack From List", "The first file on the list does not exist:\n \n"+list[0]);
				return;
			}
		}
		ImagePlus imp = IJ.openImage(list[0]);
		if (imp==null) return;
		imageWidth = imp.getWidth();
		imageHeight = imp.getHeight();
		setBitDepth(imp.getBitDepth());
		ImageStack stack = this;
		if (!showDialog(imp)) return;
		if (!virtual)
			stack = convertToRealStack(imp);
		imp2 = new ImagePlus(name, stack);
		imp2.setCalibration(imp.getCalibration());
		imp2.setFileInfo(imp.getOriginalFileInfo());
		imp2.show();
	}
	
	boolean showDialog(ImagePlus imp) {
		double bytesPerPixel = 1;
		switch (imp.getType()) {
			case ImagePlus.GRAY16:
				bytesPerPixel=2; break;
			case ImagePlus.COLOR_RGB:
			case ImagePlus.GRAY32:
				bytesPerPixel=4; break;
		}
		double size = (imageWidth*imageHeight*bytesPerPixel)/(1024.0*1024.0);
		int digits = size*getSize()<10.0?1:0;
		String size1 = IJ.d2s(size*getSize(), digits)+" MB";
		String size2 = IJ.d2s(size,1)+" MB";
		GenericDialog gd = new GenericDialog("Open Stack From List");
		gd.addCheckbox("Use Virtual Stack", virtual);
		gd.addMessage("This "+imageWidth+"x"+imageHeight+"x"+getSize()+" stack will require "+size1+",\n or "+size2+" if opened as a virtual stack.");
		gd.showDialog();
		if (gd.wasCanceled()) return false;
		virtual = gd.getNextBoolean();
		return true;
	}
	
	ImageStack convertToRealStack(ImagePlus imp) {
		ImageStack stack2 = new ImageStack(imageWidth, imageHeight, imp.getProcessor().getColorModel());
		int n = this.getSize();
		for (int i=1; i<=this.getSize(); i++) {
			IJ.showProgress(i, n);
			IJ.showStatus("Opening: "+i+"/"+n);
			ImageProcessor ip2 = this.getProcessor(i);
			if (ip2!=null)
				stack2.addSlice(this.getSliceLabel(i), ip2);
		}
		return stack2;
	}
	
	String[] open(String path) {
		if (path.startsWith("http://"))
			return openUrl(path);
		Vector v = new Vector();
		File file = new File(path);
		try {
			BufferedReader r = new BufferedReader(new FileReader(file));
			while (true) {
				String s=r.readLine();
				if (s==null || s.equals("") || s.startsWith(" "))
					break;
				else
					v.addElement(s);
			}
			r.close();
    		String[] list = new String[v.size()];
			v.copyInto((String[])list);
    		return list;
		}
		catch (Exception e) {
			IJ.error("Open List Error \n\""+e.getMessage()+"\"\n");
		}
		return null;
	}

	String[] openUrl(String url) {
		String str = IJ.openUrlAsString(url);
		if (str.startsWith("<Error: ")) {
			IJ.error("Stack From List", str);
			return null;
		} else
			return Tools.split(str, "\n");
	}
	
	/** Deletes the specified image, where {@literal 1<=n<=nslices}. */
	public void deleteSlice(int n) {
		if (n<1 || n>nImages)
			throw new IllegalArgumentException("Argument out of range: "+n);
		if (nImages<1) return;
		for (int i=n; i<nImages; i++)
			list[i-1] = list[i];
		list[nImages-1] = null;
		nImages--;
	}
	
	/** Returns an ImageProcessor for the specified slice,
		where {@literal 1<=n<=nslices}. Returns null if the stack is empty.
	*/
	public ImageProcessor getProcessor(int n) {
		if (n<1 || n>nImages)
			throw new IllegalArgumentException("Argument out of range: "+n);
		IJ.redirectErrorMessages(true);
		String url = list[n-1];
		ImagePlus imp = null;
		if (url.length()>0)
			imp = IJ.openImage(url);
		if (imp!=null) {
			labels[n-1] = (new File(list[n-1])).getName()+"\n"+(String)imp.getProperty("Info");
			ImageProcessor ip =  imp.getProcessor();
			int bitDepth = getBitDepth();
			if (imp.getBitDepth()!=bitDepth) {
				switch (bitDepth) {
					case 8: ip=ip.convertToByte(true); break;
					case 16: ip=ip.convertToShort(true); break;
					case 24:  ip=ip.convertToRGB(); break;
					case 32: ip=ip.convertToFloat(); break;
				}
			}
			if (ip.getWidth()!=imageWidth || ip.getHeight()!=imageHeight)
			ip = ip.resize(imageWidth, imageHeight);
			IJ.redirectErrorMessages(false);
			if (imp2!=null)
				imp2.setFileInfo(imp.getOriginalFileInfo());
			ip.setSliceNumber(n);
			return ip;
		} else {
				ImageProcessor ip = null;
				switch (getBitDepth()) {
					case 8: ip=new ByteProcessor(imageWidth,imageHeight); break;
					case 16: ip=new ShortProcessor(imageWidth,imageHeight); break;
					case 24:  ip=new ColorProcessor(imageWidth,imageHeight); break;
					case 32: ip=new FloatProcessor(imageWidth,imageHeight); break;
				}
			IJ.redirectErrorMessages(false);
			ip.setSliceNumber(n);
			return ip;
		}
	 }
 
	 /** Returns the number of images in this stack. */
	public int getSize() {
		return nImages;
	}

	/** Returns the name of the specified image. */
	public String getSliceLabel(int n) {
		if (n<1 || n>nImages)
			throw new IllegalArgumentException("Argument out of range: "+n);
		if (labels[n-1]!=null)
			return labels[n-1];
		else
			return (new File(list[n-1])).getName();
	}
	
	public int getWidth() {
		return imageWidth;
	}

	public int getHeight() {
		return imageHeight;
	}
	
	@Override
	public void reduce(int factor) {
		if (factor<2 || nImages/factor<1)
			return;
		nImages = nImages/factor;
		for (int i=0; i<nImages; i++) {
			list[i] = list[i*factor];
			labels[i] = labels[i*factor];
		}
		ImagePlus imp = WindowManager.getCurrentImage();
		if (imp!=null) {
			imp.setSlice(1);
			imp.updateAndRepaintWindow();
		}
	}

}
