package usda.weru.mcrew;

import com.klg.jclass.cell.JCCellEditor;
import com.klg.jclass.cell.JCCellEditorListener;
import com.klg.jclass.cell.JCCellEditorSupport;
import com.klg.jclass.cell.JCCellInfo;
import com.klg.jclass.cell.JCComponentCellRenderer;
import com.klg.jclass.cell.JCKeyModifier;
import de.schlichtherle.truezip.file.TFile;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Comparator;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.apache.log4j.Logger;
import usda.weru.mcrew.gui.FileDropDown_n;
import usda.weru.util.FileTreeNode;
import usda.weru.util.WepsTreeComboBox;

/**
 * * @author Joseph Levin <joelevin@weru.ksu.edu>
 */
public class FileDropDown extends FileDropDown_n implements JCComponentCellRenderer, JCCellEditor, PropertyChangeListener {

    private static final long serialVersionUID = 1L;

    /**
     *
     */
    public enum FileType {

        /**
         *
         */
        Crop,
        /**
         *
         */
        Operation
    }

    /**
     *
     */
    public enum Invoke {

        /**
         *
         */
        Menu,
        /**
         *
         */
        Popup,
        /**
         *
         */
        Drilldown
    }
    private static final Logger logger = Logger.getLogger(FileDropDown.class);
    /**
     * This dummy component is used when the data is null (aka no value is allowed)).
     */
    protected final JComponent DUMMY = new JLabel();
    /**
     * Key strokes to capture
     */
    private static final JCKeyModifier[] KEY_MODIFIERS = {new JCKeyModifier(KeyEvent.VK_DOWN, JCKeyModifier.ALL), new JCKeyModifier(KeyEvent.VK_UP, JCKeyModifier.ALL), new JCKeyModifier(KeyEvent.VK_LEFT, JCKeyModifier.ALL), new JCKeyModifier(KeyEvent.VK_RIGHT, JCKeyModifier.ALL)};

    /**
     *
     */
    protected FileType c_type;

    /**
     *
     */
    protected TFile c_file;

    /**
     *
     */
    protected Table c_table;
    private Table.MyMouseAdapter c_mouseAdapter;

    /**
     *
     */
    protected FileFilter c_fileFilter;

    /**
     *
     */
    protected static Comparator<java.io.File> c_fileComparator;

    /**
     *
     */
    protected boolean c_isEditor;

    /**
     *
     */
    protected boolean c_selected;

    /**
     *
     */
    protected AWTEvent c_initialEvent;

    /**
     *
     */
    protected Invoke c_invokeLater;
    /**
     * When true, the paint method will determine if the user clicked on the button or dropdown.
     */
    protected boolean c_testUserEntry;

    /**
     *
     */
    protected Object c_data;

    /**
     *
     */
    protected JCCellEditorSupport c_support = new JCCellEditorSupport();

    private InternalRootNode c_root;

    /**
     *
     */
    protected DefaultTreeModel c_model;

    /**
     *
     * @param type
     */
    public FileDropDown(FileType type) {
        c_type = type;
        g_dropdown.setRootVisible(false);

        if (type != null) {
            g_dropdown.addPropertyChangeListener(this);
            g_dropdown.setSelectOnLossOfFocus(false);
        }

        DUMMY.setOpaque(true);

    }

    /**
     *
     * @param l
     */
    @Override
    public void addFocusListener(FocusListener l) {
        //super.addFocusListener(l);
    }

    /**
     *
     * @param l
     */
    @Override
    public void removeFocusListener(FocusListener l) {
        //super.removeFocusListener(l);
    }

    /**
     *
     * @param table
     */
    public void setTable(Table table) {
        c_table = table;
        if (c_table == null) {
            c_initialEvent = null;
        }
    }

    /**
     *
     * @param adapter
     */
    public void setMouseAdapter(Table.MyMouseAdapter adapter) {
        c_mouseAdapter = adapter;
    }

    /**
     *
     * @param file
     */
    public void setFile(TFile file) {
        c_file = file;
        refreshFiles();
    }

    /**
     *
     */
    public void refreshFiles() {
        c_root = new InternalRootNode(c_file);
        c_model = new DefaultTreeModel(c_root);
        g_dropdown.setModel(c_model);

        //if one zup file lives with folders we expand it.
        TreeNode toExpand = null;
        for (int i = 0; i < c_root.getChildCount(); i++) {
            TreeNode node = c_root.getChildAt(i);
            if (node instanceof FileTreeNode) {
                TFile file = ((FileTreeNode) node).getFile();
                if (!file.isDirectory()) {
                    return;
                } else if (file.getName().toLowerCase().endsWith(".zip")) {
                    if (toExpand == null) {
                        toExpand = node;
                    } else {
                        return;
                    }
                }
            }
        }
        if (toExpand != null) {
            c_model.getPathToRoot(toExpand);
            g_dropdown.getTree().expandPath(new TreePath(c_model.getPathToRoot(toExpand)));
        }
    }


    /*
     * Hack to fix the "jumping" thet occurs when switched from renderer to editor
     */
    /**
     *
     * @param x
     * @param y
     * @param width
     * @param height
     */
    @Override
    public void setBounds(int x, int y, int width, int height) {
        if (!c_isEditor) {
            super.setBounds(x, y, width + 1, height);
        } else {
            super.setBounds(x + 2, y + 1, width - 4, height);
        }
    }

    /**
     *
     * @param info
     * @param o
     */
    protected void init(JCCellInfo info, Object o) {
        c_data = o;

        if (c_type != null) {
            //editor
            g_dropdown.getComboBox().removeAllItems();
            g_dropdown.getComboBox().addItem(o);
            g_dropdown.getComboBox().setSelectedItem(o);
            g_dropdown.getTree().scrollRowToVisible(-1);
            g_dropdown.getTree().setSelectionRow(-1);
        } else {
            //renderer           
            g_dropdown.getComboBox().removeAllItems();
            g_dropdown.getComboBox().addItem(o);
            g_dropdown.getComboBox().setSelectedItem(o);
        }

        Color background = null;
        Color foreground = null;

        if (!c_selected) {
            background = info.getBackground();
            foreground = info.getForeground();
        } else {
            background = info.getSelectedBackground();
            foreground = info.getSelectedForeground();
        }

        setBackground(background);
        setForeground(foreground);
        g_dropdown.setBackground(background);
        g_dropdown.setForeground(foreground);
        g_drilldown.setBackground(background);
        g_drilldown.setForeground(foreground);
        g_dropdown.getTree().setBackground(Color.WHITE);
    }

    //Cell Render Code
    @Override
    public Component getRendererComponent(JCCellInfo info, Object o, boolean selected) {
        c_isEditor = false;
        if (o == null) {
            DUMMY.setBackground(selected ? info.getSelectedBackground() : MCREWConfig.getDisabledColor());
            DUMMY.setForeground(selected ? info.getSelectedForeground() : info.getForeground());
            return DUMMY;
        }
        if (o == ManageData.EMPTY_OBJECT) {
            o = "";
        }
        c_selected = selected;
        init(info, o);

        return this;
    }

    @Override
    public void initialize(AWTEvent ev, JCCellInfo info, Object o) {
        c_isEditor = true;

        if (o == null) {
            g_dropdown.setVisible(false);
            g_drilldown.setVisible(false);
            //o = null;
        } else {
            g_dropdown.setVisible(true);
            g_drilldown.setVisible(true);
        }

        if (o == ManageData.EMPTY_OBJECT) {
            o = "";
        }

        c_initialEvent = ev;

        init(info, o);

        c_testUserEntry = true;
        c_invokeLater = null;

        if (ev instanceof MouseEvent) {
            MouseEvent me = (MouseEvent) ev;
            if (me.getClickCount() > 1) {
                c_table.addColumnData();
            } else if (me.getButton() != MouseEvent.BUTTON1) {
                c_table.getJCTable().cancelEdit(true);
            }
        }
    }

    @Override
    public Component getComponent() {
        return this;
    }

    //TODO: Review that these methods don't need to be implemented better
    @Override
    public Object getCellEditorValue() {
        Object item = g_dropdown.getSelectedItem();
        if (item instanceof FileTreeNode) {
            return ((FileTreeNode) item).getFile();
        } else {
            return null;
        }
    }

    @Override
    public boolean stopCellEditing() {
        return true;
    }

    @Override
    public boolean isModified() {
        return false;
    }

    @Override
    public void cancelCellEditing() {

    }

    @Override
    public JCKeyModifier[] getReservedKeys() {
        return KEY_MODIFIERS != null ? Arrays.copyOf(KEY_MODIFIERS, KEY_MODIFIERS.length) : new JCKeyModifier[0];
    }

    @Override
    public void addCellEditorListener(JCCellEditorListener listener) {
        c_support.addCellEditorListener(listener);
    }

    @Override
    public void removeCellEditorListener(JCCellEditorListener listener) {
        c_support.removeCellEditorListener(listener);
    }

    synchronized FileFilter getFileFilter() {
        if (c_fileFilter == null) {
            c_fileFilter = new FileFilter() {

                @Override
                public boolean accept(java.io.File file) {
                    if (file.isDirectory() && file.getName().toLowerCase().startsWith(".svn")) {
                        return false;
                    } else if (file.isDirectory()) {
                        return true;
                    } else if (c_type == FileType.Operation) {
                        return file.getName().toLowerCase().trim().endsWith(".oprn");
                    } else if (c_type == FileType.Crop) {
                        return file.getName().toLowerCase().trim().endsWith(".crop");
                    } else {
                        return true;
                    }
                }
            };
        }
        return c_fileFilter;
    }

    /**
     *
     * @param g
     */
    @Override
    public void paint(java.awt.Graphics g) {
        if (c_data == null) {

            DUMMY.setBackground(MCREWConfig.getDisabledColor());
            DUMMY.paint(g);
            cancelCellEditing();
            return;
        }
        super.paint(g);
        if (c_testUserEntry) {
            if (c_initialEvent != null && c_initialEvent instanceof MouseEvent) {
                MouseEvent me = (MouseEvent) c_initialEvent;
                try {
                    int x = getLocationOnScreen().x;
                    int y = getLocationOnScreen().y;
                    int mx = me.getXOnScreen();
                    int my = me.getYOnScreen();
                    x = mx - x;
                    y = my - y;
                    Component comp = getComponentAt(x, y);
                    if (comp == g_drilldown) {
                        c_invokeLater = Invoke.Drilldown;
                    } else if (comp == g_dropdown) {
                        if (me.getButton() == MouseEvent.BUTTON1) {
                            if (x > g_dropdown.getWidth() - 25) {
                                c_invokeLater = Invoke.Popup;
                            } else {
                                c_table.getJCTable().cancelEdit(true);
                            }
                        } else if (me.getButton() == MouseEvent.BUTTON2) {
                            c_invokeLater = Invoke.Menu;
                        }
                    }
                } catch (Exception e) {
                    //ignore
                }
            }
            c_testUserEntry = false;
        }
        if (c_invokeLater != null) {
            SwingUtilities.invokeLater(new Invoker());
        }
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void dropdownMouseClicked(MouseEvent evt) {
        if (evt.getButton() != MouseEvent.BUTTON1) {
            c_table.getJCTable().cancelEdit(true);
            MouseEvent me = c_mouseAdapter.translateEvent(evt); //new MouseEvent ((Component) initialEvent.getSource(), evt.getID(), evt.getWhen(), evt.getModifiers(), x, y, evt.getClickCount(), true, evt.getButton());
            c_table.requestFocusInWindow();
            c_mouseAdapter.mousePressed(me);
        } else if (evt.getClickCount() > 1) {
            c_table.addColumnData();
        } else {
            //c_table.getJCTable().cancelEdit(true);
        }
    }

    /**
     *
     * @param evt
     */
    @Override
    protected void drilldownActionPerformed(ActionEvent evt) {
        c_table.getJCTable().commitEdit(true);
        c_mouseAdapter.openDrillDownScreen();
    }

    /*
     * Invokes the correct action for the user's entry into the editor.
     */
    class Invoker implements Runnable {

        @Override
        public void run() {
            switch (c_invokeLater) {
                case Popup:
                    if (g_dropdown.isShowing() && !g_dropdown.isPopupVisible()) {
                        g_dropdown.showPopup();
                        //g_dropdown.requestFocusInWindow();
                    }
                    break;
                case Drilldown:
                    c_table.getJCTable().cancelEdit(true);
                    g_drilldown.doClick();
                    break;
                case Menu:
                    c_mouseAdapter.mousePressed((MouseEvent) c_initialEvent);

                    break;
            }
            c_invokeLater = null;
        }
    }

    /**
     *
     * @param event
     */
    @Override
    public void propertyChange(PropertyChangeEvent event) {
        if (WepsTreeComboBox.PROP_SELECTED_ITEM.equals(event.getPropertyName())) {
            Object selectedItem = event.getNewValue();

            if (selectedItem instanceof FileTreeNode) {
                TFile file = ((FileTreeNode) selectedItem).getFile();
                c_table.addColumnData(file);
                c_table.getJCTable().commitEdit(true);
            }
        }
    }

    private class InternalRootNode extends FileTreeNode {

        private static final long serialVersionUID = 1L;

        public InternalRootNode(TFile file) {
            super(file, getFileFilter());

        }

    }
}
