package de.renew.logging.gui;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractAction;
import javax.swing.ComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.SingleCDockable;
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
import bibliothek.gui.dock.common.event.CVetoClosingListener;

import de.renew.gui.ComponentRenderer;
import de.renew.gui.JComponentCellEditor;
import de.renew.windowmanagement.Workbench;
import de.renew.windowmanagement.WorkbenchImpl;


/**
* GUI for showing the trace messages produced during the
* simulation of petri nets
*
* @author Sven Offermann
*/
public class LoggingFrame {
    /**
     * {@link org.apache.log4j.Logger}
     */
    private static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(LoggingFrame.class);
    /**
     * {@link LoggingController}
     */
    private LoggingController _controller;
    /**
     * {@link JTable}
     */

    private JTable _loggerTable;
    /**
     * {@link JScrollPane}
     */

    private JScrollPane _scrollPane;
    /**
     * {@link JComboBox}
     */

    private JComboBox _loggerComboBox;
    /**
     * {@link JCheckBox}
     */

    private JCheckBox _updateCheckBox;
    /**
     * {@link JPanel}
     */

    private JPanel _panel;

    /**
     * Creates a LoggingFrame, initializes it and makes it closable.
     * @param controller {@link LoggingController}
     * @param loggerNames Array of loggers.
     * @param workbench {@link Workbench}
     */
    public LoggingFrame(LoggingController controller, String[] loggerNames, Workbench workbench) {
        this._controller = controller;
        _panel = new JPanel();
        initGUI(loggerNames);
        DefaultSingleCDockable dockable = new DefaultSingleCDockable("Simulation Trace", _panel);
        dockable.setCloseable(true);
        dockable.addVetoClosingListener(new CVetoClosingListener() {
            @Override
            public void closing(CVetoClosingEvent arg0) {}

            @Override
            public void closed(CVetoClosingEvent arg0) {
                SingleCDockable dockable = (SingleCDockable) arg0.getDockable(0);
                dockable.getControl().removeDockable(dockable);

            }
        });
        workbench.addViewWindow(dockable, WorkbenchImpl.UPPER_LEFT_GROUP);
    }

    /**
     * initialize the Logger-Gui
     * @param loggerNames array with the loggers.
     */
    public void initGUI(String[] loggerNames) {
        try {
            _loggerComboBox = new JComboBox(loggerNames);
            _loggerComboBox.addActionListener(new ActionListener() { //TODO: use lambda-expression
                @Override
                public void actionPerformed(ActionEvent e) {
                    JComboBox cb = (JComboBox) e.getSource();
                    String loggerName = (String) cb.getSelectedItem();
                    _controller.changeLogger(loggerName);
                }
            });

            _scrollPane = new JScrollPane();

            _loggerTable = new JTable(new TableModel(true)) {
                @Override
                public TableCellRenderer getCellRenderer(int row, int column) {
                    TableColumn tableColumn = getColumnModel().getColumn(column);
                    TableCellRenderer renderer = tableColumn.getCellRenderer();

                    if (renderer == null) {
                        Class<?> c = getColumnClass(column);
                        if (c.equals(Object.class)) {
                            Object o = getValueAt(row, column);
                            if (o != null) {
                                c = getValueAt(row, column).getClass();
                            }
                        }
                        renderer = getDefaultRenderer(c);
                    }

                    return renderer;
                }

                @Override
                public TableCellEditor getCellEditor(int row, int column) {
                    TableColumn tableColumn = getColumnModel().getColumn(column);
                    TableCellEditor editor = tableColumn.getCellEditor();

                    if (editor == null) {
                        Class<?> c = getColumnClass(column);
                        if (c.equals(Object.class)) {
                            Object o = getValueAt(row, column);
                            if (o != null) {
                                c = getValueAt(row, column).getClass();
                            }
                        }
                        editor = getDefaultEditor(c);
                    }

                    return editor;
                }
            };

            TableCellRenderer defaultRenderer = _loggerTable.getDefaultRenderer(JComponent.class);
            _loggerTable
                .setDefaultRenderer(JComponent.class, new ComponentRenderer(defaultRenderer));
            TableCellEditor defaultEditor = _loggerTable.getDefaultEditor(JComponent.class);
            _loggerTable
                .setDefaultEditor(JComponent.class, new JComponentCellEditor(defaultEditor, null));

            //            setName("Simulation Trace");
            BorderLayout thisLayout = new BorderLayout();
            _panel.setLayout(thisLayout);
            // this.setSize(new java.awt.Dimension(345,262));
            _panel.add(_loggerComboBox, BorderLayout.NORTH);

            _panel.add(_scrollPane, BorderLayout.CENTER);

            _scrollPane.add(_loggerTable);
            _scrollPane.setViewportView(_loggerTable);

            _updateCheckBox = new JCheckBox("permanent update");
            _updateCheckBox.setSelected(true);
            _updateCheckBox.addActionListener(new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    _controller.setPermanentUpdate(_updateCheckBox.isSelected());
                }
            });
            _panel.add(_updateCheckBox, BorderLayout.SOUTH);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    /**
     *
     * @return {@link ComboBoxModel}
     */
    public ComboBoxModel getComboBoxModel() {
        return this._loggerComboBox.getModel();
    }

    /**
     *
     * @param dm {@link LoggerTableModel}
     */
    public void setTableModel(LoggerTableModel dm) {
        if (dm != null) {
            this._loggerTable.setModel(dm);
            dm.addTableModelListener(new TableModelListenerImpl());
            updateRowHeights();
        }
    }

    /**
     *
     * @return {@link LoggerTableModel}
     */
    public LoggerTableModel getTableModel() {
        return (LoggerTableModel) this._loggerTable.getModel();
    }

    /**
     * @return the selected Item of the combobox.
     */
    public String getSelectedLoggerName() {
        return (String) _loggerComboBox.getSelectedItem();
    }

    /**
     * Updates the heights of the rows.
     * Iterates over the {@link TableModel} found in the {@link JTable} of this object.
     * Uses the preferred Size given by the {@link JComponent} found in the Table.
     */
    public void updateRowHeights() {
        TableModel tm = (TableModel) this._loggerTable.getModel();
        for (int x = 0; x < tm.getRowCount(); x++) {
            Object o = tm.getValueAt(x, 0);
            if (o instanceof JComponent) {
                JComponent c = (JComponent) o;
                this._loggerTable.setRowHeight(x, c.getPreferredSize().height);
            }
        }
    }

    /**
     * Set the used row height for the Frame.
     * @param row integer
     */
    public void updateRowHeight(int row) {
        TableModel tm = (TableModel) this._loggerTable.getModel();
        Object o = tm.getValueAt(row, 0);
        if (o instanceof JComponent) {
            JComponent c = (JComponent) o;
            this._loggerTable.setRowHeight(row, c.getPreferredSize().height);
        }
    }

    /**
     * implementation of the TableModelListener
     */
    private class TableModelListenerImpl implements TableModelListener {
        @Override
        public void tableChanged(TableModelEvent e) {
            if ((e.getType() == TableModelEvent.INSERT)
                || (e.getType() == TableModelEvent.UPDATE)) {
                for (int x = e.getFirstRow(); x <= e.getLastRow(); x++) {
                    updateRowHeight(x);
                }
            }
        }
    }
}