package de.duehl.swing.ui.tables;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;

/**
 * The ButtonColumn class provides a renderer and an editor that looks like a JButton. The renderer
 * and editor will then be used for a specified column in the table. The TableModel will contain
 * the String to be displayed on the button.
 *
 * The button can be invoked by a mouse click or by pressing the space bar when the cell has focus.
 * Optionaly a mnemonic can be set to invoke the button. When the button is invoked the provided
 * Action is invoked. The source of the Action will be the table. The action command will contain
 * the model row number of the button that was clicked.
 *
 * https://tips4java.wordpress.com/2009/07/12/table-button-column/
 * https://github.com/tips4java/tips4java/blob/main/source/ButtonColumn.java
 *
 * Deutlich verändert und angepasst von Christian Dühl.
 */

public class ButtonColumn extends AbstractCellEditor
        implements TableCellRenderer, TableCellEditor, ActionListener, MouseListener {
    private static final long serialVersionUID = 1L;

    /** Die Tabelle mit dem Button. */
    private JTable table;

    /** Wird durch den Buttonklick aufgerufen. */
    private TableButtonReactor buttonReactor;

    private int mnemonic;
    private Border originalBorder;
    private Border focusBorder;

    private JButton renderButton;
    private JButton editButton;
    private Object editorValue;
    private boolean isButtonColumnEditor;

    /**
     * Create the ButtonColumn to be used as a renderer and editor. The renderer and editor will
     * automatically be installed on the TableColumn of the specified column.
     *
     * @param table
     *            Die Tabelle mit dem Button.
     * @param buttonReactor
     *            Wird durch den Buttonklick aufgerufen.
     * @param column
     *            the column to which the button renderer/editor is added
     */
    public ButtonColumn(JTable table, TableButtonReactor buttonReactor, int column) {
        this.table = table;
        this.buttonReactor = buttonReactor;

        renderButton = new JButton();
        editButton = new JButton();
        editButton.setFocusPainted(false);
        editButton.addActionListener(this);
        originalBorder = editButton.getBorder();
        setFocusBorder(new LineBorder(Color.BLUE));

        TableColumnModel columnModel = table.getColumnModel();
        columnModel.getColumn(column).setCellRenderer(this);
        columnModel.getColumn(column).setCellEditor(this);
        table.addMouseListener(this);
    }

    /**
     * Get foreground color of the button when the cell has focus
     *
     * @return the foreground color
     */
    public Border getFocusBorder() {
        return focusBorder;
    }

    /**
     * The foreground color of the button when the cell has focus
     *
     * @param focusBorder
     *            the foreground color
     */
    public void setFocusBorder(Border focusBorder) {
        this.focusBorder = focusBorder;
        editButton.setBorder(focusBorder);
    }

    public int getMnemonic() {
        return mnemonic;
    }

    /**
     * The mnemonic to activate the button when the cell has focus
     *
     * @param mnemonic
     *            the mnemonic
     */
    public void setMnemonic(int mnemonic) {
        this.mnemonic = mnemonic;
        renderButton.setMnemonic(mnemonic);
        editButton.setMnemonic(mnemonic);
    }

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
            int row, int column) {
        if (value == null) {
            editButton.setText("");
            editButton.setIcon(null);
        }
        else if (value instanceof Icon) {
            editButton.setText("");
            editButton.setIcon((Icon) value);
        }
        else {
            editButton.setText(value.toString());
            editButton.setIcon(null);
        }

        this.editorValue = value;
        return editButton;
    }

    @Override
    public Object getCellEditorValue() {
        return editorValue;
    }

    //
    // Implement TableCellRenderer interface
    //

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column) {
        if (isSelected) {
            renderButton.setForeground(table.getSelectionForeground());
            renderButton.setBackground(table.getSelectionBackground());
        }
        else {
            renderButton.setForeground(table.getForeground());
            renderButton.setBackground(UIManager.getColor("Button.background"));
        }

        if (hasFocus) {
            renderButton.setBorder(focusBorder);
        }
        else {
            renderButton.setBorder(originalBorder);
        }

        if (value == null) {
            renderButton.setText("");
            renderButton.setIcon(null);
        }
        else if (value instanceof Icon) {
            renderButton.setText("");
            renderButton.setIcon((Icon) value);
        }
        else {
            renderButton.setText(value.toString());
            renderButton.setIcon(null);
        }

        return renderButton;
    }

    //
    // Implement ActionListener interface
    //

    /**
     * The button has been pressed. Stop editing and invoke the custom Action
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        int row = table.convertRowIndexToModel(table.getEditingRow());
        fireEditingStopped();

        buttonReactor.reactOnButtonClick(row);
    }

    //
    // Implement MouseListener interface
    //

    /**
     * When the mouse is pressed the editor is invoked. If you then then drag the mouse to another
     * cell before releasing it, the editor is still active. Make sure editing is stopped when the
     * mouse is released.
     */
    @Override
    public void mousePressed(MouseEvent e) {
        if (table.isEditing() && table.getCellEditor() == this)
            isButtonColumnEditor = true;
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        if (isButtonColumnEditor && table.isEditing())
            table.getCellEditor().stopCellEditing();

        isButtonColumnEditor = false;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }

}
