package vnt;
/**
 * Create_Settings.java - v1.0
 * Began: March 27, 2006
 * Last Updated: March 27, 2006 
 * 
 * Copyright (C) 2006 - Michael D. Miller - mdm162@truman.edu
 * Truman State University
 * 100 E. Normal
 * Kirksville, MO - 63501
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

import ij.*;
import ij.gui.*;
import ij.process.*;
import ij.measure.*;
import ij.util.Tools;
import java.awt.*;
import java.awt.event.*;
import java.awt.Color.*;
import ij.plugin.filter.*;
import ij.plugin.filter.PlugInFilter;
import java.io.*;
import ij.io.*;

import java.util.*;
import java.text.*;
import java.lang.String.*;
import java.lang.Math.*;

/**
 * <p>This plugin presents a dialog which creates/edits a settings.txt file.
 * The settings file will be used by the other VNT plugins.
 * The basis for this code was taken directly from ImageJ's ScaleDialog.java.</p> 
 * 
 * @author Michael Miller - Truman State University
 * @version 1.0
 * @since 1.0
 */
public class Create_Settings extends VascularNetworkToolkit implements PlugInFilter {

    /**
     * The Primary version number for this plugin. Used in the settings.txt generation.
     */
    private int versionMain = 0;
    /**
     * The Sub version number for this plugin. Used in the settings.txt generation.
     */
    private int versionSub = 1;
    
    /**
    * Specifies the preconditions for the plug-in.
    * If this method succeeds then run() is called.
    *
    * <p>Pre: ImageJ is running and an 8-bit grayscale image is open. The plug-in was just activated.
    * <br />Post: Either an argument was processed, the image was not saved to a local folder, or the plug-in is cleared to run on the image.</p>
    * @param arg Required by the interface. The argument list passed to the plug-in.
    * @param imp Required by the interface. The ImagePlus datastructure holding (among other things) information to grab path and filename.
    * @return If DONE is returned, ImageJ quits without run()'ing the plug-in. Otherwise, the plug-in signals to ImageJ that this plugin only handles 8-bit (256 grayscale) and will not change the original image.
    * @see #run
    */
    public int setup(String arg, ImagePlus ip) {
        if (arg.equals("about")) { 
            showAbout("Create Settings","  * Creates a settings.txt file in the folder of the active image. The file is used by other VNT plugins. (Copyright 2006. Michael Miller mdm162@truman.edu)");
            return DONE; // exit without run()
        }

        boolean processImage = getFileInformation(ip);
        if (!processImage) {
            IJ.error("Must have a file path. Please save this image to a local folder.");
            return DONE; // exit without run()
        }
                    
        return DOES_ALL+NO_CHANGES; // success, run()
    }

    /**
     * This is called if the setup() was successful. 
     *  
     * <p>Pre: The image was cleared to run by the setup() method.
     * <br />Post: Something operation is performed. 
     * @param bp Required by the interface. The access information to the original image.
     * @see #setup
     */
    public void run(ImageProcessor bp){
        // store our values for our dialogue
        double measured = distanceInPixels;
        double known = distanceInUnits;
        double aspectRatio = imageAspectRatio;
        int lightingCutoff = lightingCorrectionCutoff;
        int lightingOrder = lightingCorrectionPolynomialOrder;
        int maxMeshArea = maximumAreaToIgnoreMesh;
        String unit = distanceUnitName;
        boolean polynomialLightingCorrection = performPolynomialLightingCorrection;
        boolean lightingCorrectionEyeCandy = generateLightingCorrectionSurfaceEyeCandy;
        boolean eyeCandy = performEyeCandy;
        boolean originalImage = generateEyeCandyWithOriginalImage;
        boolean highlowSegmentation = useHighLowSegmentation; 
        boolean tempImageFiles = keepTemporaryImageFiles;
        boolean graphTextFiles = keepGraphTextFiles;
        boolean debugText = displayDebugText;
        boolean animation = animatedDisplay;
        boolean advancedSettings = false;
        String scale = "<no scale>";
        int digits = 2;
        if (known > 0) {
            scale = IJ.d2s(measured/known,digits)+" pixels/"+unit;
        }

        if (imageCalibration != null) {
            // is this calibration non-standard?
            if (imageCalibration.scaled()) {
                // load up initial information for the dialog
                // Note that this could either be from the current image
                // or from the local settings.txt file.
                measured = distanceInPixels;
                known = distanceInUnits;
                aspectRatio = imageAspectRatio;
                unit = distanceUnitName;
                scale = IJ.d2s(measured/known,digits)+" pixels/"+unit;
            }
        }
        Roi roi = imp.getRoi();
        if (roi!=null && (roi instanceof Line)) {
            measured = ((Line)roi).getRawLength();
            known = 0.0;
        }
        
        SetSettingsDialog introDialog = new SetSettingsDialog("VNT Settings Editor", scale);
        introDialog.addMessage("The following settings will be saved in the folder of the active image.");
        introDialog.addNumericField("Distance in Pixels:", measured, digits, 8, null);
        introDialog.addNumericField("Known Distance:", known, 2, 8, null);
        introDialog.addNumericField("Pixel Aspect Ratio:", aspectRatio, 1, 8, null);
        introDialog.addStringField("Unit of Length:", unit);
        introDialog.addMessage("Scale: "+"12345.789 pixels per centimeter");
        introDialog.addCheckbox("Perform Lighting Correction", polynomialLightingCorrection);
        introDialog.addCheckbox("Generate Evaluation Image 'Eye Candy'", eyeCandy);
        introDialog.addCheckbox("Use High/Low Segmentation? (No means FindEdges Segmentation)", highlowSegmentation);
        introDialog.addCheckbox("Keep Temporary Image Files", tempImageFiles);
        introDialog.addCheckbox("Keep Graph Text Files", graphTextFiles);
        introDialog.addCheckbox("Display Advanced Settings", advancedSettings);
        introDialog.showDialog();
        
        // make sure we pushed OK
        if (introDialog.wasCanceled()) {
            IJ.error("Cancel detected. No settings will be saved.");
            return;
        }
        measured = introDialog.getNextNumber();
        known = introDialog.getNextNumber();
        aspectRatio = introDialog.getNextNumber();
        unit = introDialog.getNextString();
        if (unit.equals("um"))
            unit = IJ.micronSymbol+"m";
        else if (unit.equals("A"))
            unit = ""+IJ.angstromSymbol;
        polynomialLightingCorrection = introDialog.getNextBoolean();
        eyeCandy = introDialog.getNextBoolean();
        highlowSegmentation = introDialog.getNextBoolean();
        tempImageFiles = introDialog.getNextBoolean();
        graphTextFiles = introDialog.getNextBoolean();
        advancedSettings = introDialog.getNextBoolean();
        // if input is invalid, use default setting
        if (measured<=0.0 || known <= 0.0 || unit.startsWith("pixel") || unit.startsWith("Pixel") || unit.equals("")) {
            measured = 0.0;
            known = 1.0;
            aspectRatio = 1.0;
            unit = "pixel";
        }

        // based on your inputs, we may have to ask a few more questions
        if (polynomialLightingCorrection || eyeCandy || advancedSettings || !highlowSegmentation) {
            GenericDialog advancedDialog = new GenericDialog("VNT Advanced Settings Editor");
            advancedDialog.addMessage("Based on your inputs, there are a few more parameters:");
            
            if (polynomialLightingCorrection) {
                // lighting correction only
                advancedDialog.addMessage("Lighting Correction Settings:");
                advancedDialog.addNumericField("Lighting Correction Black Pixel Cutoff (-1 will use histogram):", lightingCutoff, 0, 8, null);
                advancedDialog.addNumericField("Lighting Correction Polynomial Surface Order:", lightingOrder, 0, 8, null);
            }
            if (polynomialLightingCorrection && eyeCandy) {
                // lighting correction and eye candy
                advancedDialog.addMessage("Lighting Correction Eye Candy:");
                advancedDialog.addCheckbox("Generate Lighting Correction '3D'PolynomSurface Image 'Eye Candy'", lightingCorrectionEyeCandy);
            }
            if (eyeCandy) {
                // eye candy only
                advancedDialog.addMessage("Eye Candy Settings:");
                advancedDialog.addCheckbox("Add original Image to 'Eye Candy'", originalImage);
            }
            if (!highlowSegmentation) {
                advancedDialog.addMessage("FindEdges Variance Segmentation Settings:");
                advancedDialog.addNumericField("Maximum Area to Fill Meshes in Pixels:", maxMeshArea, 0, 8, null);
            }
            if (advancedSettings) {
                // "advanced features"
                advancedDialog.addMessage("The following are 'advanced' features:");
                advancedDialog.addCheckbox("Display VNT Java Code Debug Text In Console", debugText);
                advancedDialog.addCheckbox("Animate Displays (if on, relatively very slow but very visual!)", animation);
            }
            advancedDialog.showDialog();
            if (advancedDialog.wasCanceled()) {
                IJ.error("Cancel detected. No settings will be saved.");
                return;
            }
            if (polynomialLightingCorrection) {
                lightingCutoff = (int)advancedDialog.getNextNumber();
                lightingOrder = (int)advancedDialog.getNextNumber();
            }
            if (polynomialLightingCorrection && eyeCandy) {         
                lightingCorrectionEyeCandy = advancedDialog.getNextBoolean();
            }
            if (eyeCandy) {
                originalImage = advancedDialog.getNextBoolean();
            }
            if (!highlowSegmentation) {
                maxMeshArea = (int)advancedDialog.getNextNumber();
            }
            if (advancedSettings) {
                debugText = advancedDialog.getNextBoolean();
                animation = advancedDialog.getNextBoolean();
            }
        }      
        
        // calculate a pretty date and time
        Date rightNow = new Date();
        DateFormat dfFullDate = DateFormat.getDateInstance(DateFormat.FULL);
        DateFormat dfFullTime = DateFormat.getTimeInstance(DateFormat.FULL);
        String myDate = dfFullDate.format(rightNow);
        String myTime = dfFullTime.format(rightNow);
        
        // save changes
        FileOutputStream out = null; // declare a file output object
        PrintStream p = null; // declare a print stream object
        try{
            String pathName = directory + "settings.txt";
            // Create a new file output stream
            out = new FileOutputStream(pathName);
            // Connect print stream to the output stream
            p = new PrintStream( out );
            p.println("; =====================================");
            p.println("; =Vascular Network Toolkit for ImageJ=");
            p.println("; = Settings File for Local Directory =");
            p.println("; =====================================");
            p.println("; * Copyright (C) 2005-2006 - (see VNT's readme.txt for more information)");
            p.println("; Package by: Michael Miller, Truman State University");
            p.println("; This file was generated by Create_Settings.java v"+versionMain+"."+versionSub+": " + myTime + " on " + myDate + ".");
            p.println("; Visit the VNT webpage at: http://www2.truman.edu/~mdm162/vnt/");
            p.println("LengthUnitName = " + unit);
            p.println("DistanceInPixels = " + measured);
            p.println("DistanceInUnits = " + known);
            p.println("AspectRatio = " + aspectRatio);
            p.print("PerformPolynomialLightingCorrection = ");
            if (polynomialLightingCorrection)
                p.println("true");
            else
                p.println("false"); 
            p.println("LightingCorrectionBlackCutoff = " + lightingCutoff);
            p.println("LightingCorrectionPolynomialOrder = " + lightingOrder);                  
            p.print("GenerateLightingCorrectionSurfaceEyeCandy = ");
            if (lightingCorrectionEyeCandy)
                p.println("true");              
            else
                p.println("false");
            p.print("GenerateEyeCandy = ");
            if (eyeCandy)
                p.println("true");              
            else
                p.println("false");
            p.print("GenerateEyeCandyWithOriginalImage = ");
            if (originalImage)
                p.println("true");              
            else
                p.println("false");             
            p.print("UseHighLowSegmentation = ");
            if (highlowSegmentation)
                p.println("true");              
            else
                p.println("false");
            p.println("MaxMeshAreaIgnore = " + maxMeshArea);
            p.print("KeepTemporaryImageFiles = ");
            if (tempImageFiles)
                p.println("true");              
            else
                p.println("false");
            p.print("KeepGraphTextFiles = ");
            if (graphTextFiles)
                p.println("true");              
            else
                p.println("false");
            p.print("DisplayJavaCodeDebugConsoleText = ");
            if (debugText)
                p.println("true");              
            else
                p.println("false");
            p.print("PerformAnimatedRendering = ");
            if (animation)
                p.println("true");              
            else
                p.println("false");
            
            p.println("");
            p.flush();
            out.flush();
            p.close();
            out.close();
        } catch (Exception e) {
            // didn't work for whatever reason. clean up and exit.          
            System.out.println("Create_Settings.java -- Unexpected error writing to file settings.txt");
            try {
                if (p != null)
                    p.close();
                if (out != null)
                    out.close();
            } catch (Exception error) {
                // error occured inside an error, we better stop poking around :)
            }
        }
        // all done! Assuming no error, there should be a newly saved settings.txt
        // file in the same folder as the opened image
    }
}

/**
 * This beautiful class was stolen from ImageJ's ScaleDialog.java file.
 * Slight modifications were made to suit the VNT.
 */
class SetSettingsDialog extends GenericDialog {
    static final String NO_SCALE = "<invalid scale!>";
    String initialScale;

    public SetSettingsDialog(String title, String scale) {
        super(title);
        initialScale = scale;
    }

    protected void setup() {
        if (IJ.isJava2())
            initialScale += "          ";
        setScale(initialScale);
    }
    
    public void textValueChanged(TextEvent e) {
        Double d = getValue(((TextField)numberField.elementAt(0)).getText());
        if (d==null)
            {setScale(NO_SCALE); return;}
        double measured = d.doubleValue();
        d = getValue(((TextField)numberField.elementAt(1)).getText());
        if (d==null)
            {setScale(NO_SCALE); return;}
        double known = d.doubleValue();
        String theScale;
        String unit = ((TextField)stringField.elementAt(0)).getText();
        boolean noScale = measured<=0||known<=0||unit.startsWith("pixel")||unit.startsWith("Pixel")||unit.equals("");
        if (noScale)
            theScale = NO_SCALE;
        else {
            double scale = measured/known;
            int digits = Tools.getDecimalPlaces(scale, scale);
            theScale = IJ.d2s(scale,digits)+" pixels/"+unit;
        }
        setScale(theScale);
    }
    
    void setScale(String theScale) {
        ((Label)theLabel).setText("Scale: "+theScale);
    }
}