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

/** Implements the commands in the Process/Binary submenu. */
public class Binary implements PlugInFilter {
	
	String arg;
	ImagePlus imp;
	static int iterations = 1;
	static int count = 1;
	int foreground, background;

	public int setup(String arg, ImagePlus imp) {
		this.arg = arg;
		this.imp = imp;
		IJ.register(Binary.class);
		
		if (arg.equals("options")) {
			showDialog();
			return DONE;
		}
		
		if (imp!=null && (imp.getType()==ImagePlus.GRAY8 || imp.getType()==ImagePlus.COLOR_256)) {
			ImageStatistics stats = imp.getStatistics();
			if (stats.histogram[0]+stats.histogram[255]!=stats.pixelCount) {
				IJ.error("8-bit binary (black and white only) image required.");
				return DONE;
			}
		}
		return IJ.setupDialog(imp, DOES_8G+DOES_8C+SUPPORTS_MASKING);
	}

	public void run(ImageProcessor ip) {
		foreground = Prefs.blackBackground?255:0;
		if (ip.isInvertedLut())
			foreground = 255 - foreground;
        background = 255 - foreground;
		ip.setSnapshotCopyMode(true);
		if (arg.equals("erode")) erode(ip);
		else if (arg.equals("dilate")) dilate(ip);
		else if (arg.equals("open")) open(ip);
		else if (arg.equals("close")) close(ip);
		else if (arg.equals("outline")) outline(ip);
		else if (arg.equals("skel")) skeletonize(ip);
		ip.setSnapshotCopyMode(false);
	}

		
	void erode(ImageProcessor ip) {
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).erode(count, background);
	}
	
	void dilate(ImageProcessor ip) {
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).dilate(count, background);
	}
	

	void open(ImageProcessor ip) {
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).erode(count, background);
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).dilate(count, background);
	}
	
	void close(ImageProcessor ip) {
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).dilate(count, background);
		for (int i=0; i<iterations; i++)
			((ByteProcessor)ip).erode(count, background);
	}
	
	void outline(ImageProcessor ip) {
		if (Prefs.blackBackground) ip.invert();
		((ByteProcessor)ip).outline();
		if (Prefs.blackBackground) ip.invert();
	}

	void skeletonize(ImageProcessor ip) {
		if (Prefs.blackBackground) ip.invert();
		boolean edgePixels = hasEdgePixels(ip);
		ImageProcessor ip2 = expand(ip, edgePixels);
		((ByteProcessor)ip2).skeletonize();
		ip = shrink(ip, ip2, edgePixels);
		if (Prefs.blackBackground) ip.invert();
	}
		
	void showDialog() {
		GenericDialog gd = new GenericDialog("Binary Options");
		gd.addNumericField("Iterations (1-25):", iterations, 0, 3, "");
		gd.addNumericField("Count (1-8):", count, 0, 3, "");
		gd.addCheckbox("Black Background", Prefs.blackBackground);
		gd.showDialog();
		if (gd.wasCanceled()) return;
		int n = (int)gd.getNextNumber();
		Prefs.blackBackground = gd.getNextBoolean();
		if (n>25) n = 25;
		if (n<1) n = 1;
		iterations = n;
		count = (int)gd.getNextNumber();
        if (count<1) count = 1;
        if (count>8) count = 8;
	}
	
	boolean hasEdgePixels(ImageProcessor ip) {
		int width = ip.getWidth();
		int height = ip.getHeight();
		boolean edgePixels = false;
		for (int x=0; x<width; x++) { // top edge
			if (ip.getPixel(x, 0)==foreground)
				edgePixels = true;
		}
		for (int x=0; x<width; x++) { // bottom edge
			if (ip.getPixel(x, height-1)==foreground)
				edgePixels = true;
		}
		for (int y=0; y<height; y++) { // left edge
			if (ip.getPixel(0, y)==foreground)
				edgePixels = true;
		}
		for (int y=0; y<height; y++) { // right edge
			if (ip.getPixel(height-1, y)==foreground)
				edgePixels = true;
		}
		return edgePixels;
	}
	
	ImageProcessor expand(ImageProcessor ip, boolean hasEdgePixels) {
		if (hasEdgePixels) {
			ImageProcessor ip2 = ip.createProcessor(ip.getWidth()+2, ip.getHeight()+2);
			if (foreground==0) {
				ip2.setColor(255);
				ip2.fill();
			}
			ip2.insert(ip, 1, 1);
            //new ImagePlus("ip2", ip2).show();
			return ip2;
		} else
			return ip;
	}

	ImageProcessor shrink(ImageProcessor ip, ImageProcessor ip2, boolean hasEdgePixels) {
		if (hasEdgePixels) {
			int width = ip.getWidth();
			int height = ip.getHeight();
			for (int y=0; y<height; y++)
				for (int x=0; x<width; x++)
					ip.putPixel(x, y, ip2.getPixel(x+1, y+1));
		}
		return ip;
	}
}
