package ij.plugin;

import java.awt.*;
import java.awt.image.*;
import java.util.Vector;
import java.io.*;
import ij.*;
import ij.process.*;
import ij.io.*;
import ij.gui.*;
import ij.measure.ResultsTable;


/** Writes the XY coordinates and pixel values of all non-background 
    pixels to a tab-delimited text file. Backround is assumed to be the 
    value of the pixel in the upper left corner of the image. */
public class XYCoordinates implements PlugIn {

    private static boolean processStack;
    private static boolean invertY;
    private static boolean suppress;

    public void run(String arg) {
        ImagePlus imp = IJ.getImage();
        Roi roi = imp.getRoi();
        String options = Macro.getOptions();
        boolean legacyMacro = IJ.isMacro() && options!=null && options.contains("background=");
        if (roi!=null && roi.isArea() && !legacyMacro) {
            saveSelectionCoordinates(imp);
            return;
        }
        ImageProcessor ip = imp.getProcessor();
        int width = imp.getWidth();
        int height = imp.getHeight();
        double background = ip.getPixelValue(0,0);
        String bg = " \n";
        boolean rgb = imp.getBitDepth()==24;
        if (rgb) {
            int c = ip.getPixel(0,0);
            int r = (c&0xff0000)>>16;
            int g = (c&0xff00)>>8;
            int b = c&0xff;
            bg = r+","+g+","+b;
            bg = " \n    Background value: " + bg + "\n";
        }
        imp.deleteRoi();
        
        int slices = imp.getStackSize();
        String msg =
            "This plugin writes to a text file the XY coordinates and\n"
            + "pixel value of all non-background pixels. Backround\n"
            + "defaults to be the value of the pixel in the upper\n"
            + "left corner of the image.\n \n"
            + "If there is a selection, this dialog is skipped and the\n"
            + "coordinates and values of pixels in the selection are saved.\n";
                
        GenericDialog gd = new GenericDialog("Save XY Coordinates");
        gd.setInsets(0, 20, 0);
        gd.addMessage(msg, null, Color.darkGray);
        int digits = (int)background==background?0:4;
        if (!rgb) {
            gd.setInsets(5, 35, 3);
            gd.addNumericField("Background value:", background, digits);
        }
        gd.setInsets(10, 35, 0);
        gd.addCheckbox("Invert y coordinates off (0 at top of image)", invertY);
        gd.setInsets(0, 35, 0);
        gd.addCheckbox("Suppress Log output", suppress);
        if (slices>1) {
            gd.setInsets(0, 35, 0);
            gd.addCheckbox("Process all "+slices+" images", processStack);
        }
        gd.showDialog();
        if (gd.wasCanceled())
            return;
        if (!rgb)
            background = gd.getNextNumber();
        invertY = gd.getNextBoolean();
        suppress = gd.getNextBoolean();
        if (slices>1)
            processStack = gd.getNextBoolean();
        else
            processStack = false;
        if (!processStack) slices = 1;

        SaveDialog sd = new SaveDialog("Save Coordinates as Text...", imp.getTitle(), ".txt");
        String name = sd.getFileName();
        if (name == null)
            return;
        String directory = sd.getDirectory();
        PrintWriter pw = null;
        try {
            FileOutputStream fos = new FileOutputStream(directory+name);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            pw = new PrintWriter(bos);
        }
        catch (IOException e) {
            IJ.error("Save XY Coordinates", "Error saving coordinates:\n   "+e.getMessage());
            return;
        }

        IJ.showStatus("Saving coordinates...");
        int count = 0;
        float v;
        int c,r,g,b;
        int type = imp.getType();
        ImageStack stack = imp.getStack();
        boolean nanBackground = Double.isNaN(background);
        for (int z=0; z<slices; z++) {
            if (slices>1) ip = stack.getProcessor(z+1);
            String zstr = slices>1?z+"\t":"";
            for (int i=0; i<height; i++) {
                int y = invertY?i:height-1-i;
                for (int x=0; x<width; x++) {
                    v = ip.getPixelValue(x,y);
                    if (v!=background && !(nanBackground&&Double.isNaN(v))) {
                        if (type==ImagePlus.GRAY32)
                            pw.println(x+"\t"+(invertY?y:height-1-y)+"\t"+zstr+v);
                        else if (rgb) {
                            c = ip.getPixel(x,y);
                            r = (c&0xff0000)>>16;
                            g = (c&0xff00)>>8;
                            b = c&0xff;
                            pw.println(x+"\t"+(invertY?y:height-1-y)+"\t"+zstr+r+"\t"+g+"\t"+b);
                        } else
                            pw.println(x+"\t"+(invertY?y:height-1-y)+"\t"+zstr+(int)v);
                        count++;
                    }
                } // x
                if (slices==1&&y%10==0) IJ.showProgress((double)(height-y)/height);
            } // y
            if (slices>1) IJ.showProgress(z+1, slices);
            String img = slices>1?"-"+(z+1):"";
            if (!suppress)
                IJ.log(imp.getTitle() + img+": " + count + " pixels (" + IJ.d2s(count*100.0/(width*height)) + "%)\n");
            count = 0;
        } // z
        IJ.showProgress(1.0);
        IJ.showStatus("");
        pw.close();
    }
    
    private void saveSelectionCoordinates(ImagePlus imp) {
        SaveDialog sd = new SaveDialog("Save Coordinates as Text...", imp.getTitle(), ".csv");
        String name = sd.getFileName();
        if (name == null)
            return;
        String dir = sd.getDirectory();
        Roi roi = imp.getRoi();
        ImageProcessor ip = imp.getProcessor();
        ImageProcessor mask = roi.getMask();
        Rectangle r = roi.getBounds();
        ResultsTable rt = new ResultsTable();
        boolean rgb = imp.getBitDepth()==24;
        for (int y=0; y<r.height; y++) {
            for (int x=0; x<r.width; x++) {
                if (mask==null || mask.getPixel(x,y)!=0) {
                    rt.incrementCounter();
                    rt.addValue("X", r.x+x);
                    rt.addValue("Y", r.y+y);
                    if (rgb) {
                        int c = ip.getPixel(r.x+x,r.y+y);
                        rt.addValue("Red", (c&0xff0000)>>16);
                        rt.addValue("Green", (c&0xff00)>>8);
                        rt.addValue("Blue", c&0xff);
                    } else
                        rt.addValue("Value", ip.getPixelValue(r.x+x,r.y+y));
                }
            }
        }
        rt.save(dir+name);
    }

}