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.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import usda.weru.mcrew.gui.FileDropDown_n;
import usda.weru.util.tree.WepsTreeNodeFile;
import usda.weru.util.tree.WepsTreeComboBox;
import usda.weru.util.tree.WepsTreeModel;
import usda.weru.util.tree.WepsTreeModelCrop;
import usda.weru.util.tree.WepsTreeModelOp;
import usda.weru.util.tree.WepsTreeNode;

/**
 * * @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
    }
    
    /**
     * 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 WepsTreeModel c_model;


    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);
        this.addKeyListener(new KeyListener() {

            @Override
            public void keyPressed(KeyEvent e) {
                c_table.keyPressed(e);
            }

            @Override
            public void keyReleased(KeyEvent e) {
              if (e.getKeyCode() == KeyEvent.VK_DELETE) {
                    c_table.keyReleased(e);
                }
            }

            @Override
            public void keyTyped(KeyEvent e) {
                c_table.keyTyped(e);
            }
        });

        // MEH
        if (c_type == FileType.Operation) {
            c_model = new WepsTreeModelOp(g_dropdown, true);
        } else {
            c_model = new WepsTreeModelCrop(g_dropdown, true);            
        }
        g_dropdown.setModel(c_model);

        // let the invoker expand paths, if desired.
        // if invoked here, it confuses the LazyLoadingTreeController.
        //g_dropdown.getTree().expandPath(c_model.getPathToProject());
    }

    public void setTable(Table table) {
        c_table = table;
        if (c_table == null) {
            c_initialEvent = null;
        }
    }

    public void setMouseAdapter(Table.MyMouseAdapter adapter) {
        c_mouseAdapter = adapter;
    }

    public void setFile(TFile file) {
        c_file = file;
        c_root = new InternalRootNode(c_file);
        refreshFiles();
    }


    public void refreshFiles() {
        if (c_type == FileType.Operation) {
            c_model = new WepsTreeModelOp(g_dropdown, true);
        } else {
            c_model = new WepsTreeModelCrop(g_dropdown, true);            
        }
        c_model.refresh();
        g_dropdown.setModel(c_model);
        
        //if one zip 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 WepsTreeNodeFile) {
                TFile file = ((WepsTreeNodeFile) node).getNodeData();
                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
     */
    @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);
        }
    }

    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 WepsTreeNodeFile) {
            return ((WepsTreeNodeFile) item).getNodeData();
        } 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;
    }

    @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());
        }
    }

    @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);
        }
    }

    @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() {
            if (c_invokeLater != null) {
                switch (c_invokeLater) {
                    case Popup:
                        if (g_dropdown.isShowing() && !g_dropdown.isPopupVisible()) {
                            g_dropdown.showPopup();
                        }
                        break;
                    case Drilldown:
                        
                        c_table.changeHoldDC(false);
                        c_table.getJCTable().cancelEdit(true);
                        g_drilldown.doClick();
//                        System.out.println("here"+ g_drilldown.isVisible());
                        break;
                    case Menu:
                        c_mouseAdapter.mousePressed((MouseEvent) c_initialEvent);

                        break;
                }
                c_invokeLater = null;
            }
        }
    }

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

            TFile file = null;
            try {
                if (selectedItem instanceof WepsTreeNode) {
                    file = ((WepsTreeNode) selectedItem).getNodeData();
                }
            } catch (IOException ignore) {
                g_dropdown.getComboBox().addItem("no crop");
                g_dropdown.getComboBox().setSelectedItem("no crop");
            }
            
            if (file != null) {
                c_table.addColumnData(file);
                c_table.getJCTable().commitEdit(true);
            }
        }
    }

    private class InternalRootNode extends WepsTreeNodeFile {

        private static final long serialVersionUID = 1L;

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

        }

    }
}
