package ij.plugin;
import ij.*;
import ij.process.*;
import ij.gui.*;
import ij.plugin.frame.RoiManager;
import ij.plugin.frame.Recorder;
import ij.macro.Interpreter;
import ij.io.RoiDecoder;
import ij.plugin.filter.PlugInFilter;
import ij.measure.*;
import java.awt.*;
import java.util.ArrayList;
import java.awt.geom.Rectangle2D;

/** This plugin implements the commands in the Image/Overlay menu. */
public class OverlayCommands implements PlugIn {
	private static int opacity = 100;
	private static Roi defaultRoi;
	private static boolean zeroTransparent;
	
	static {
		defaultRoi = new Roi(0, 0, 1, 1);
		defaultRoi.setPosition(1); // set stacks positions by default
	}

	public void run(String arg) {
		if (arg.equals("add"))
			addSelection();
		else if (arg.equals("image"))
			addImage(false);
		else if (arg.equals("image-roi"))
			addImage(true);
		else if (arg.equals("flatten"))
			flatten();
		else if (arg.equals("hide"))
			hide();
		else if (arg.equals("show"))
			show();
		else if (arg.equals("remove"))
			remove();
		else if (arg.equals("from"))
			fromRoiManager();
		else if (arg.equals("to"))
			toRoiManager();
		else if (arg.equals("list"))
			list();
		else if (arg.equals("measure"))
			measure();
		else if (arg.equals("options"))
			options();
	}
	
	private void measure() {
		ImagePlus imp = IJ.getImage();
		if (imp==null)
			return;
		Overlay overlay = imp.getOverlay();
		if (overlay==null) {
			IJ.error("Overlay required");
			return;
		}
		Roi roi0 = imp.getRoi();
		Roi roi1 = roi0;
		if (roi0!=null && !roi0.isArea())
			roi0 = null;
		boolean isPoints = false;
		for (int i=0; i<overlay.size(); i++) {
			Roi roi = (Roi)overlay.get(i).clone();
			if (roi0!=null && roi instanceof PointRoi) {
				isPoints = true;
				PointRoi croppedRoi = ((PointRoi)roi).crop(roi0);
				imp.setRoi(croppedRoi);
				IJ.run(imp, "Measure", "");
			} else {
				imp.setRoi(roi);
				ImageStatistics stats = null;
				if (roi0!=null) {
					ImageProcessor ip = imp.getProcessor();
					stats = ImageStatistics.getStatistics(ip, Measurements.CENTROID, null);
				}
				if (stats==null || roi0.contains((int)stats.xCentroid,(int)stats.yCentroid)) {
					if (stats!=null)
						roi.setFillColor(Color.cyan);
					IJ.run(imp, "Measure", "");
				}
			}
		}
		imp.setRoi(roi1);
		if (roi0!=null && !IJ.isMacro() && !isPoints) {
			IJ.wait(3000);
			for (int i=0; i<overlay.size(); i++)
				overlay.get(i).setFillColor(null);
			imp.draw();
		}
	}
			
	void addSelection() {
		ImagePlus imp = IJ.getImage();
		String macroOptions = Macro.getOptions();
		if (macroOptions!=null && IJ.macroRunning() && macroOptions.indexOf("remove")!=-1) {
			imp.setOverlay(null);
			return;
		}
		Roi roi = imp.getRoi();
		if (roi==null && imp.getOverlay()!=null) {
			GenericDialog gd = new GenericDialog("No Selection");
			gd.addMessage("\"Overlay>Add\" requires a selection.");
			gd.setInsets(15, 40, 0);
			gd.addCheckbox("Remove existing overlay", false);
			gd.showDialog();
			if (gd.wasCanceled()) return;
			if (gd.getNextBoolean())
				imp.setOverlay(null);
			return;
 		}
		if (roi==null) {
			IJ.error("This command requires a selection.");
			return;
		}
		roi = (Roi)roi.clone();
		Overlay overlay = imp.getOverlay();
		if (!roi.isDrawingTool()) {
			if (roi.getStroke()==null)
				roi.setStrokeWidth(defaultRoi.getStrokeWidth());
			if (roi.getStrokeColor()==null || Line.getWidth()>1&&defaultRoi.getStrokeColor()!=null)
				roi.setStrokeColor(defaultRoi.getStrokeColor());
			if (roi.getFillColor()==null)
				roi.setFillColor(defaultRoi.getFillColor());
		}
		setPosition(imp, roi);
		boolean points = roi instanceof PointRoi && ((PolygonRoi)roi).getNCoordinates()>1;
		if (IJ.altKeyDown() || (IJ.macroRunning() && Macro.getOptions()!=null)) {
			RoiProperties rp = new RoiProperties("Add to Overlay", roi);
			if (!rp.showDialog()) return;
			defaultRoi.setStrokeColor(roi.getStrokeColor());
			defaultRoi.setStrokeWidth(roi.getStrokeWidth());
			defaultRoi.setFillColor(roi.getFillColor());
		}
		String name = roi.getName();
		boolean newOverlay = name!=null && name.equals("new-overlay");
		Roi roiClone = (Roi)roi.clone();
		if (roi.getStrokeColor()==null)
			roi.setStrokeColor(Roi.getColor());
		if (overlay==null || newOverlay)
			overlay = OverlayLabels.createOverlay();
		overlay.add(roi);
		imp.setOverlay(overlay);
		boolean brushRoi = roi.getType()==Roi.COMPOSITE && Toolbar.getToolId()==Toolbar.OVAL && Toolbar.getBrushSize()>0;
		if (points || (roi instanceof ImageRoi) || (roi instanceof Arrow&&!Prefs.keepArrowSelections) || brushRoi)
			imp.deleteRoi();
		Undo.setup(Undo.OVERLAY_ADDITION, imp);
	}
	
	void addImage(boolean createImageRoi) {
		ImagePlus imp = IJ.getImage();
		int[] wList = WindowManager.getIDList();
		if (wList==null || wList.length<2) {
			IJ.error("Add Image...", "The command requires at least two open images.");
			return;
		}
		String[] titles = new String[wList.length];
		for (int i=0; i<wList.length; i++) {
			ImagePlus imp2 = WindowManager.getImage(wList[i]);
			titles[i] = imp2!=null?imp2.getTitle():"";
		}
		int x=0, y=0;
		Roi roi = imp.getRoi();
		if (roi!=null && roi.isArea()) {
			Rectangle r = roi.getBounds();
			x = r.x; y = r.y;
		}
		int index = 0;
		if (wList.length==2) {
			ImagePlus i1 = WindowManager.getImage(wList[0]);
			ImagePlus i2 = WindowManager.getImage(wList[1]);
			if (i2.getWidth()<i1.getWidth() && i2.getHeight()<i1.getHeight())
				index = 1;
		} else if (imp.getID()==wList[0])
			index = 1;

		String title = createImageRoi?"Create Image ROI":"Add Image...";
		if (IJ.isMacro()) {
			opacity = 100;
			zeroTransparent = false;
		}
		GenericDialog gd = new GenericDialog(title);
		if (createImageRoi)
			gd.addChoice("Image:", titles, titles[index]);
		else {
			gd.addChoice("Image to add:", titles, titles[index]);
			gd.addNumericField("X location:", x, 0);
			gd.addNumericField("Y location:", y, 0);
		}
		gd.addNumericField("Opacity (0-100%):", opacity, 0);
		gd.addCheckbox("Zero transparent", zeroTransparent);
		gd.showDialog();
		if (gd.wasCanceled())
			return;
		index = gd.getNextChoiceIndex();
		if (!createImageRoi) {
			x = (int)gd.getNextNumber();
			y = (int)gd.getNextNumber();
		}
		opacity = (int)gd.getNextNumber();
		zeroTransparent = gd.getNextBoolean();
		ImagePlus overlay = WindowManager.getImage(wList[index]);
		if (wList.length==2) {
			ImagePlus i1 = WindowManager.getImage(wList[0]);
			ImagePlus i2 = WindowManager.getImage(wList[1]);
			if (i2.getWidth()<i1.getWidth() && i2.getHeight()<i1.getHeight()) {
				imp = i1;
				overlay = i2;
			}
		}
		if (overlay==imp) {
			IJ.error("Add Image...", "Image to be added cannot be the same as\n\""+imp.getTitle()+"\".");
			return;
		}
		if (overlay.getWidth()>imp.getWidth() && overlay.getHeight()>imp.getHeight()) {
			IJ.error("Add Image...", "Image to be added cannnot be larger than\n\""+imp.getTitle()+"\".");
			return;
		}
		if (createImageRoi && x==0 && y==0) {
			x = imp.getWidth()/2-overlay.getWidth()/2;
			y = imp.getHeight()/2-overlay.getHeight()/2;
		}	
		roi = new ImageRoi(x, y, overlay.getProcessor());
		roi.setName(overlay.getShortTitle());
		if (opacity!=100)
			((ImageRoi)roi).setOpacity(opacity/100.0);
		((ImageRoi)roi).setZeroTransparent(zeroTransparent);
		if (createImageRoi)
			imp.setRoi(roi);
		else {
			setPosition(imp, roi);
			Overlay overlayList = imp.getOverlay();
			if (overlayList==null)
				overlayList = new Overlay();
			overlayList.add(roi);
			imp.setOverlay(overlayList);
			Undo.setup(Undo.OVERLAY_ADDITION, imp);
		}
	}
	
	private void setPosition(ImagePlus imp, Roi roi) {
		int stackSize = imp.getStackSize();
		if (roi.hasHyperStackPosition() && imp.isHyperStack())
			return;
		if (roi.getPosition()>0 && stackSize>1)
			return;
		boolean setPos = defaultRoi.getPosition()!=0;
		if (setPos && stackSize>1) {
			if (imp.isHyperStack()||imp.isComposite()) {
				boolean compositeMode = imp.isComposite() && ((CompositeImage)imp).getMode()==IJ.COMPOSITE;
				int channel = !compositeMode||imp.getNChannels()==stackSize?imp.getChannel():0;
				if (imp.getNSlices()>1)
					roi.setPosition(channel, imp.getSlice(), 0);
				else if (imp.getNFrames()>1)
					roi.setPosition(channel, 0, imp.getFrame());
			} else
				roi.setPosition(imp.getCurrentSlice());
		}
	}

	void hide() {
		ImagePlus imp = IJ.getImage();
		imp.setHideOverlay(true);
		RoiManager rm = RoiManager.getInstance();
		if (rm!=null) rm.runCommand("show none");
	}

	void show() {
		ImagePlus imp = IJ.getImage();
		imp.setHideOverlay(false);
		if (imp.getOverlay()==null) {
			RoiManager rm = RoiManager.getInstance();
			if (rm!=null && rm.getCount()>1) {
				if (!IJ.isMacro()) rm.toFront();
				rm.runCommand("show all with labels");
			}
		}
	}

	void remove() {
		ImagePlus imp = WindowManager.getCurrentImage();
		if (imp!=null) {
			ImageCanvas ic = imp.getCanvas();
			if (ic!=null)
				ic.setShowAllList(null);
			imp.setOverlay(null);
		}
	}

	void flatten() {
		ImagePlus imp = IJ.getImage();
		Roi roi = imp.getRoi();
		if (imp.getStackSize()>1 && roi!=null && (roi instanceof PointRoi)) {
			ImagePlus imp2 = imp.flatten();
			imp2.setTitle(WindowManager.getUniqueName(imp.getTitle()));
			imp2.show();
			return;
		}
		Overlay overlay = imp.getOverlay();
		Overlay roiManagerOverlay = null;
		ImageCanvas ic = imp.getCanvas();
		if (ic!=null)
			roiManagerOverlay = ic.getShowAllList();
		if (imp.getBitDepth()==24 && overlay==null && imp.getRoi()==null && roiManagerOverlay==null && !imp.isComposite() && !IJ.macroRunning()) {
			IJ.error("Flatten", "Overlay or selection required to flatten RGB image");
			return;
		}
		int flags = IJ.setupDialog(imp, 0);
		if (flags==PlugInFilter.DONE)
			return;
		else if (flags==PlugInFilter.DOES_STACKS && !(imp.isComposite()&&overlay==null)) {
			//Added by Marcel Boeglin 2014.01.24
			if (overlay==null && roiManagerOverlay==null && !imp.isComposite()) {
				IJ.error("Flatten", "Overlay or multi-channel image required");
				return;
			}
			flattenStack(imp);
			if (Recorder.record)
				Recorder.recordCall("imp.flattenStack();");
		} else {
			ImagePlus imp2 = imp.flatten();
			imp2.setTitle(WindowManager.getUniqueName(imp.getTitle()));
			imp2.show();
			if (Recorder.record) // Added by Marcel Boeglin 2014.01.12
				Recorder.recordCall("imp = imp.flatten();");
		}
	}


	//Marcel Boeglin 2014.01.25
	void flattenStack(ImagePlus imp) {
		imp.flattenStack();
	}
	
	void fromRoiManager() {
		ImagePlus imp = IJ.getImage();
		RoiManager rm = RoiManager.getInstance2();
		if (rm==null) {
			IJ.error("ROI Manager is not open");
			return;
		}
		Roi[] rois = rm.getRoisAsArray();
		if (rois.length==0) {
			IJ.error("ROI Manager is empty");
			return;
		}
		rm.moveRoisToOverlay(imp);
		imp.deleteRoi();
	}

	void toRoiManager() {
		ImagePlus imp = IJ.getImage();
		Overlay overlay = imp.getOverlay();
		if (overlay==null) {
			IJ.error("Overlay required");
			return;
		}
		RoiManager rm = RoiManager.getInstance2();
		if (rm==null)
			rm = new RoiManager();
		if (overlay.size()>=4 && overlay.get(3).getPosition()!=0)
			Prefs.showAllSliceOnly = true;
		rm.setOverlay(overlay);
		imp.setOverlay(null);
	}
	
	void options() {
		ImagePlus imp = WindowManager.getCurrentImage();
		Overlay overlay = null;
		Roi roi = null;
		if (imp!=null) {
			overlay = imp.getOverlay();
			roi = imp.getRoi();
			if (roi!=null)
				roi = (Roi)roi.clone();
		}
		if (roi==null)
			roi = defaultRoi;
		if (roi==null) {
			int size = imp!=null?imp.getWidth():512;
			roi = new Roi(0, 0, size/4, size/4);
		}
		if (!roi.isDrawingTool()) {
			if (roi.getStroke()==null)
				roi.setStrokeWidth(defaultRoi.getStrokeWidth());
			if (roi.getStrokeColor()==null || Line.getWidth()>1&&defaultRoi.getStrokeColor()!=null)
				roi.setStrokeColor(defaultRoi.getStrokeColor());
			if (roi.getFillColor()==null)
				roi.setFillColor(defaultRoi.getFillColor());
		}
		boolean points = roi instanceof PointRoi && ((PolygonRoi)roi).getNCoordinates()>1;
		if (points) roi.setStrokeColor(Color.red);
		roi.setPosition(defaultRoi.getPosition());
		RoiProperties rp = new RoiProperties("Overlay Options", roi);
		if (!rp.showDialog()) return;
		defaultRoi = roi;
	}
	
	void list() {
		ImagePlus imp = IJ.getImage();
		Overlay overlay = imp.getOverlay();
		if (overlay!=null)
			listRois(overlay.toArray());
	}
	
	public static void listRois(Roi[] rois) {
		ImagePlus imp = WindowManager.getCurrentImage();
		ResultsTable rt = new ResultsTable();
		rt.showRowNumbers(true);
		for (int i=0; i<rois.length; i++) {
			if (rois[i]==null)
				continue;
			Rectangle r = rois[i].getBounds();
			String color = Colors.colorToString(rois[i].getStrokeColor());
			String fill = Colors.colorToString(rois[i].getFillColor());
			double strokeWidth = rois[i].getStrokeWidth();
			int digits = strokeWidth==(int)strokeWidth?0:1;
			String sWidth = IJ.d2s( strokeWidth,digits);
			String group = ""+rois[i].getGroup();
			if (group.equals("0"))
				group = "none";
			int position = rois[i].getPosition();
			int c = rois[i].getCPosition();
			int z = rois[i].getZPosition();
			int t = rois[i].getTPosition();
			if (imp!=null && imp.getNChannels()==1 && imp.getNSlices()==1 && imp.getNFrames()>1) {
				t = z;
				z = 0;
			}
			rt.setValue("Index", i, i);
			rt.setValue("Name", i, rois[i].getName());
			rt.setValue("Type", i, rois[i].getTypeAsString());
			rt.setValue("Group", i, group);
			if (rois[i] instanceof PointRoi) {
				Rectangle2D.Double bounds = rois[i].getFloatBounds();
				rt.setValue("X", i, (int)Math.round(bounds.x));
				rt.setValue("Y", i, (int)Math.round(bounds.y));
			} else if (rois[i] instanceof Arrow) {
				Polygon p = ((Arrow)rois[i]).getPoints();
				rt.setValue("X", i, p.xpoints[1]);
				rt.setValue("Y", i, p.ypoints[1]);
			} else {
				rt.setValue("X", i, r.x);
				rt.setValue("Y", i, r.y);
			}
			rt.setValue("Width", i, r.width);		
			rt.setValue("Height", i, r.height);		
			rt.setValue("Points", i, rois[i].size());
			rt.setValue("Color", i, color);		
			rt.setValue("Fill", i, fill);		
			rt.setValue("LWidth", i, sWidth);		
			rt.setValue("Pos", i, position);	
			rt.setValue("C", i, c);	
			rt.setValue("Z", i, z);	
			rt.setValue("T", i, t);	
		}
		String title = imp!=null?" of "+imp.getTitle():"";
		rt.show("Overlay Elements"+title);
	}
	
}
