package ij.plugin.filter;
import ij.*;
import ij.gui.*;
import ij.process.*;
import java.awt.*;
import java.awt.geom.*;


/** This plugin implements the Image/Rotate/Arbitrarily command. */
public class Rotator implements ExtendedPlugInFilter, DialogListener {
	private int flags = DOES_ALL|SUPPORTS_MASKING|PARALLELIZE_STACKS;
	private static double angle = 15.0;
	private static boolean fillWithBackground;
	private static boolean enlarge;
	private static int gridLines = 1;
	private ImagePlus imp;
	private int bitDepth;
	private boolean canEnlarge;
	private boolean isEnlarged;
	private GenericDialog gd;
	private PlugInFilterRunner pfr;
	private String[] methods = ImageProcessor.getInterpolationMethods();
	private static int interpolationMethod = ImageProcessor.BILINEAR;

	public int setup(String arg, ImagePlus imp) {
		this.imp = imp;
		if (imp!=null) {
			bitDepth = imp.getBitDepth();
			Roi roi = imp.getRoi();
			Rectangle r = roi!=null?roi.getBounds():null;
			canEnlarge = r==null || (r.x==0&&r.y==0&&r.width==imp.getWidth()&&r.height==imp.getHeight());
		}
		return flags;
	}

	public void run(ImageProcessor ip) {
		if(enlarge && gd.wasOKed()) synchronized(this) {
			if (!isEnlarged) {
				enlargeCanvas();
				isEnlarged=true;
			}
		}
		if (isEnlarged) {	//enlarging may have made the ImageProcessor invalid, also for the parallel threads
			int slice = pfr.getSliceNumber();
			if (imp.getStackSize()==1)
				ip = imp.getProcessor();
			else
				ip = imp.getStack().getProcessor(slice);
		}
		ip.setInterpolationMethod(interpolationMethod);
		if (fillWithBackground) {
			Color bgc = Toolbar.getBackgroundColor();
			if (bitDepth==8)
				ip.setBackgroundValue(ip.getBestIndex(bgc));
			else if (bitDepth==24)
				ip.setBackgroundValue(bgc.getRGB());
		} else
			ip.setBackgroundValue(0);
		ip.rotate(angle);
		if (!gd.wasOKed())
			drawGridLines(gridLines);
		if (isEnlarged && imp.getStackSize()==1) {
			imp.changes = true;
			imp.updateAndDraw();
			Undo.setup(Undo.COMPOUND_FILTER_DONE, imp);
		}
	}

	void enlargeCanvas() {
		imp.unlock();
		if (imp.getStackSize()==1)
			Undo.setup(Undo.COMPOUND_FILTER, imp);
		IJ.run("Select All");
		IJ.run("Rotate...", "angle="+angle);
		Roi roi = imp.getRoi();
		Rectangle r = roi.getBounds();
		if (r.width<imp.getWidth()) r.width = imp.getWidth();
		if (r.height<imp.getHeight()) r.height = imp.getHeight();
		IJ.showStatus("Rotate: Enlarging...");
		IJ.run("Canvas Size...", "width="+r.width+" height="+r.height+" position=Center "+(fillWithBackground?"":"zero"));
		IJ.showStatus("Rotating...");
	}

	void drawGridLines(int lines) {
		ImageCanvas ic = imp.getCanvas();
		if (ic==null) return;
		if (lines==0) {ic.setDisplayList(null); return;}
		GeneralPath path = new GeneralPath();
		float width = imp.getWidth();
		float height = imp.getHeight();
		float xinc = width/lines;
		float yinc = height/lines;
		float xstart = xinc/2f;
		float ystart = yinc/2f;
		for (int i=0; i<lines; i++) {
			path.moveTo(xstart+xinc*i, 0f);
			path.lineTo(xstart+xinc*i, height);
			path.moveTo(0f, ystart+yinc*i);
			path.lineTo(width, ystart+yinc*i);
		}
		ic.setDisplayList(path, null, null);
	}
	
	public int showDialog(ImagePlus imp, String command, PlugInFilterRunner pfr) {
		this.pfr = pfr;
		String macroOptions = Macro.getOptions();
		if (macroOptions!=null) {
			if (macroOptions.indexOf(" interpolate")!=-1)
				macroOptions.replaceAll(" interpolate", " interpolation=Bilinear");
			else if (macroOptions.indexOf(" interpolation=")==-1)
				macroOptions = macroOptions+" interpolation=None";
			Macro.setOptions(macroOptions);
		}
		gd = new GenericDialog("Rotate", IJ.getInstance());
		gd.addNumericField("Angle (degrees):", angle, (int)angle==angle?1:2);
		gd.addNumericField("Grid Lines:", gridLines, 0);
		gd.addChoice("Interpolation:", methods, methods[interpolationMethod]);
		if (bitDepth==8 || bitDepth==24)
			gd.addCheckbox("Fill with Background Color", fillWithBackground);
		if (canEnlarge)
			gd.addCheckbox("Enlarge Image to Fit Result", enlarge);
		else
			enlarge = false;
		gd.addPreviewCheckbox(pfr);
		gd.addDialogListener(this);
		gd.showDialog();
		drawGridLines(0);
		if (gd.wasCanceled())
			return DONE;
		if (!enlarge)
			flags |= KEEP_PREVIEW;		// standard filter without enlarge
		else if (imp.getStackSize()==1)
			flags |= NO_CHANGES;			// undoable as a "compound filter"
		return IJ.setupDialog(imp, flags);
	}
	
	public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
		angle = gd.getNextNumber();
		//only check for invalid input to "angle", don't care about gridLines
		if (gd.invalidNumber()) {
			if (gd.wasOKed()) IJ.error("Angle is invalid.");
			return false;
		}
		gridLines = (int)gd.getNextNumber();
		interpolationMethod = gd.getNextChoiceIndex();
		if (bitDepth==8 || bitDepth==24)
			fillWithBackground = gd.getNextBoolean();
		if (canEnlarge)
			enlarge = gd.getNextBoolean();
		return true;
	}

	public void setNPasses(int nPasses) {
	}

}

