package usda.weru.util.tree;

//TODO: figure out a way not to use protected Sun classes
//Done? -DB
//import com.sun.java.swing.plaf.motif.MotifComboBoxUI;

import java.awt.Color;
import javax.swing.event.PopupMenuListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.border.EmptyBorder;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Point;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Map;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.plaf.ComboBoxUI;
import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.tree.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 
import usda.weru.mcrew.MCREWWindow;
import usda.weru.util.ConfigData;
import usda.weru.weps.Weps;

/**
 *
 * @author maxerdwien
 */
public class WepsTreeComboBox extends JComponent {

    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LogManager.getLogger(WepsTreeComboBox.class);

    public static final String PROP_MODEL = "model";
    public static final String PROP_SELECTED_ITEM = "selectedItem";
    protected JComboBox<Object> c_comboBox = null;
    protected JTree c_tree = null;
    protected LazyLoadingTreeController c_treeController;
    protected Object c_selectedItem;
    protected boolean c_selectOnLossOfFocus = false;
    private boolean closeAll = false;
    protected Map<Object, TreePath> c_pathCache;
    private boolean treeExpanding = false;

    public WepsTreeComboBox() {
        setLayout(new BorderLayout());
        initializeComboBox();
        initializeTree();
        
    }


    public WepsTreeComboBox(TreeModel model) {
        this();
        setModel(model);
    }


    public WepsTreeComboBox(TreeNode root) {
        this(new DefaultTreeModel(root));
    }

    private void initializeComboBox() {
        c_comboBox = new JComboBox<Object>() {
            private static final long serialVersionUID = 1L;

            @Override
            public void updateUI() {
                super.updateUI();
                ComboBoxUI cui = (ComboBoxUI) UIManager.getUI(this);
                //Removed MotifComboBox because it is outdated
                //if (cui instanceof MotifComboBoxUI) {
                    //cui = new MotifTreeComboBoxUI();
                //}else {
                cui = new MetalTreeComboBoxUI();
                //} 
                setUI(cui);
            }
        };
        c_comboBox.setMaximumRowCount(14);
        add(c_comboBox);
    }

    private void initializeTree() {
        c_tree = new JTree();
        c_treeController = new LazyLoadingTreeController(c_tree);

        c_tree.setVisibleRowCount(8);
        c_tree.setShowsRootHandles(true);
    }


    public JTree getTree() {
        return c_tree;
    }


    public JComboBox<Object> getComboBox() {
        return c_comboBox;
    }


    public void setModel(TreeModel model) {
        TreeModel old = c_tree.getModel();
        c_pathCache = null;
        c_tree.setModel(model);
        firePropertyChange(PROP_MODEL, old, c_tree.getModel());
    }


    public TreeModel getModel() {
        return c_tree.getModel();
    }


    protected void setComboSelectedItem(Object item) {
        c_comboBox.removeAllItems();
        c_comboBox.addItem(item);
        c_comboBox.setSelectedItem(item);
    }


    protected void setTreeSelectedPath(TreePath path) {
        c_tree.setSelectionPath(path);
    }


    public void setSelectedItem(Object item) {
        c_selectedItem = item;
        firePropertyChange(PROP_SELECTED_ITEM, null, c_selectedItem);
    }


    public void setSelectedPath(TreePath path) {
        if (path != null) {
            Object item = path.getLastPathComponent();
            setTreeSelectedPath(path);
            setComboSelectedItem(item);
            setSelectedItem(item);
        } else {
            c_tree.setSelectionRow(-1);
            c_comboBox.removeAllItems();
            setSelectedItem(null);
        }
    }


    public Object getSelectedItem() {
        return c_selectedItem;
    }


    public void setSelectOnLossOfFocus(boolean select) {
        c_selectOnLossOfFocus = select;
    }


    @Override
    public void setCursor(Cursor cursor) {
        super.setCursor(cursor);
        c_tree.setCursor(cursor);
//       c_comboBox.setCursor(cursor);
        int i = 1;
    }


    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        c_tree.setEnabled(enabled);
        c_comboBox.setEnabled(enabled);
    }

    //pass through methods

    public void setRootVisible(boolean rootVisible) {
        c_tree.setRootVisible(rootVisible);
    }


    public boolean isRootVisible() {
        return c_tree.isRootVisible();
    }


    public boolean isPopupVisible() {
        return c_comboBox.isPopupVisible();
    }


    public void setPopupVisible(boolean visible) {
        c_comboBox.setPopupVisible(visible);
    }

    public void showPopup() {
        c_comboBox.showPopup();
        if(closeAll && ConfigData.isToCollapse())
        {
            for(int index = 0; index < c_tree.getRowCount(); index ++)
            { 
                TreePath path = c_tree.getPathForRow(index);
                Object lastPathCompnent = path.getLastPathComponent();
                if (lastPathCompnent instanceof WepsTreeNodeFile) {
                    if(((WepsTreeNodeFile) lastPathCompnent).getNodeData().getAbsolutePath().endsWith(".zip")) continue;
                    c_tree.collapseRow(index);
                }
            }
        }
    }

    public void setToClose(boolean input)
    {
        closeAll = input;
    }
    
    public void hidePopup() {
        c_comboBox.hidePopup();
    }


    @Override
    public void setBackground(Color bg) {
        super.setBackground(bg);
        c_comboBox.setBackground(bg);
        c_tree.setBackground(bg);
    }


    @Override
    public void setForeground(Color fg) {
        super.setForeground(fg);
        c_comboBox.setForeground(fg);
        c_tree.setForeground(fg);
    }

    class TreePopup extends JPopupMenu implements ComboPopup, MouseMotionListener, MouseListener, KeyListener,
            PopupMenuListener, TreeExpansionListener, TreeWillExpandListener, FocusListener {
        private static final long serialVersionUID = 1L;

        protected JScrollPane c_scroller;
        protected JComboBox<?> c_comboBox;
//        protected JPopupMenu c_popup;
        protected boolean c_mouseInside = false;
        
        // required for the ComboPopup interface, this class does nothing with it but return it.
        protected final JList<Object> list = new JList<>();

        public TreePopup(JComboBox<?> comboBox) {
            c_comboBox = comboBox;
            initializePopup();
            this.setInvoker(c_comboBox);
        }

        protected void initializePopup() {
            c_scroller = new JScrollPane();

            /*never show the horizontal scrollbar, the popup will automagically resize through updatePopup();
            updatePopup() isn't working on initial folder expansion. Will leave the horizontal scrollbar on 
            for now while I work on updatePopup, this will be a quick workaround for now. Below is the line 
            to re-disable the horizontal scroll if/when I get updatePopup working properly.
            c_scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
            */
//MEH            setLightWeightPopupEnabled(false);
            setLayout(new BorderLayout());
            setBorder(new EmptyBorder(0, 0, 0, 0));
            addPopupMenuListener(this);
            add(c_scroller);
            pack();
            
        }

        protected void updatePopup() {
            
            int comboWidth = c_comboBox.getWidth() + 1;
            int popupWidth = (int) c_tree.getPreferredSize().getWidth();
            int width = Math.max(comboWidth, popupWidth);
            if(treeExpanding) {
                width = Math.max(width, 418);
            }
            //System.out.println("Row width: " + popupWidth);
            int height = (int) c_tree.getPreferredScrollableViewportSize().getHeight() * 2;
            c_tree.setSize(width, height);
//          c_popup.setPopupSize(width, height);
            setPopupSize(width + 10, height);
            
            Container parent = c_comboBox.getParent();
            while(parent != null)
            {
                if(parent instanceof Weps) break;
                if(parent instanceof MCREWWindow) break;
                parent = parent.getParent();
            }
            
            //Get the top left corner of WEPS or MCREW
            Point orig = parent.getLocationOnScreen();
            //Find the halfway point
            int half  = parent.getBounds().height / 2;
            //Get the combobox location
            Point other = c_comboBox.getLocationOnScreen();
            //find out if the point is lower than the halfway mark.
            int place = other.y - orig.y;
//            if(place > half) verticalOffset = other.y - height;
//            else verticalOffset = other.y + 25;
            if(place > half) verticalOffset = - height;
            else verticalOffset = + 25;
            
            //prevent loss of focus
            //c_tree.requestFocusInWindow();
            
        }

        int verticalOffset;
        
        @Override
        @SuppressWarnings("deprecation")
        public void show() {
            c_scroller.setViewportView(c_tree);
            //make sure we're the correct size
            //System.out.println("Pre-resizing combo box width: " + c_tree.getPreferredSize().getWidth());
            //System.out.println("Pre-resizing combo box max width: " + c_tree.getMaximumSize().getWidth());
            updatePopup();
            
            //System.out.println("Post-resizing combo box width: " + c_tree.getPreferredSize().getWidth());
            //System.out.println("Post-resizing combo box max width: " + c_tree.getMaximumSize().getWidth());
            if (c_comboBox.isShowing()) {
                show(c_comboBox, 0, verticalOffset);
                //updatePopup();
            } else {
                LOGGER.warn("Can't show popup. Parent not showing.");
                return;
            }

            TreePath path = c_tree.getSelectionPath();
            if (path != null) {
                c_tree.setSelectionPath(path);
            } else {
                c_tree.setSelectionRow(0);
            }

            //scroll to the currently selected node
            c_tree.scrollPathToVisible(path);

            c_tree.addTreeExpansionListener(this);
            c_tree.addTreeWillExpandListener(this);
            c_tree.addMouseListener(this);
            c_tree.addKeyListener(this);
            c_tree.addFocusListener(this);

            //show the popup
            setVisible(true);

            //make sure we have focus
            c_tree.requestFocus();
            
//MEH            setLocation(c_comboBox.getLocationOnScreen().x, verticalOffset);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void hide() {
            setVisible(false);

            //cleanup listeners
            c_tree.removeTreeExpansionListener(this);
            c_tree.removeTreeWillExpandListener(this);
            c_tree.removeMouseListener(this);
            c_tree.removeKeyListener(this);
            c_tree.removeFocusListener(this);

        }

        @Override
        public JList<Object> getList() {
            return list;
        }

        @Override
        public MouseListener getMouseListener() {
            return this;
        }

        @Override
        public MouseMotionListener getMouseMotionListener() {
            return this;
        }

        @Override
        public KeyListener getKeyListener() {
            return this;
        }

//        @Override
//        public boolean isVisible() {
//            return c_popup.isVisible();
//        }

        @Override
        public void uninstallingUI() {
            removePopupMenuListener(this);
        }

        // MouseListener
        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        // something else registered for MousePressed
        @Override
        public void mouseClicked(MouseEvent e) {
            WepsTreeComboBox.this.processEvent(e);
            if (e.getSource().equals(c_tree)) {
                //tree event
                TreePath path = c_tree.getPathForLocation(e.getX(), e.getY());
                if (path != null) {
                    Object o = path.getLastPathComponent();
                    if (o != null && c_tree.getModel().isLeaf(o)) {
                        //user clicked a leaf, select it!
                        System.out.println("hereleafclicked");
                        fromComboBox = true;
                        setComboSelectedItem(o);
                        setSelectedItem(o);
                        hide();
                    }
                }

            } else {
                //combo box
                if (e.getClickCount() > 1) {
                    return;
                }
                if (!SwingUtilities.isLeftMouseButton(e)) {
                    return;
                }
                if (!c_comboBox.isEnabled()) {
                    return;
                }
                if (c_comboBox.isEditable()) {
                    c_comboBox.getEditor().getEditorComponent().requestFocusInWindow();
                } else {
                    c_comboBox.requestFocusInWindow();
                }
                togglePopup();
            }
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            if (e.getSource().equals(c_tree)) {
                c_mouseInside = true;
            }
        }

        @Override
        public void mouseExited(MouseEvent e) {
            if (e.getSource().equals(c_tree)) {
                c_mouseInside = false;
            }
        }

        // MouseMotionListener
        @Override
        public void mouseDragged(MouseEvent e) {
        }

        @Override
        public void mouseMoved(MouseEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
           // if the space bar is pressed, go into the selection
           
           
        }

        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyReleased(KeyEvent e) {
            if (e.getSource().equals(c_tree)) {
                if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_SPACE) {
                    TreePath path = c_tree.getSelectionPath();
                    if (path != null) {
                        Object o = path.getLastPathComponent();
                        if (o != null && c_tree.getModel().isLeaf(o)) {
                            //user clicked a leaf, select it!
                            fromComboBox =true;
                            System.out.println("hereleaf2");
                            setComboSelectedItem(o);
                            setSelectedItem(o);
                            //hide();
                        }
                    }
                }
            }
        }

        @Override
        public void focusGained(FocusEvent e) {

        }

        @Override
        public void focusLost(FocusEvent e) {
            //System.out.println("Focus Lost.");
            
            if (c_selectOnLossOfFocus) {
                System.out.println("Inside selectOnLossOfFocus");
                TreePath path = c_tree.getSelectionPath();
                if (path != null) {
                    Object o = path.getLastPathComponent();
                    if (o != null && c_tree.getModel().isLeaf(o)) {
                        //user clicked a leaf, select it!
                        fromComboBox = true;
                        System.out.println("hereleaf3");
                        setComboSelectedItem(o);
                        setSelectedItem(o);
                    }
                }
               hide();
            }
            //create an else if for if the left or right arrow keys caused a loss of focus
            else{
                c_tree.requestFocus();
                c_tree.setVisible(true);
            }
            
        }

        @Override
        public void popupMenuCanceled(PopupMenuEvent e) {
        }

        @Override
        public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {

        }

        @Override
        public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
        }

        protected void togglePopup() {
            if (isVisible()) {
                hide();
            } else {
                show();
            }
        }

        @Override
        public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
            //begin loading the kids, so show a friendly wait cursor
        }

        @Override
        public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException {
            //do nothing
        }

        @Override
        public void treeExpanded(TreeExpansionEvent event) {
            //done loading the kids
            treeExpanding = true;
            //resize                     
            updatePopup();
            treeExpanding = false;

        }

        @Override
        public void treeCollapsed(TreeExpansionEvent event) {
            //resize
            updatePopup();
        }
    }
    public boolean fromComboBox = false;

    //special ui classes for creating the popup, it's an ugly hack.
    class MetalTreeComboBoxUI extends MetalComboBoxUI {

        @Override
        protected ComboPopup createPopup() {
            return new TreePopup(comboBox);
        }
    }

//    class MotifTreeComboBoxUI extends MotifComboBoxUI {
//
//        private static final long serialVersionUID = 1L;
//
//        @Override
//        protected ComboPopup createPopup() {
//            return new TreePopup(comboBox);
//        }
//    }

    class BasicTreeComboBoxUI extends BasicComboBoxUI {

        @Override
        protected ComboPopup createPopup() {
            return new TreePopup(comboBox);
        }
    }
}
