/*
 * RunNameChooser.java
 *
 * Created on March 17, 2006, 1:24 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package usda.weru.weps;
import de.schlichtherle.io.File;
import javax.swing.*;

import java.awt.*;
import usda.weru.weps.gui.*;
import usda.weru.util.*;
import javax.swing.event.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
 * RunNameChooser is a dialog box used for selecting a run name.  The dialog box
 * also allows the user to select the location for the run.  A set of checks is 
 * performed on the run name and location to make sure it meets the requirements
 * before the user has to click ok.
 * @author Joseph Levin
 */
public class RunNameChooser extends RunNameChooser_n implements DocumentListener, FocusListener, ActionListener, PropertyChangeListener{    
    /**
     * File object which represents the new run.
     * Run Location + Run Name + RunNameSuffix
     */
    protected File c_runFile;
    /**
     * References the Ok button so the CheckForErrors() method can update the enabled
     * state of the button.
     */
    protected JButton c_okButton;    
    /**
     * References the JOptionPane so the CheckForErrors() method can change the icon
     * to indicate the existince of an error.
     */
    protected JOptionPane c_jop;
    /**
     * References the component which should be given focus after CheckForErrors() is 
     * called.  This is to fix odd behavior in which the text fields lost focus due
     * to the error checking.
     */
    protected Component c_lastFocus;

    protected ConfigData c_cd;
    protected RunFileData c_rfd;
    
    protected String c_runsLocation;
    protected String c_defaultRunsLocation;
    protected String c_lastAttemptedRun;
    protected String c_lastRun;
    /**
     * Creates a new instance of RunNameChooser
     * @param runLocations Intial directory to display for run location.
     * @param runName Intial name to use for the run.
     */    
    public RunNameChooser(RunFileData rfd) {
        super();        
        c_cd = ConfigData.getDefault();        
        c_cd.fireAll(this);        
        
        c_rfd = rfd;        
        c_rfd.fireAll(this);        
        
        //Document listeners
        JTF_runname.getDocument().addDocumentListener(this);
        JTF_runlocation.getDocument().addDocumentListener(this);
        
        //Focus listeners
        JTF_runname.addFocusListener(this);
        JTF_runlocation.addFocusListener(this);
        
        //Action Listeners
        JTF_runname.addActionListener(this);
        JTF_runlocation.addActionListener(this);
        
        
                        
        
        String runName = "new run";
        
        if (c_lastAttemptedRun != null && c_lastAttemptedRun.length() > 0) {
            runName = c_lastAttemptedRun;
        }
                        
        String runLocation = c_runsLocation;
        if(runLocation == null || runLocation.trim().length() == 0) {
            runLocation = c_defaultRunsLocation;
        }

        //FIXME: this is the original code -- above is fixed to remove redundant null check but may be wrong
//        if(runLocation == null || runLocation.trim().length() == 0) runLocation = c_runsLocation;
//        if(runLocation == null || runLocation.trim().length() == 0) {
//            runLocation = c_defaultRunsLocation;
//        }
        
        c_runFile = Util.incrementFileName(new File (runLocation, runName + RunFileData.RunSuffix), null, RunFileData.RunSuffix);          
        
        JTF_runname.setText(Util.purgeExtensions(c_runFile.getName(), RunFileData.RunSuffix));
        JTF_runlocation.setText(runLocation);
        
        
        

    }
    
    public boolean getCreateBatch(){
        return JCB_batchMode.isSelected();
    }
    
    /**
     * Getter for the created run file.
     * @return File object representing the new run.
     */
    public File getRunFile(){
        return c_runFile;
    }
    
    /**
     * Getter for the run location.
     * @return Returns the parent file object from the getRunFile() method.
     */
    public File getRunLocation(){
        return new File(getRunFile().getParentFile());
    }
    
    /**
     * Combines the values in the text fields and the run suffix to create a new
     * File object representing the run.  The suffix is a constant in the RunFileData 
     * class.
     * 
     * RunLocatino + RunName + Suffix
     */
    private void buildRunFile(){
        c_runFile = new File (JTF_runlocation.getText().trim(), JTF_runname.getText().trim() + RunFileData.RunSuffix);
        JTF_runfile.setText(c_runFile.getPath());
        checkForErrors();
    }
    
    /**
     * Checks the built run File object for errors.  If an error is found a message is
     * displayed on the RunNameChooser dialog.  The dialog icon is also changed to
     * indicate an error, and the Ok button is disabled when there is an error.
     * 
     * Errors:
     * 1. Run name is required.
     * 2. Run location is required.
     * 3. The run file cannot already exist.
     * 4. Run name contains invalid characters.
     */
    private void checkForErrors(){            
        boolean hasError = false;
        if (c_runFile == null){
            return;
        }
        else if (JTF_runname.getText().trim().length() == 0){
            //run name is required
            setError("A run name is required.");
            hasError = true;
        }          
        else if (JTF_runlocation.getText().trim().length() == 0){
            //run location is required
            setError("A run location is required.");
            hasError = true;
        }          
        else if (c_runFile.exists()){
            //The file already exists
            if(c_runFile.getName().length() > 50) {
                setError("Run name already exists.  Please choose another.");
            } else {
                setError("Run '" + c_runFile.getName() + "' already exists.");
            }
            hasError = true;
        }          
        else if (checkInvaildChar(JTF_runname.getText().trim())){
            //The file is not a valid name
            setError("Invalid character in run name.");
            hasError = true;
        }    

        if(Util.isWindows()){
            String lengthError = checkFileLengths(new File(JTF_runfile.getText()));
            if(lengthError != null){
                setError(lengthError);
                hasError = true;
            }
        }
 
        
        if(!hasError){
            setError("");
        }
        
        if (c_jop != null){
            if (hasError){
                c_jop.setMessageType(JOptionPane.ERROR_MESSAGE);
            }
            else{
                c_jop.setMessageType(JOptionPane.INFORMATION_MESSAGE);
            }
        }
         
        
        
        
        //Fix focus loss issue
        if (c_lastFocus != null){
            c_lastFocus.requestFocusInWindow();
        }     
        
        c_okButton = (JButton) findOptionButton(c_jop, "Ok"); 
        
        if (c_okButton != null){              
            c_okButton.setEnabled(!hasError);                                
            c_jop.revalidate();
            c_jop.repaint();
        }
         
    }

    private String checkFileLengths(File run){
        //check file lengths of the man,ifc
        String runPath = run.getAbsolutePath();

        //run location
        if(runPath.length() + 25 > Util.MAX_WINDOWS_FILEPATH_LENGTH){
            return "Run location invalid.  Run name too long.";
        }

        //man file
        String manPath = c_rfd.getData(RunFileData.ManageFile);
        if(manPath != null){
            File manFile = new File(manPath);
            String manName = manFile.getName();
            if(runPath.length() + manName.length() + 1 > Util.MAX_WINDOWS_FILEPATH_LENGTH){
                return "Management file invalid. Filename too long.";
            }
        }

        //ifc file
        String ifcPath = c_rfd.getData(RunFileData.SoilFile);
        if(ifcPath != null){
            File ifcFile = new File(ifcPath);
            String ifcName = ifcFile.getName();
            if(runPath.length() + ifcName.length() + 1 > Util.MAX_WINDOWS_FILEPATH_LENGTH){
                return "Soil file invalid. Filename too long.";
            }
        }

        return null;
    }
    
    /**
     * If the error message does not equal the one already being displayed, the new
     * message is displaye in the error label.
     * @param error Error message to display.
     */
    private void setError(String error){
        //only update if the text changed            
        if (JL_error.getText().equals(error) == false){
            JL_error.setText(error);
        }  
         
    }
    
    
    /**
     * Creates and displays a JOptionPane with 'this' as the message.  The result is
     * parsed and returned to the user.  If the user clicks Ok the run location will 
     * be checked for existince.  The user will be prompted to confirm the creation
     * of the run location.  If the user denies the run location creation the main
     * dialog will be displayed again.
     * @param parent Component which owns should the dialog box.  Used for the modal
     * behavior.
     * @return File object representing the run to create.  Null is return if the 
     * user did not click Ok.
     */
    public File showRunNameChooser(Component parent){    
        //return JOptionPane.showOptionDialog(parent, this, "WEPS Run Name", JOptionPane.YES_OPTION + JOptionPane.CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE, null, null, null);                      
        c_jop = new JOptionPane(this);
        c_jop.setMessageType(JOptionPane.INFORMATION_MESSAGE);
        String[] options = {"Ok", "Cancel"};
        c_jop.setOptions(options);        
        JDialog dialog = c_jop.createDialog(parent, "WEPS Run Name");             
        checkForErrors();        
        
        dialog.setVisible(true);
        Object result = c_jop.getValue();
        dialog.dispose();
        
        if (result == options[0]){
            //ok
            if (validateParentStructure(getRunFile())){
                return getRunFile();
            }
            else{
                return showRunNameChooser(parent);
            }            
        }
        else{
            //cancel
            return null;
        }        
    }
    
    /**
     * The file is check for existince.  If it does not exist the user will be 
     * prompted to confirm the creation of the run location.  If the user denies the 
     * run location creation the main dialog will be displayed again.
     * @param file File object representing the run location to create.
     * @return Returns true if the location directory and all required parent
     * directories have been created.
     */
    private boolean validateParentStructure(File file){
        File parent = new File(file.getParentFile());
        if (parent.exists() == false){
            int result = JOptionPane.showConfirmDialog(this, "The run location does not exist.  Would you like to create it now?\n\n" + parent.getPath(), "WEPS Run Name",JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
            if (result == JOptionPane.YES_OPTION){
                return parent.mkdirs();
            }
            else{
                return false;
            }
        }  
        else{
            return true;
        }
    }
    
    /**
     * Recursive method for locating a button.  Looks through each of the components
     * in the comp object.  If the component is a JButton it tests the text and returns
     * if matching the testValue.  If no button is found then this method is called
     * for each child container of the comp object.
     * @param comp Component to search for the button down from.
     * @param testValue String value to test the text of buttons with.
     * @return Button with text matching the testValue.  If no button is found the 
     * method returns null.
     */
    private Component findOptionButton(Component comp, String testValue){
            if (comp instanceof JButton){
                JButton button = (JButton) comp;
                if (button.getText().equals(testValue)){
                    return comp;
                }                
            }
            if (comp instanceof Container){
                Component[] components = ((Container)comp).getComponents();
                for(int i = 0; i < components.length; i++){
                    Component child = findOptionButton(components[i], testValue);
                    if (child != null) return child;
                }
            }
            return null;
        }
    

    /**
     * Tests a directory name for invalid characters.
     * @param newDir Directort name to test.
     * @return True if newDir contains invalid characters.
     */
    private static boolean checkInvaildChar(String newDir){
        char[] invalidChars = {'\\','/','<','>','|','?','*','&','"',':','~','`'};
        //A single quote cannot be given as a character, so given as a separate string
        String singleQuote = "'";
        boolean invalidPresent = false;
        for(int i = 0;i < newDir.length(); i++){
            for(int j = 0; j < invalidChars.length;j++){
                if(newDir.charAt(i)== invalidChars[j] || newDir.charAt(i)== singleQuote.charAt(0)){
                    invalidPresent = true;
                    break;
                }
            }
            
        }
        return invalidPresent;
    }
    /**
     * Handles action events for the Browse button.  A WepsFileChooser is used to 
     * select a directory for the run location.
     * @param evt ActionEvent from the browse button.
     */
    
   
    //Browse button event
    
    protected void JB_browse_actionPerformed(java.awt.event.ActionEvent evt) {
        WepsFileChooser wfc = new WepsFileChooser(WepsFileChooser.RUNDIR, getRunLocation().getParent(), WepsFileChooser.SELECT);
        wfc.setAccessory(new ResetRunLocationPanel(c_cd, c_rfd, wfc));                       
        wfc.setSelectedFile(getRunLocation());
        int result = wfc.showDialog(this);
        if (result == wfc.APPROVE_OPTION){
            JTF_runlocation.setText(wfc.getSelectedFile().getPath());
            JTF_runlocation.requestFocusInWindow();
            c_rfd.setData(RunFileData.RunsLocation, getRunLocation().getAbsolutePath());
        }
    }
    
    //Document listener event
    
    /**
     * Calls buildRunFile() when a textfield's content changes.
     * @param e DocumentEvent from the changed text field.
     */
    public void changedUpdate(DocumentEvent e){
        buildRunFile();
    }
    
    /**
     * Calls buildRunFile() when a textfield's content changes.
     * @param e DocumentEvent from the changed text field.
     */
    public void insertUpdate(DocumentEvent e){
        buildRunFile();
    }
    
    /**
     * Calls buildRunFile() when a textfield's content changes.
     * @param e DocumentEvent from the changed text field.
     */
    public void removeUpdate(DocumentEvent e){
        buildRunFile();
    }
    
    //Focus Listener event
    /**
     * Sets the c_lastFocus variable to the source component of the FocusEvent.
     * @param e FocusEvent
     */
    public void focusGained(FocusEvent e){
        c_lastFocus = e.getComponent();
    }
    
    /**
     * Not used.  Required by FocusListener interface.
     * @param e Focus Event
     */
    public void focusLost(FocusEvent e){
        
    }
    
    //Action events
    /**
     * Action event handler to click the Ok button when the user presses [Enter] from 
     * the run name or run location text fields.
     * @param e ActionEvent
     */
    public void actionPerformed(ActionEvent e){
        if (c_okButton != null){
            c_okButton.doClick();
        }
    }
    
    public void propertyChange(PropertyChangeEvent e){
        String property = e.getPropertyName();
        String value = e.getNewValue().toString();
        if (property.equals(RunFileData.LastRunAttempt)){
            c_lastAttemptedRun = new File(value).getName();
        }
        else if(property.equals(RunFileData.RunsLocation)){
            if(value.equals("NULL")) {
                value = ConfigData.getDefault().getData(ConfigData.DefaultRunsLocation);
            }
            c_runsLocation = value;
        }
        else if(property.equals(ConfigData.DefaultRunsLocation)){
            c_defaultRunsLocation = value;
        }
        else if (property.equals(ConfigData.AllowScriptCreation)){
            if ("1".equals(value)){
                JCB_batchMode.setVisible(true);
            }
            else{
                JCB_batchMode.setVisible(false);
                JCB_batchMode.setSelected(false);
            }
        }
    }
}

