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;
    }
}