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

/** This plugin implements the Analyze/Tools/Draw Scale Bar command.
    Divakar Ramachandran added options to draw a background 
    and use a serif font on 23 April 2006.
*/
public class ScaleBar implements PlugIn {

    static final String[] locations = {"Upper Right", "Lower Right", "Lower Left", "Upper Left", "At Selection"};
    static final int UPPER_RIGHT=0, LOWER_RIGHT=1, LOWER_LEFT=2, UPPER_LEFT=3, AT_SELECTION=4;
    static final String[] colors = {"White","Black","Light Gray","Gray","Dark Gray","Red","Green","Blue","Yellow"};
    static final String[] bcolors = {"None","Black","White","Dark Gray","Gray","Light Gray","Yellow","Blue","Green","Red"};
    static final String[] checkboxLabels = {"Bold Text", "Hide Text", "Serif Font", "Overlay"};
    final static String SCALE_BAR = "|SB|";
    static double barWidth;
    static int defaultBarHeight = 4;
    static int barHeightInPixels = defaultBarHeight;
    static String location = locations[LOWER_RIGHT];
    static String color = colors[0];
    static String bcolor = bcolors[0];
    static boolean boldText = true;
    static boolean hideText;
    static boolean createOverlay;
    static int defaultFontSize = 14;
    static int fontSize;
    static boolean labelAll;
    ImagePlus imp;
    double imageWidth;
    double mag;
    int xloc, yloc;
    int barWidthInPixels;
    int roiX=-1, roiY, roiWidth, roiHeight;
    boolean serifFont;
    boolean[] checkboxStates = new boolean[4];
    boolean showingOverlay, drawnScaleBar;

    public void run(String arg) {
        imp = WindowManager.getCurrentImage();
        if (imp!=null) {
            if (showDialog(imp) && imp.getStackSize()>1 && labelAll)
                labelSlices(imp);
        } else
            IJ.noImage();
     }

    void labelSlices(ImagePlus imp) {
        if (createOverlay) return;
        ImageStack stack = imp.getStack();
        String units = getUnits(imp);
        for (int i=1; i<=stack.getSize(); i++)
            drawScaleBar(stack.getProcessor(i), units);
        imp.setStack(stack);
    }

    boolean showDialog(ImagePlus imp) {
        Roi roi = imp.getRoi();
        if (roi!=null) {
            Rectangle r = roi.getBounds();
            roiX = r.x;
            roiY = r.y; 
            roiWidth = r.width;
            roiHeight = r.height;
            location = locations[AT_SELECTION];
        } else if (location.equals(locations[AT_SELECTION]))
            location = locations[UPPER_RIGHT];

        Calibration cal = imp.getCalibration();
        ImageWindow win = imp.getWindow();
        mag = (win!=null)?win.getCanvas().getMagnification():1.0;
        if (mag>1.0)
            mag = 1.0;
        if (fontSize<(defaultFontSize/mag))
            fontSize = (int)(defaultFontSize/mag);
        String units = cal.getUnits();
        // Handle Digital Micrograph unit microns
        if (units.equals("micron"))
            units = IJ.micronSymbol+"m";
        double pixelWidth = cal.pixelWidth;
        if (pixelWidth==0.0)
            pixelWidth = 1.0;
        double scale = 1.0/pixelWidth;
        imageWidth = imp.getWidth()*pixelWidth;
        if (roiX>0 && roiWidth>10)
            barWidth = roiWidth*pixelWidth;
        else if (barWidth==0.0 || barWidth>0.67*imageWidth) {
            barWidth = (80.0*pixelWidth)/mag;
            if (barWidth>0.67*imageWidth)
                barWidth = 0.67*imageWidth;
            if (barWidth>5.0)
                barWidth = (int)barWidth;
        }
        int stackSize = imp.getStackSize();
        int digits = (int)barWidth==barWidth?0:1;
        if (barWidth<1.0)
            digits = 2;
        int percent = (int)(barWidth*100.0/imageWidth);
        if (mag<1.0 && barHeightInPixels<defaultBarHeight/mag)
            barHeightInPixels = (int)(defaultBarHeight/mag);
        imp.getProcessor().snapshot();
        if (IJ.macroRunning())
            boldText = hideText = serifFont = createOverlay = false;
        else
            updateScalebar();
        GenericDialog gd = new BarDialog("Scale Bar");
        gd.addNumericField("Width in "+units+": ", barWidth, digits);
        gd.addNumericField("Height in pixels: ", barHeightInPixels, 0);
        gd.addNumericField("Font size: ", fontSize, 0);
        gd.addChoice("Color: ", colors, color);
        gd.addChoice("Background: ", bcolors, bcolor);
        gd.addChoice("Location: ", locations, location);
        checkboxStates[0] = boldText; checkboxStates[1] = hideText;
        checkboxStates[2] = serifFont; checkboxStates[3] = createOverlay;
        gd.setInsets(10, 25, 0);
        gd.addCheckboxGroup(2, 2, checkboxLabels, checkboxStates);
        if (stackSize>1) {
            gd.setInsets(0, 25, 0);
            gd.addCheckbox("Label all slices", labelAll);
        }
        gd.showDialog();
        if (gd.wasCanceled()) {
            imp.getProcessor().reset();
            imp.updateAndDraw();
            Overlay overlay = imp.getOverlay();
            if (showingOverlay && overlay!=null) {
                overlay.remove(SCALE_BAR);
                imp.draw();
            }
            return false;
        }
        barWidth = gd.getNextNumber();
        barHeightInPixels = (int)gd.getNextNumber();
        fontSize = (int)gd.getNextNumber();
        color = gd.getNextChoice();
        bcolor = gd.getNextChoice();
        location = gd.getNextChoice();
        boldText = gd.getNextBoolean();
        hideText = gd.getNextBoolean();
        serifFont = gd.getNextBoolean();
        createOverlay = gd.getNextBoolean();
        if (stackSize>1)
            labelAll = gd.getNextBoolean();
        if (IJ.macroRunning()) updateScalebar();
         return true;
    }

    void drawScaleBar(ImagePlus imp) {
          if (!updateLocation())
            return;
        Undo.setup(Undo.FILTER, imp);
        drawScaleBar(imp.getProcessor(), getUnits(imp));
        imp.updateAndDraw();
    }
    
    String getUnits(ImagePlus imp) {
        String units = imp.getCalibration().getUnits();
        if (units.equals("microns"))
            units = IJ.micronSymbol+"m";
        return units;
    }

    void createOverlay(ImagePlus imp) {
        Overlay overlay = imp.getOverlay();
        if (overlay==null)
            overlay = new Overlay();
        else
            overlay.remove(SCALE_BAR);
        Color color = getColor();
        Color bcolor = getBColor();
        int x = xloc;
        int y = yloc;
        int fontType = boldText?Font.BOLD:Font.PLAIN;
        String face = serifFont?"Serif":"SanSerif";
        Font font = new Font(face, fontType, fontSize);
        String label = getLength(barWidth) + " "+ getUnits(imp);
        ImageProcessor ip = imp.getProcessor();
        ip.setFont(font);
        int swidth = hideText?0:ip.getStringWidth(label);
        int xoffset = (barWidthInPixels - swidth)/2;
        int yoffset =  barHeightInPixels + (hideText?0:fontSize+fontSize/4);
        if (bcolor!=null) {
            int w = barWidthInPixels;
            int h = yoffset;
            if (w<swidth) w = swidth;
            int x2 = x;
            if (x+xoffset<x2) x2 = x + xoffset;
            int margin = w/20;
            if (margin<2) margin = 2;
            x2 -= margin;
            int y2 = y - margin;
            w = w+ margin*2;
            h = h+ margin*2;
            Roi background = new Roi(x2, y2, w, h);
            background.setFillColor(bcolor);
            overlay.add(background, SCALE_BAR);
        }
        Roi bar = new Roi(x, y, barWidthInPixels, barHeightInPixels);
        bar.setFillColor(color);
        overlay.add(bar, SCALE_BAR);
        if (!hideText) {
            TextRoi text = new TextRoi(x+xoffset, y+barHeightInPixels, label, font);
            text.setStrokeColor(color);
            overlay.add(text, SCALE_BAR);
        }
        imp.setOverlay(overlay);
        showingOverlay = true;
    }
    
    void drawScaleBar(ImageProcessor ip, String units) {
        Color color = getColor();
        Color bcolor = getBColor();
        int x = xloc;
        int y = yloc;
        int fontType = boldText?Font.BOLD:Font.PLAIN;
        String font = serifFont?"Serif":"SanSerif";
        ip.setFont(new Font(font, fontType, fontSize));
        ip.setAntialiasedText(true);
        String label = getLength(barWidth) + " "+ units;
        int swidth = hideText?0:ip.getStringWidth(label);
        int xoffset = (barWidthInPixels - swidth)/2;
        int yoffset =  barHeightInPixels + (hideText?0:fontSize+fontSize/(serifFont?8:4));

        // Draw bkgnd box first,  based on bar width and height (and font size if hideText is not checked)
        if (bcolor!=null) {
            int w = barWidthInPixels;
            int h = yoffset;
            if (w<swidth) w = swidth;
            int x2 = x;
            if (x+xoffset<x2) x2 = x + xoffset;
            int margin = w/20;
            if (margin<2) margin = 2;
            x2 -= margin;
            int y2 = y - margin;
            w = w+ margin*2;
            h = h+ margin*2;
            ip.setColor(bcolor);
            ip.setRoi(x2, y2, w, h);
            ip.fill();
        }
        
        ip.resetRoi();
        ip.setColor(color);
        ip.setRoi(x, y, barWidthInPixels, barHeightInPixels);
        ip.fill();
        ip.resetRoi();
        if (!hideText)
            ip.drawString(label, x+xoffset, y+yoffset);
        drawnScaleBar = true;
    }

    String getLength(double barWidth) {
        int digits = (int)barWidth==barWidth?0:1;
        if (barWidth<1.0) digits=1;
        if (digits==1) {
            String s = IJ.d2s(barWidth/0.1, 2);
            if (!s.endsWith(".00")) digits = 2;
        }
        return IJ.d2s(barWidth, digits);
    }

    boolean updateLocation() {
        Calibration cal = imp.getCalibration();
        barWidthInPixels = (int)(barWidth/cal.pixelWidth);
        int width = imp.getWidth();
        int height = imp.getHeight();
        int fraction = 20;
        int fontType = boldText?Font.BOLD:Font.PLAIN;
        String font = serifFont?"Serif":"SanSerif";
        ImageProcessor ip = imp.getProcessor();
        ip.setFont(new Font(font, fontType, fontSize));
        ip.setAntialiasedText(true);
        String label = getLength(barWidth)+" "+getUnits(imp);
        int swidth = hideText?0:ip.getStringWidth(label);
        int labelWidth = (swidth < barWidthInPixels)?0:(int) (barWidthInPixels-swidth)/2;
        int x = 0;
        int y = 0;
        if (location.equals(locations[UPPER_RIGHT])) {
            x = width - width/fraction - barWidthInPixels + labelWidth;
            y = height/fraction;
        } else if (location.equals(locations[LOWER_RIGHT])) {
            x = width - width/fraction - barWidthInPixels + labelWidth;
            y = height - height/fraction - barHeightInPixels - fontSize;
        } else if (location.equals(locations[UPPER_LEFT])) {
            x = width/fraction - labelWidth;
            y = height/fraction;
        } else if (location.equals(locations[LOWER_LEFT])) {
            x = width/fraction - labelWidth;
            y = height - height/fraction - barHeightInPixels - fontSize;
        } else {
            if (roiX==-1)
                return false;
            x = roiX;
            y = roiY;
        }
        xloc = x;
        yloc = y;
        return true;
    }

    Color getColor() {
        Color c = Color.black;
        if (color.equals(colors[0])) c = Color.white;
        else if (color.equals(colors[2])) c = Color.lightGray;
        else if (color.equals(colors[3])) c = Color.gray;
        else if (color.equals(colors[4])) c = Color.darkGray;
        else if (color.equals(colors[5])) c = Color.red;
        else if (color.equals(colors[6])) c = Color.green;
        else if (color.equals(colors[7])) c = Color.blue;
        else if (color.equals(colors[8])) c = Color.yellow;
       return c;
    }

    // Div., mimic getColor to write getBColor for bkgnd    
    Color getBColor() {
        if (bcolor==null || bcolor.equals(bcolors[0])) return null;
        Color bc = Color.white;
        if (bcolor.equals(bcolors[1])) bc = Color.black;
        else if (bcolor.equals(bcolors[3])) bc = Color.darkGray;
        else if (bcolor.equals(bcolors[4])) bc = Color.gray;
        else if (bcolor.equals(bcolors[5])) bc = Color.lightGray;
        else if (bcolor.equals(bcolors[6])) bc = Color.yellow;
        else if (bcolor.equals(bcolors[7])) bc = Color.blue;
        else if (bcolor.equals(bcolors[8])) bc = Color.green;
        else if (bcolor.equals(bcolors[9])) bc = Color.red;
        return bc;
    }

    void updateScalebar() {
        updateLocation();
        imp.getProcessor().reset();
        if (createOverlay) {
            if (drawnScaleBar) {
                imp.updateAndDraw();
                drawnScaleBar = false;
            }
            createOverlay(imp);
        } else {
            if (showingOverlay) {
                imp.setOverlay(null);
                showingOverlay = false;
            }
            drawScaleBar(imp);
        }
    }

   class BarDialog extends GenericDialog {

        BarDialog(String title) {
            super(title);
        }

        public void textValueChanged(TextEvent e) {
            TextField widthField = ((TextField)numberField.elementAt(0));
            Double d = getValue(widthField.getText());
            if (d==null)
                return;
            barWidth = d.doubleValue();
            TextField heightField = ((TextField)numberField.elementAt(1));
            d = getValue(heightField.getText());
            if (d==null)
                return;
            barHeightInPixels = (int)d.doubleValue();
            TextField fontSizeField = ((TextField)numberField.elementAt(2));
            d = getValue(fontSizeField.getText());
            if (d==null)
                return;
            int size = (int)d.doubleValue();
            if (size>5)
                fontSize = size;
            updateScalebar();
        }

        public void itemStateChanged(ItemEvent e) {
            Choice col = (Choice)(choice.elementAt(0));
            color = col.getSelectedItem();
            Choice bcol = (Choice)(choice.elementAt(1));
            bcolor = bcol.getSelectedItem();
            Choice loc = (Choice)(choice.elementAt(2));
            location = loc.getSelectedItem();
            boldText = ((Checkbox)(checkbox.elementAt(0))).getState();
            hideText = ((Checkbox)(checkbox.elementAt(1))).getState();
            serifFont = ((Checkbox)(checkbox.elementAt(2))).getState();
            createOverlay = ((Checkbox)(checkbox.elementAt(3))).getState();
            updateScalebar();
        }

   } //BarDialog inner class

} //ScaleBar class