
package usda.weru.util.wepsFileChooser2;

import de.schlichtherle.truezip.file.TFile;
import usda.weru.remoteDataAccess.remoteFiles.WepsBaseFile;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import static javax.swing.JOptionPane.showMessageDialog;
import javax.swing.ListCellRenderer;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import usda.weru.util.Util;
import usda.weru.weps.Weps;

/**
 *
 * @author mhaas
 */
public class WfcPathComboBox extends JComboBox<WfcPathComboBox.WFC_PathComboElement> implements PropertyChangeListener {
    
    static final long serialVersionUID = 1L;
    private static final Logger LOGGER = LogManager.getLogger(WfcPathComboBox.class);
    
    WFC_comboBoxModel pathComboModel;
    WFC_PathComboElement selectedItem;
    WFC_PathComboElement defaultDirElement;
    WFC_PathComboElement projDirElement;
    WFC_PathComboElement homeDirElement;
    WFC_PathComboElement dbDirElement;
    WFC_PathComboElement wepsHomeDirElement; // The weps home directory (Sometimes differs from the user's 'home' directory)
    WFC_PathComboElement cfgDefaultDirElement;

    public static String propChangeCmdStrPathChanged = "WFC_ComboBox_path_changed";
    public static String propChangeCmdStrUpdatePath = "WFC_ComboBox_update_path";

    public WfcPathComboBox () {
        
        selectedItem = new WFC_PathComboElement (new WepsBaseFile ());
        setRenderer(new WFC_cbRenderer());
        
        pathComboModel = new WFC_comboBoxModel (this);
        addItemListener(pathComboModel);
        setModel ( pathComboModel );
        
        // Set to the default location, can be changed if needed.
        defaultDirElement = null;
        projDirElement = null;
        homeDirElement = null;
        dbDirElement = null;
        
        addActionListener(this);
    }
    
    // strings for getting the default locations from WEPS
    static final String DEFAULT_RUNS_LOCATION = "CD-default runs location Template"; 
    static final String PROJECT_LOCATION = "CD-projects path";
    
    /**
     * This method sets the defaults of the WFC_PathCombo_Elements to their values
     * specified in config data. Only needs to be caalled in WepsFileChooser2
     */
    protected void setElements() {
        // Normally this could be done outside of the constructor. However 
        // *sometimes* when in the constructor, the .form file for WepsFileChooser2_n
        // gets corrupted and cannot be edited with gui editor. So I pulled it out. CKM
        defaultDirElement = new WFC_PathComboElement(new File(Util.parse(Weps.getInstance().cd.getData(DEFAULT_RUNS_LOCATION))));
        projDirElement = new WFC_PathComboElement(new File(Util.parse(Weps.getInstance().cd.getData(PROJECT_LOCATION))));
    }
    
    public File getSelectedDir () {
        return selectedItem.file;
    }
        
//    public void addItem(WepsBaseFile item) {
    public void addItem(File item) {
        super.addItem (new WFC_PathComboElement(item));
        pathComboModel.setDefaultSelection();
    }
    
    /**
     * Deletes all elements in file chooser drop down menu.
     * Used to narrow down how many elements appear in file chooser drop
     * down menu.
     */
    public void deleteAll() {
        pathComboModel.deleteAll();
    }
    
    /**
     * Set the directory for clicking the "Def Loc" or default location
     * button in the file chooser. Does not set the initial directory the file 
     * chooser opens up into!
     * @param defaultDir
     */
    public void setDefaultDir(File defaultDir) {
        defaultDirElement = new WFC_PathComboElement(defaultDir);
    }
    
    /**
     * Set the directory for clicking the "cfg Def Loc" or default location
     * button in the file chooser. Does not set the initial directory the file 
     * chooser opens up into!
     * @param defaultDir
     */
    public void setCfgDefaultDir(File defaultDir) {
        cfgDefaultDirElement = new WFC_PathComboElement(defaultDir);
    }
     public void selectCfgDefaultDir() {
        super.addItem(cfgDefaultDirElement);
        
        cfgDefaultDirElement = pathComboModel.selectNewestItem();
    }
    
    /**
     * Sets the location of the Database button for this particular
     * filechooser instance.
     * @param dbDir 
     */
    public void setDbDir(File dbDir) {
        dbDirElement = new WFC_PathComboElement(dbDir);
    }
    
    public void selectConfDefaultDir() {
        super.addItem(defaultDirElement);
        
        defaultDirElement = pathComboModel.selectNewestItem();
    }
    
    public void selectDefaultDir() {
        //defaultDirElement = new WFC_PathComboElement(new File(Util.parse(Weps.getInstance().cd.getData(DEFAULT_RUNS_LOCATION))));
        super.addItem(defaultDirElement);
        ///pathComboModel.setSelectedItem(defaultDirElement);
        defaultDirElement = pathComboModel.selectNewestItem();
    }
    
    public void selectProjDir() {
        projDirElement = new WFC_PathComboElement(new File (Util.getProperty("project.directory")));
        super.addItem (projDirElement);
        // need to store again, because selectNewestItem() updates values in the element.
        projDirElement = pathComboModel.selectNewestItem();
    }
    
    public void selectProjParentDir() {
        projDirElement = new WFC_PathComboElement((new File (Util.getProperty("project.directory"))).getParentFile());
        super.addItem (projDirElement);
        // need to store again, because selectNewestItem() updates values in the element.
        projDirElement = pathComboModel.selectNewestItem();
    }
    
    public void selectDbDir() {
        if (dbDirElement == null) {
            dbDirElement = new WFC_PathComboElement(new File (Util.getProperty("weps.database_dir")));
            super.addItem (dbDirElement);
            // need to store again, because selectNewestItem() updates values in the element.
            dbDirElement = pathComboModel.selectNewestItem();
        } else {
           pathComboModel.setSelectedItem(dbDirElement);
        }
    }
    
    /**
     * Selects the ${weps.home} value and sets the filechooser to select that
     */
    public void selectWepsHome () {
        if (wepsHomeDirElement == null) {
            wepsHomeDirElement = new WFC_PathComboElement(new File (Util.getProperty("weps.home")));
            super.addItem(wepsHomeDirElement);
                        // need to store again, because selectNewestItem() updates values in the element.
            dbDirElement = pathComboModel.selectNewestItem();
        } else {
            pathComboModel.setSelectedItem(dbDirElement);
        }
    }
    
    public void selectDir(String initialPath) {
        parseDir(initialPath);
    }
    
//    public void selectSysTempDir() {
//        //System.out.println("Selecting System Template Directory.");
//        String initialPath = Weps.getInstance().cd.getData(ConfigData.ManTemp);
//        parseDir(initialPath);
//    }
//    
//    public void selectLocTempDir() {
//        //System.out.println("Selecting Local Template Directory.");
//        String initialPath = Weps.getInstance().cd.getData(ConfigData.ManTempSaveAs);
//        parseDir(initialPath);
//    }
//    
//    public void selectShaTempDir() {
//        //System.out.println("Selecting Shared Template Directory.");
//        String initialPath = Weps.getInstance().cd.getData(ConfigData.LocalManDB);
//        parseDir(initialPath);
//    }
    
    private void parseDir(String initialPath) {
//        System.out.println("*** Initial Path: " + initialPath);
//        System.out.println("*** Parsed Path:  " + Util.parse(initialPath));
        pathComboModel.setPath(Util.parse(initialPath));
    }
    
    public void selectHomeDir() {
        if (homeDirElement == null) {
            //homeDirElement = new WFC_PathComboElement(About.getWepsHome());
            homeDirElement = new WFC_PathComboElement(new File(System.getProperty("user.home")));
            super.addItem (homeDirElement);
            // need to store again, because selectNewestItem() updates values in the element.
            homeDirElement = pathComboModel.selectNewestItem();
        } else {
           pathComboModel.setSelectedItem(homeDirElement);
        }
    }
    
    public void upOneDir() {
        pathComboModel.selectUpOneDir();
    }
    
    public void addAndSelectItem(File item) {
        if (item.compareTo(getSelectedDir()) != 0) {
            super.addItem (new WFC_PathComboElement(item));
            pathComboModel.selectNewestItem();
        }
    }

    public void addToCurrentPath (File dir) {
        pathComboModel.addToCurrentPath (dir);
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(WfcPathComboBox.propChangeCmdStrUpdatePath)) {
            addToCurrentPath ((File)evt.getNewValue());
        }
    }
    
    @Override
    public void actionPerformed(ActionEvent e) { 
        String s1 = e.getActionCommand();
        String s2 = e.paramString();
        WfcPathComboBox.WFC_PathComboElement item = (WfcPathComboBox.WFC_PathComboElement) this.getSelectedItem();
        firePropertyChange(propChangeCmdStrPathChanged, selectedItem.file, item.file);
        selectedItem = item;
    }        
    

    //
    // Internal class
    //
    protected class WFC_comboBoxModel extends DefaultComboBoxModel<WFC_PathComboElement> implements ItemListener {

        static final long serialVersionUID = 1L;
    
        WfcPathComboBox comboBox;
        protected ArrayList <ArrayList <WFC_PathComboElement>> roots;
        protected int selectedItemIdx;
        protected WFC_PathComboElement selectedItem;

        public WFC_comboBoxModel (WfcPathComboBox comboBox) {
            super();
            this.comboBox = comboBox;
            roots = new ArrayList <> ();
            selectedItemIdx = -1;
            selectedItem = new WFC_PathComboElement (new WepsBaseFile ());
        }
        
        public void setDefaultSelection () {
            if (roots.size() > 0) {
                ArrayList <WFC_PathComboElement> l = roots.get(0);
                setSelectedItem (l.get(l.size() - 1));
                this.fireContentsChanged(this, 0, 0); 
            }
        }
        public WFC_PathComboElement selectNewestItem () {
            ArrayList <WFC_PathComboElement> l = roots.get(roots.size() - 1);
            setSelectedItem (l.get(l.size() - 1));
            fireContentsChanged(this, 0, 0);
            return l.get(l.size() - 1);
        }
        public void selectUpOneDir () {
            if (selectedItem.pathIdx > 0) {
                ArrayList <WFC_PathComboElement> l = roots.get(selectedItem.rootIdx);
                setSelectedItem (l.get(selectedItem.pathIdx - 1));
                fireContentsChanged(this, 0, 0);
            }
        }

        public void addToCurrentPath (File dir) {
            File newFile = selectedItem.file;
            
            newFile = WepsBaseFile.newOfParentType (newFile,dir.getName() );
            ArrayList <WFC_PathComboElement> l = roots.get(selectedItem.rootIdx);
            WFC_PathComboElement newItem = new WFC_PathComboElement (newFile, getIndent(l.size()), selectedItem.rootIdx, l.size() );
            l.add(newItem);
            setSelectedItem (newItem);
            this.fireContentsChanged(this, 0, 0);
        }
        
        protected ArrayList<WFC_PathComboElement> buildPath (File item, ArrayList<WFC_PathComboElement> list, int rootIdx) {
            if (item.getParentFile() != null) {
                list = buildPath (item.getParentFile(), list, rootIdx);
            }
            list.add(new WFC_PathComboElement (item, getIndent(list.size()), rootIdx, list.size() ));
            return list;
        }
        
        protected File getElementAbsoluteFile (ArrayList<WFC_PathComboElement> list, int idx) {
            File f = list.get(0).file;
            
            for (int i = 1; i <= idx; i++) {
                if(i < 0) {
                    System.out.println("WARNING: Provided index in wepsFileChooser2\\WfcPathComboBox is less than 0. Pulling first element of list.");
                    f = WepsBaseFile.newOfParentType (f,list.get(0).file.getName() );
                }else if(i < list.size()) {
                    f = WepsBaseFile.newOfParentType (f,list.get(i).file.getName() );
                } else {
                    System.out.println("WARNING: Provided index in wepsFileChooser2\\WfcPathComboBox is out-of-bounds. Pulling last element of list.");
                    f = WepsBaseFile.newOfParentType (f,list.get(list.size() - 1).file.getName() );
                }
            }
            return f;
        }

        public int getElementIndex(WFC_PathComboElement element) {
            int rootIdx;
            int elemIdx;
            int rootOffset = 0;
            for (rootIdx = 0; rootIdx < roots.size(); rootIdx++) {
                ArrayList <WFC_PathComboElement> elemList = roots.get(rootIdx);
                for (elemIdx = 0; elemIdx < elemList.size(); elemIdx++) {
                    if (elemList.get(elemIdx).file.equals(element.file)) {
                        rootOffset += elemIdx;
                        return rootOffset;
                    }
                }
                rootOffset += elemIdx;
            }
            return -1;
        }

        public WFC_PathComboElement getLastElement() {
            int rootIdx = roots.size()-1;
            ArrayList <WFC_PathComboElement> elemList = roots.get(rootIdx);
            int elemIdx = elemList.size()-1;
            return elemList.get(elemIdx);
        }
        
        protected WFC_PathComboElement buildNewElementIntoRoots (File elemFile) {
            return buildNewElementIntoRoots (elemFile, -1);
        }
        
        protected WFC_PathComboElement buildNewElementIntoRoots (File elemFile, int rootIdx) {
            ArrayList<WFC_PathComboElement> newList;
            if (rootIdx >= 0) {
                newList = buildPath (elemFile, new ArrayList<> (), rootIdx);
                roots.set(rootIdx, newList);
            } else {
                newList = buildPath (elemFile, new ArrayList<> (), roots.size());
                roots.add(newList);
            }
            return newList.get(newList.size()-1);
        }
        
        protected final static String indentSrc = "                                                                      ";
        protected String getIndent (int idx) {
            return indentSrc.substring(0, idx * 2);
        }

        
        @Override
        public void itemStateChanged(ItemEvent e) {
            File elemFile = null;
            if (e.getStateChange() == java.awt.event.ItemEvent.SELECTED) {
                WFC_PathComboElement element = (WFC_PathComboElement)e.getItem();
                if (element.rootIdx == -1) {
                    // throw error
                } else {
                    // Item changed.  Example: it is an ancestor of the (previous) current selection
                    // Get the full (absolute) path
                    elemFile = getElementAbsoluteFile(roots.get(element.rootIdx), element.pathIdx);
                    // And (re) build this element hierarchy
                    buildNewElementIntoRoots (elemFile, element.rootIdx);
                }
            }
        }


        @Override
        public int getSize() {
            int size = 0;
            for (int i = 0; i < roots.size(); i++) {
                size += roots.get(i).size();
            }
            return size;
        }
        
        /**
         * Removes all element in file chooser drop down list.
         * Used when you only want one option in file chooser drop down menu
         * could be dangerous for windows systems, if you need access to another drive
         */
        private void deleteAll() {
            roots.removeAll(roots); // Dirty hack?
        }

        @Override
        public WFC_PathComboElement getElementAt(int index) {
            int idx = index;
            
            int rootIdx;
            int rootOffset = idx;
            for (rootIdx = 0; rootIdx < roots.size(); rootIdx++) {
                if (roots.get(rootIdx).size() <= rootOffset) {
                    rootOffset -= roots.get(rootIdx).size();
                } else {
                    break;
                }
            }

            if (roots.size() > 0) {
                return roots.get(rootIdx).get(rootOffset);
            } else {
                return null;
            }
        }

        @Override
        public void addElement(WFC_PathComboElement item) {
            buildNewElementIntoRoots (item.file);

            int idx = getSize()-1;
            fireContentsChanged(this, getSize()-1, getSize()-1);
        }
        
        @Override
        public void setSelectedItem(Object anItem) {
            if (selectedItem != anItem) {
                File oldVal = selectedItem.file; // Why is this here?
                if (anItem instanceof WFC_PathComboElement) {
                    selectedItem = (WFC_PathComboElement)anItem;
                    fireContentsChanged(this, 0, 0);
                } else  {
                    // if element is a string, it is newly typed into the combo
                    // and needs to be built into our data tree (roots)
                    
                    // This redundancy normalizes any possible path naming discrepancies
                    // due to user typing / input variances
                    TFile newFile = new TFile(anItem.toString());
                    System.out.println("newFile Location: " + newFile.getAbsolutePath());
                    if (newFile.exists()) {
                        try {
                            newFile = new TFile(newFile.getCanonicalPath());
                            selectedItem = buildNewElementIntoRoots(newFile);
                            fireContentsChanged(this, getSize()-1, getSize()-1);
                        } catch (IOException ex) {
                            LOGGER.log(Level.ERROR, ex);
                        }
                    } else {
                        showMessageDialog(comboBox, "The specified directory or path to file does not exist,\n" +
                                "please re-specify or manually navigate to the location.", "Error", JOptionPane.ERROR_MESSAGE);
                        setDefaultSelection(); 
                    }
                }
            }
        }
        
        public void setPath(String path) {
            System.out.println("Path: " + path);
            TFile newFile = new TFile(path);
            System.out.println("newFile Location: " + newFile.getAbsolutePath());
            if (newFile.exists()) {
                try {
                    newFile = new TFile(newFile.getCanonicalPath());
                    selectedItem = buildNewElementIntoRoots(newFile);
                    fireContentsChanged(this, getSize()-1, getSize()-1);
                } catch (IOException ex) {
                    LOGGER.log(Level.ERROR, ex);
                }
            } else {
                showMessageDialog(comboBox, "The file location specified does not seem to exist: \n"
                        + path 
                        + "\nPlease re-specify in the configuration panel settings\n" +
                        "or navigate to your desired directory manually.", "Error", JOptionPane.ERROR_MESSAGE);
                setDefaultSelection();
            }
        }
        
        @Override
        public Object getSelectedItem() {
            return selectedItem;
        }
    }
    
        

    //
    // Internal class
    //
    protected class WFC_cbRenderer extends JLabel implements ListCellRenderer<WFC_PathComboElement> {    
        private static final long serialVersionUID = 1L;
        public WFC_cbRenderer () {

        }
        
        
        
        @Override
        public Component getListCellRendererComponent(JList<? extends WFC_PathComboElement> list, WFC_PathComboElement value, int index, boolean isSelected, boolean cellHasFocus) {
            String txt = "";
            if (value != null) {
                // set text to full path name for convinience
                txt = value.file.getName(); // value.file.getName(); // for only the names
                if (txt.length() <= 0) {
                    // For a root path ( c:\), there is no file, only a path
                    txt = value.file.getPath();
                }
                if (index >= 0) {
                    // index = -1 for the combo item, >=0 for the dropdown
                    // So only add the padding if in the dropdown
                    txt = value.prefix + txt;
                }
            }
            
             setText(txt);
             return this;
        }
    }
    

    //
    // Internal class
    //
    protected class WFC_PathComboElement {
//        WepsBaseFile file;
        File file;
        String prefix;
        int rootIdx;
        int pathIdx;
        
        protected WFC_PathComboElement (File file) {
            this(file, null, -1, -1);
        }
        
        protected WFC_PathComboElement (File file, String prefix, int rootIdx, int pathIdx) {
            this.file = file;
            this.prefix = prefix;
            this.rootIdx = rootIdx;
            this.pathIdx = pathIdx;
        }
        
        @Override
        public String toString () {
            String s = this.file.getName();
            // The root elements (ex. C:/) do not have a name, only a path
            if (s.length() <= 0) {
                s = this.file.getPath();
            }
            return s;
        }
    }
    
}
