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

/** This plugin implements the Edit/Selection/Straighten command. */
public class Straightener implements PlugIn {

    public void run(String arg) {
        ImagePlus imp = IJ.getImage();
        Roi roi = imp.getRoi();
        if (roi==null || !roi.isLine()) {
            IJ.error("Straightener", "Line selection required");
            return;
        }
        int width = Line.getWidth();
        int originalWidth = width;
        boolean isMacro = IJ.macroRunning();
        if (width==1 || isMacro) {
            GenericDialog gd = new GenericDialog("Straightener");
            gd.addNumericField("Line Width:", 20, 0, 3, "pixels");
            gd.showDialog();
            if (gd.wasCanceled()) return;
            width = (int)gd.getNextNumber();
            Line.setWidth(width);
        }
        roi = (Roi)imp.getRoi().clone();
        int type = roi.getType();
        if (type==Roi.FREELINE)
            IJ.run(imp, "Fit Spline", "");
        ImageProcessor ip2;
        if (imp.getBitDepth()==24)
            ip2 = straightenRGB(imp, width);
        else if (imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE)
            ip2 = straightenComposite(imp, width);
        else if (roi.getType()==Roi.LINE)
            ip2 = straightenStraightLine(imp, width);
        else
            ip2 = straighten(imp, width);
        (new ImagePlus(WindowManager.getUniqueName(imp.getTitle()), ip2)).show();
        imp.setRoi(roi);
        if (type==Roi.POLYLINE&& !((PolygonRoi)roi).isSplineFit())
            ((PolygonRoi)roi).fitSpline();
        if (isMacro) Line.setWidth(originalWidth);
    }
    
    public ImageProcessor straighten(ImagePlus imp, int width) {
        PolygonRoi roi = (PolygonRoi)imp.getRoi();
        boolean isSpline = roi.isSplineFit();
        int type = roi.getType();
        roi.fitSplineForStraightening();
        FloatPolygon p = roi.getFloatPolygon();
        int n = p.npoints;
        ImageProcessor ip = imp.getProcessor();
        ImageProcessor ip2 = new FloatProcessor(n, width);
        ImageProcessor distances = null;
        if (IJ.debugMode)  distances = new FloatProcessor(n, 1);
        float[] pixels = (float[])ip2.getPixels();
        float x1, y1;
        float x2=p.xpoints[0]-(p.xpoints[1]-p.xpoints[0]);
        float y2=p.ypoints[0]-(p.ypoints[1]-p.ypoints[0]);
        if (width==1)
            ip2.putPixelValue(0, 0, ip.getInterpolatedValue(x2, y2));
        for (int i=0; i<n; i++) {
            x1=x2; y1=y2;
            x2=p.xpoints[i]; y2=p.ypoints[i];
            if (distances!=null) distances.putPixelValue(i, 0, (float)Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)));
            if (width==1) {
                ip2.putPixelValue(i, 0, ip.getInterpolatedValue(x2, y2));
                continue;
            }
            float dx = x2-x1;
            float dy = y1-y2;
            //IJ.log(i+"  "+x2+"  "+dy+"  "+(dy*width/2f)+"   "+y2+"  "+dx+"   "+(dx*width/2f));
            float x = x2-dy*width/2f;
            float y = y2-dx*width/2f;
            int j = 0;
            int n2 = width;
            do {
                ip2.putPixelValue(i, j++, ip.getInterpolatedValue(x, y));;
                //ip.drawDot((int)x, (int)y);
                x += dy;
                y += dx;
            } while (--n2>0);
        }
        imp.updateAndDraw();
        if (!isSpline) {
            if (type==Roi.FREELINE)
                roi.removeSplineFit();
            else
                imp.draw();
        }
        if (imp.getBitDepth()!=24) {
            ip2.setColorModel(ip.getColorModel());
            ip2.resetMinAndMax();
        }
        if (distances!=null) {
            distances.resetMinAndMax();
            (new ImagePlus("Distances", distances)).show();
        }
        return ip2;
    }
    
    public ImageProcessor straightenStraightLine(ImagePlus imp, int width) {
        Line.setWidth(1);
        Polygon p = imp.getRoi().getPolygon();
        Line.setWidth(width);
        imp.setRoi(new PolygonRoi(p.xpoints, p.ypoints, 2, Roi.POLYLINE));
        ImageProcessor ip2 = straighten(imp, width);
        imp.setRoi(new Line(p.xpoints[0], p.ypoints[0], p.xpoints[1], p.ypoints[1]));
        return ip2;
    }
    
    ImageProcessor straightenRGB(ImagePlus imp, int width) {
        int w=imp.getWidth(), h=imp.getHeight();
        int size = w*h;
        byte[] r = new byte[size];
        byte[] g = new byte[size];
        byte[] b = new byte[size];
        ColorProcessor cp = (ColorProcessor)imp.getProcessor();
        cp.getRGB(r, g, b);
        ImagePlus imp2 = new ImagePlus("red", new ByteProcessor(w, h, r, null));
        imp2.setRoi((Roi)imp.getRoi().clone());
        ImageProcessor red = straighten(imp2, width);
        imp2 = new ImagePlus("green", new ByteProcessor(w, h, g, null));
        imp2.setRoi((Roi)imp.getRoi().clone());
        ImageProcessor green = straighten(imp2, width);
        imp2 = new ImagePlus("blue", new ByteProcessor(w, h, b, null));
        imp2.setRoi((Roi)imp.getRoi().clone());
        ImageProcessor blue = straighten(imp2, width);
        ColorProcessor cp2 = new ColorProcessor(red.getWidth(), red.getHeight());
        red = red.convertToByte(false);
        green = green.convertToByte(false);
        blue = blue.convertToByte(false);
        cp2.setRGB((byte[])red.getPixels(), (byte[])green.getPixels(), (byte[])blue.getPixels());
        imp.setRoi(imp2.getRoi());
        return cp2;
    }
    
    ImageProcessor straightenComposite(ImagePlus imp, int width) {
        Image img = imp.getImage();
        ImagePlus imp2 = new ImagePlus("temp", new ColorProcessor(img));
        imp2.setRoi(imp.getRoi());
        ImageProcessor ip2 = straightenRGB(imp2, width);
        imp.setRoi(imp2.getRoi());
        return ip2;
    }

}