/**
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 * Copyright (C)
 * <a href="http://www.inab.org">Spanish National Institute of Bioinformatics (INB)</a>
 * <a href="http://www.bsc.es">Barcelona Supercomputing Center (BSC)</a>
 * <a href="http://inb.bsc.es">Computational Node 6</a>
 */

package org.inb.biomoby.central.gui;

import java.awt.Point;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collection;
import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import org.inb.biomoby.central.gui.dnd.ObjectTypeTransferable;
import org.inb.biomoby.central.gui.model.MobyPropertiesTableModel;
import org.inb.biomoby.central.gui.renderer.CardinalityTableCellEditor;
import org.inb.biomoby.central.gui.renderer.CardinalityTableCellRenderer;
import org.inb.biomoby.central.gui.renderer.ObjectTypeTableCellRenderer;
import org.inb.biomoby.central.gui.renderer.PropertyNameTableCellEditor;
import org.inb.biomoby.central.gui.renderer.PropertyNameTableCellRenderer;
import org.inb.biomoby.shared.registry.ObjectType;
import org.inb.swing.ImageLoader;

/**
 * Abstract BioMoby properties table implementation.
 * The implementation is independent from any object the edited properties could belong to.
 *
 * @author Dmitry Repchevsky
 */

public abstract class AbstractMobyPropertyTable extends JTable implements DropTargetListener, MouseListener
{
    public AbstractMobyPropertyTable()
    {
        super();

        setFillsViewportHeight(true);
        putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
        getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        TableColumn typeNameColumn = columnModel.getColumn(0);
        typeNameColumn.setCellRenderer(new ObjectTypeTableCellRenderer());

        TableColumn propertyNameColumn = columnModel.getColumn(1);
        propertyNameColumn.setCellRenderer(new PropertyNameTableCellRenderer());
        propertyNameColumn.setCellEditor(new PropertyNameTableCellEditor());

        TableColumn cardinalityColumn = columnModel.getColumn(2);
        cardinalityColumn.setCellRenderer(new CardinalityTableCellRenderer());
        cardinalityColumn.setCellEditor(new CardinalityTableCellEditor());

        getTableHeader().setReorderingAllowed(false);
    }

    @Override
    protected abstract MobyPropertiesTableModel createDefaultDataModel();
    
    @Override
    public MobyPropertiesTableModel getModel()
    {
        return (MobyPropertiesTableModel) super.getModel();
    }

    // BUG: 4330950 workaround
    @Override
    public void columnMarginChanged(ChangeEvent e)
    {
        cancelCellEditing();

        super.columnMarginChanged(e);
    }

    @Override
    public void editingCanceled(ChangeEvent e)
    {
        cancelCellEditing();

        super.editingCanceled(e);
    }

    private void cancelCellEditing()
    {
        if (editingColumn == 1)
        {
            TableCellEditor editor = getCellEditor();

            if (editor != null)
            {
                ObjectType property = (ObjectType)editor.getCellEditorValue();
                String oldPropertyName = property.getArticleName();

                if (oldPropertyName == null || oldPropertyName.isEmpty())
                {
                    MobyPropertiesTableModel model = getModel();
                    model.remove(model.getRowCount() - 1);
                }
            }
        }
    }

    @Override
    public void tableChanged(TableModelEvent e)
    {
        removeEditor();

        super.tableChanged(e);
    }

    private void popup(MouseEvent e)
    {
        Point p = e.getPoint();
        final int row = rowAtPoint(p);

        JPopupMenu popup = new JPopupMenu();

        JMenuItem addMenuItem = new JMenuItem("add a new property", new ImageIcon(ImageLoader.load("images/add.png")));

        addMenuItem.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                final MobyPropertiesTableModel model = getModel();

                ObjectType objectType = new ObjectType("Object");
                objectType.setArticleName("");
                model.addProperty(objectType);

                // it looks like a bug in jtable if we start editing immediately...
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        editCellAt(model.getRowCount() - 1, 1);
                        getEditorComponent().requestFocusInWindow();
                    }
                });
            }
        });

        popup.add(addMenuItem);

        if (row >= 0)
        {
            this.getSelectionModel().setSelectionInterval(row, row);

            if (getModel().isCellEditable(row, 0))
            {
                JMenuItem removeMenuItem = new JMenuItem("delete the property", new ImageIcon(ImageLoader.load("images/delete.png")));

                removeMenuItem.addActionListener(new ActionListener()
                {
                    @Override
                    public void actionPerformed(ActionEvent e)
                    {
                        MobyPropertiesTableModel model = getModel();
                        model.remove(row);
                    }
                });

                popup.add(removeMenuItem);
            }
        }

        popup.show(this, e.getX(), e.getY());
    }

    @Override
    public void dragEnter(DropTargetDragEvent dtde)
    {
        Transferable tr = dtde.getTransferable();

        if (!tr.isDataFlavorSupported(ObjectTypeTransferable.OBJECT_TYPE))
        {
            dtde.rejectDrag();
        }
    }

    @Override public void dragOver(DropTargetDragEvent dtde) {}
    @Override public void dropActionChanged(DropTargetDragEvent dtde) {}
    @Override public void dragExit(DropTargetEvent dte) {}

    @Override
    public void drop(DropTargetDropEvent dtde)
    {
        Transferable tr = dtde.getTransferable();

        if (tr.isDataFlavorSupported(ObjectTypeTransferable.OBJECT_TYPE))
        {
            try
            {
                MobyPropertiesTableModel model = getModel();

                Collection<ObjectType> types = (Collection<ObjectType>)tr.getTransferData(ObjectTypeTransferable.OBJECT_TYPE);

                for (ObjectType type : types)
                {
                    model.addProperty(type);
                }

                // if there is only one property droped - start edit its name
                if (types.size() == 1)
                {
                    editCellAt(model.getRowCount() - 1, 1);
                    getEditorComponent().requestFocusInWindow();
                }
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        if (e.isPopupTrigger())
        {
            popup(e);
        }
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        if (e.isPopupTrigger())
        {
            popup(e);
        }
    }

    @Override public void mouseClicked(MouseEvent e) {}
    @Override public void mouseEntered(MouseEvent e) {}
    @Override public void mouseExited(MouseEvent e) {}
}
