/*
 * PriorsPanel.java
 *
 * Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.beauti.priorsPanel;

import dr.app.beauti.BeautiFrame;
import dr.app.beauti.BeautiPanel;
import dr.app.beauti.components.hpm.HierarchicalModelComponentOptions;
import dr.app.beauti.components.hpm.HierarchicalPhylogeneticModel;
import dr.app.beauti.components.linkedparameters.LinkedParameter;
import dr.app.beauti.components.linkedparameters.LinkedParameterComponentOptions;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.ClockModelGroup;
import dr.app.beauti.options.Operator;
import dr.app.beauti.options.Parameter;
import dr.app.beauti.types.ClockType;
import dr.app.beauti.types.PriorType;
import dr.app.beauti.util.PanelUtils;
import dr.app.gui.table.TableEditorStopper;
import dr.util.NumberFormatter;
import jam.framework.Exportable;
import jam.table.TableRenderer;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Andrew Rambaut
 * @author Alexei Drummond
 * @version $Id: PriorsPanel.java,v 1.9 2006/09/05 13:29:34 rambaut Exp $
 */
public class PriorsPanel extends BeautiPanel implements Exportable {
    private static final long serialVersionUID = -2936049032365493416L;
    JScrollPane scrollPane = new JScrollPane();
    JTable priorTable = null;
    PriorTableModel priorTableModel = null;
    JLabel messageLabel = new JLabel();
    JButton linkButton = null;
    JButton hpmButton = null;
    JButton unlinkButton = null;

    public ArrayList<Parameter> parameters = new ArrayList<Parameter>();

    BeautiFrame frame = null;
    BeautiOptions options = null;

    boolean hasUndefinedPrior = false;
    boolean hasImproperPrior = false;
    boolean hasRate = false;

    private final boolean isDefaultOnly;

    public PriorsPanel(BeautiFrame parent, boolean isDefaultOnly) {
        this.frame = parent;
        this.isDefaultOnly = isDefaultOnly;

        priorTableModel = new PriorTableModel(this);
        priorTable = new JTable(priorTableModel);

        priorTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_LAST_COLUMN);
        priorTable.getTableHeader().setReorderingAllowed(false);
//        priorTable.getTableHeader().setDefaultRenderer(
//                new HeaderRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));

        priorTable.getColumnModel().getColumn(0).setCellRenderer(
                new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
        priorTable.getColumnModel().getColumn(0).setMinWidth(200);

        priorTable.getColumnModel().getColumn(1).setCellRenderer(
                new ButtonRenderer(SwingConstants.LEFT, new Insets(0, 8, 0, 8)));
        priorTable.getColumnModel().getColumn(1).setCellEditor(
                new ButtonEditor(SwingConstants.LEFT, new Insets(0, 8, 0, 8)));
        priorTable.getColumnModel().getColumn(1).setMinWidth(260);

        priorTable.getColumnModel().getColumn(2).setCellRenderer(
                new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
        priorTable.getColumnModel().getColumn(2).setMinWidth(30);


        priorTable.getColumnModel().getColumn(3).setCellRenderer(
                new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
        priorTable.getColumnModel().getColumn(3).setMinWidth(410);

        TableEditorStopper.ensureEditingStopWhenTableLosesFocus(priorTable);

        DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() {
            // override renderer preparation
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                           boolean hasFocus,
                                                           int row, int column)
            {
                // allow default preparation
                super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

                Parameter parameter = parameters.get(row);

                if (isSelected) {
                    setForeground(parameter.isLinked ? Color.LIGHT_GRAY : SystemColor.textHighlightText);
                } else {
                    setForeground(parameter.isLinked ? Color.GRAY : SystemColor.controlText);
                }
//                if (parameter.isLinked) {
//                    setFont(getFont().deriveFont(Font.ITALIC));
//                }
                return this;
            }
        };
        priorTable.getColumnModel().getColumn(0).setCellRenderer(renderer);
        priorTable.getColumnModel().getColumn(2).setCellRenderer(renderer);
        priorTable.getColumnModel().getColumn(3).setCellRenderer(renderer);

        scrollPane = new JScrollPane(priorTable,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

        scrollPane.setOpaque(false);

        Action setLinkedParametersAction = new AbstractAction("Link parameters together") {
            public void actionPerformed(ActionEvent actionEvent) {
                // Make list of selected parameters;
                int[] rows = priorTable.getSelectedRows();
                linkButtonPressed(rows);
            }
        };

        linkButton = new JButton(setLinkedParametersAction);
        linkButton.setVisible(true);
        linkButton.setEnabled(false);
        linkButton.setToolTipText("<html>Join the selected parameters so their values are<br>" +
                "kept the same. This will create a new controlling<br>" +
                "parameter which can be given a prior.");

        Action setHierarchicalAction = new AbstractAction("Link parameters into a hierarchical model") {
            public void actionPerformed(ActionEvent actionEvent) {
                // Make list of selected parameters;
                int[] rows = priorTable.getSelectedRows();
                hierarchicalButtonPressed(rows);
            }
        };

        hpmButton = new JButton(setHierarchicalAction);
        hpmButton.setVisible(true);
        hpmButton.setEnabled(false);
        hpmButton.setToolTipText(HierarchicalPhylogeneticModel.TIP_TOOL);

        Action setUnlinkAction = new AbstractAction("Unlink parameters") {
            public void actionPerformed(ActionEvent actionEvent) {
                // Make list of selected parameters;
                int[] rows = priorTable.getSelectedRows();
                unlinkButtonPressed(rows);
            }
        };

        unlinkButton = new JButton(setUnlinkAction);
        unlinkButton.setVisible(true);
        unlinkButton.setEnabled(false);
        unlinkButton.setToolTipText("<html>Unlink parameters joined as a linked parameter<br>" +
                "or hierarchical model. Will delete the enclosing<br>" +
                "model.");

        messageLabel.setText(getMessage());

        setOpaque(false);
        setLayout(new BorderLayout(0, 0));
        setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 12)));

        if (!isDefaultOnly) {
            add(new JLabel("Priors for model parameters and statistics:"), BorderLayout.NORTH);
        }

        add(scrollPane, BorderLayout.CENTER);

        if (!isDefaultOnly) {
            JPanel southPanel = new JPanel();
            southPanel.setLayout(new BorderLayout(0, 0));
            JToolBar toolBar1 = new JToolBar();
            toolBar1.setFloatable(false);
            toolBar1.setOpaque(false);
            toolBar1.setLayout(new BoxLayout(toolBar1, BoxLayout.X_AXIS));

            PanelUtils.setupComponent(linkButton);
            toolBar1.add(linkButton);

            PanelUtils.setupComponent(hpmButton);
            toolBar1.add(hpmButton);

            toolBar1.addSeparator();

            PanelUtils.setupComponent(unlinkButton);
            toolBar1.add(unlinkButton);

            southPanel.add(toolBar1, BorderLayout.NORTH);
            southPanel.add(messageLabel, BorderLayout.SOUTH);
            add(southPanel, BorderLayout.SOUTH);
        } else {
            add(messageLabel, BorderLayout.SOUTH);
        }

        priorTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent evt) {
                selectionChanged();
            }
        });

    }

    public void selectionChanged() {
        LinkedParameterComponentOptions comp = (LinkedParameterComponentOptions)
                options.getComponentOptions(LinkedParameterComponentOptions.class);

        int[] selRows = priorTable.getSelectedRows();
        int jointPriorCount = 0;
        for (int i = 0; i < selRows.length; ++i) {
            Parameter parameter = parameters.get(selRows[i]);
            if (parameter.isLinked || comp.getLinkedParameterForArgument(parameter) != null) {
                jointPriorCount++;
            }
        }

        linkButton.setEnabled(selRows.length > 1);
        hpmButton.setEnabled(selRows.length > 1);
        unlinkButton.setEnabled(jointPriorCount > 0);
    }


    public void setOptions(BeautiOptions options) {
        this.options = options;

        parameters = options.selectParameters();

        messageLabel.setText(getMessage());

        priorTableModel.fireTableDataChanged();

        validate();
        repaint();
    }

    private String getMessage() {
        String message = "<html>";
        if (isDefaultOnly) {
            message += "<ul><li>These priors listed above are still set to the default values " +
                    "and need to be reviewed.</li>";

            hasUndefinedPrior = false;
            hasImproperPrior = false;
            hasRate = false;

            for (Parameter param : parameters) {
                if (param.priorType == PriorType.UNDEFINED) {
                    hasUndefinedPrior = true;
                }
                if (param.isPriorImproper()) {
                    hasImproperPrior = true;
                }
                if (param.getBaseName().endsWith("clock.rate") || param.getBaseName().endsWith(ClockType.UCED_MEAN)
                        || param.getBaseName().endsWith(ClockType.UCLD_MEAN)) {
                    hasRate = true;
                }
            }

            if (hasUndefinedPrior) {
                message += "<li><b><font color=\"#E42217\">These priors need to be defined by the user.</font></b></li>";
            }
            if (hasImproperPrior) {
                message += "<li><b><font color=\"#B4B417\">Warning: one or more parameters have improper priors.</font></b></li>";
            }
            if (hasRate) {
                message += "<li>" +
                        "Priors on clock rates in particular should be proper such as a high variance lognormal or gamma distribution " +
                        "with a mean appropriate for the organism and units of time employed.</li>";
            }
            message += "</ul>";

        } else {
            message += "* Marked parameters currently have a default prior distribution. " +
                    "You should check that these are appropriate.";
        }


        return message + "</html>";
    }

    public void setParametersList(BeautiOptions options) {
        this.options = options;

        parameters.clear();
        for (Parameter param : options.selectParameters()) {
            if (!param.isPriorEdited() || param.isPriorImproper()) {
                parameters.add(param);
            }
        }
        messageLabel.setText(getMessage());

        if (continueButton != null) {
            continueButton.setEnabled(!hasUndefinedPrior);
        }
    }

    private PriorDialog priorDialog = null;
    private HierarchicalPriorDialog hierarchicalPriorDialog = null;
    private JointPriorDialog linkParameterDialog = null;

    private JButton continueButton = null;

    public void setContinueButton(JButton continueButton) {
        this.continueButton = continueButton;
        if (continueButton != null) {
            continueButton.setEnabled(!hasUndefinedPrior);
        }
    }

    private void linkButtonPressed(int[] rows) {
        Parameter firstParameter = parameters.get(rows[0]);

        // gather the selected parameters...
        List<Parameter> selectedParameters = new ArrayList<Parameter>();
        boolean isLinked = false;
        for (int i = 0; i < rows.length; ++i) {

            Parameter parameter = parameters.get(rows[i]);
            if (parameter.isStatistic) {
                JOptionPane.showMessageDialog(frame,
                        "Statistics cannot be linked together or\n" +
                                "linked with parameter.",
                        "Parameter linking error",
                        JOptionPane.WARNING_MESSAGE);
                return; // Bail out
            }
            if (parameter.truncationLower != firstParameter.truncationLower ||
                    parameter.truncationUpper != firstParameter.truncationUpper ||
                    options.getOperator(parameter).operatorType != options.getOperator(firstParameter).operatorType) {
                JOptionPane.showMessageDialog(frame,
                        "Only parameters that share the same bounds\n" +
                                "and have the same operator types can be linked.",
                        "Parameter linking error",
                        JOptionPane.WARNING_MESSAGE);
                return; // Bail out
            }

            if (parameter.isLinked) {
                isLinked = true;
            }
            selectedParameters.add(parameters.get(rows[i]));
        }

        if (isLinked) {
            int option = JOptionPane.showConfirmDialog(this,
                    "One or more selected parameters are already linked or\n" +
                            "part of a hierarchical model.\n\n" +
                            "Constructing a new prior will remove these parameter\n" +
                            "from the original model. Continue?",
                    "Link warning",
                    JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
            if (option == JOptionPane.NO_OPTION) {
                return;
            }
        }

        LinkedParameterComponentOptions comp = (LinkedParameterComponentOptions)
                options.getComponentOptions(LinkedParameterComponentOptions.class);

        LinkedParameter linkedParameter = comp.createLinkedParameter(null, selectedParameters);

        editLinkedParameter(linkedParameter, selectedParameters);
    }

    private void unlinkButtonPressed(int[] rows) {
        for (int i = 0; i < rows.length; ++i) {

            Parameter parameter = parameters.get(rows[i]);
            if (parameter.isLinked) {
                removeFromJointPrior(parameter);
            }
        }

        priorTableModel.fireTableDataChanged();
        frame.setAllOptions();
    }

    private void hierarchicalButtonPressed(int[] rows) {

        if (rows.length < 2) {
            JOptionPane.showMessageDialog(frame,
                    "Fewer than two parameters selected.",
                    "Parameter linking error",
                    JOptionPane.WARNING_MESSAGE);
            return;
        }

        Double lowerBound = null;
        Double upperBound = null;

        List<Parameter> paramList = new ArrayList<Parameter>();
        for (int i = 0; i < rows.length; ++i) {
            Parameter parameter = parameters.get(rows[i]);
            if (parameter.isStatistic) {
                JOptionPane.showMessageDialog(frame,
                        "Statistics are not currently allowed.",
                        "HPM parameter linking error",
                        JOptionPane.WARNING_MESSAGE);
                return;
            }
            boolean sameBounds = true;
            if (lowerBound == null) {
                lowerBound = parameter.truncationLower;
            } else {
                if (lowerBound != parameter.truncationLower) {
                    sameBounds = false;
                }
            }
            if (upperBound == null) {
                upperBound = parameter.truncationUpper;
            } else {
                if (upperBound != parameter.truncationUpper) {
                    sameBounds = false;
                }
            }

            if (!sameBounds) {
                JOptionPane.showMessageDialog(frame,
                        "Only parameters that share the same bounds\n" +
                                "should be included in a HPM.",
                        "HPM parameter link error",
                        JOptionPane.WARNING_MESSAGE);
                return; // Bail out
            }

            paramList.add(parameter);
        }

        if (hierarchicalPriorDialog != null) { // Already called
            // Check to see if selected parameters are already in a HPM
            HierarchicalModelComponentOptions comp = (HierarchicalModelComponentOptions)
                    options.getComponentOptions(HierarchicalModelComponentOptions.class);
            boolean anyConflicts = false;
            for (Parameter parameter : paramList) {
                if (comp.isHierarchicalParameter(parameter)) {
                    anyConflicts = true;
                    break;
                }
            }
            if (anyConflicts) {
                int option = JOptionPane.showConfirmDialog(this,
                        "At one selected parameter already exists in a HPM.\n" +
                                "Constructing a new prior will remove these parameter\n" +
                                "from the original model. Continue?",
                        "HPM warning",
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE);
                if (option == JOptionPane.NO_OPTION) {
                    return;
                }
            }
        }

        if (hierarchicalPriorDialog == null) {
            hierarchicalPriorDialog = new HierarchicalPriorDialog(frame, options);
        }

        boolean done = false;

        while (!done) {
            int result = hierarchicalPriorDialog.showDialog(paramList);
            if (result == JOptionPane.OK_OPTION && hierarchicalPriorDialog.validateModelName()) {
                hierarchicalPriorDialog.getArguments();
                done = true;
            }
            if (result == JOptionPane.CANCEL_OPTION) {
                return;
            }
        }

        // Remove parameters from old list
        for (Parameter parameter : paramList) {
            HierarchicalModelComponentOptions comp = (HierarchicalModelComponentOptions)
                    options.getComponentOptions(HierarchicalModelComponentOptions.class);
            if (comp.isHierarchicalParameter(parameter)) {
                comp.removeParameter(this, parameter, false);
            }

        }

        // Add HPM to component manager
        hierarchicalPriorDialog.addHPM(paramList);

        for (Parameter parameter : paramList) {
            parameter.setPriorEdited(true);
        }
        priorTableModel.fireTableDataChanged();
    }

    private void priorButtonPressed(int row) {
        Parameter parameter = parameters.get(row);

        LinkedParameterComponentOptions lpco = (LinkedParameterComponentOptions)
                options.getComponentOptions(LinkedParameterComponentOptions.class);

        HierarchicalModelComponentOptions hmco = (HierarchicalModelComponentOptions)
                options.getComponentOptions(HierarchicalModelComponentOptions.class);

        if (lpco.isArgumentParameter(parameter)) {
            LinkedParameter linkedParameter = lpco.getLinkedParameterForArgument(parameter);
            editLinkedParameter(linkedParameter, linkedParameter.getDependentParameterList());
        } else {
            if (hmco.isHierarchicalParameter(parameter)) {
                int option = JOptionPane.showConfirmDialog(this,
                        "This parameter forms part of an Hierarchical Model.\n" +
                                "Selecting a new prior will remove the parameter from\n" +
                                "this HPM.\nContinue?",
                        "Hierarchical parameter warning",
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE);
                if (option == JOptionPane.NO_OPTION) {
                    return;
                }
            }
            if (parameter.isLinked) {
                int option = JOptionPane.showConfirmDialog(this,
                        "Parameter linked to other parameter. Selecting a\n" +
                                "new prior will remove the link.\nContinue?",
                        "Linked parameter warning",
                        JOptionPane.YES_NO_OPTION,
                        JOptionPane.WARNING_MESSAGE);
                if (option == JOptionPane.NO_OPTION) {
                    return;
                }
            }

            editPrior(parameter);
        }
    }

    private void editLinkedParameter(LinkedParameter linkedParameter, List<Parameter> selectedParameters) {
        Parameter sourceParameter = linkedParameter.getArgumentParameter();
        Operator sourceOperator = linkedParameter.getArgumentOperator();

        List<Parameter> compatibleParameters = new ArrayList<Parameter>();
        for (Parameter parameter : parameters) {
            boolean isCompatible = true;
            if (parameter.isStatistic) {
                isCompatible = false;
            }
            if (parameter.truncationLower != sourceParameter.truncationLower) {
                isCompatible = false;
            }
            Operator operator = options.getOperator(parameter);
            if (operator == null || operator.operatorType != sourceOperator.operatorType) {
                isCompatible = false;
            }
            if (isCompatible) {
                compatibleParameters.add(parameter);
            }
        }

        if (linkParameterDialog == null) {
            linkParameterDialog = new JointPriorDialog(frame, options);
        }

        linkParameterDialog.setLinkedParameter(linkedParameter);
        linkParameterDialog.setCompatibleParameterList(compatibleParameters);
        linkParameterDialog.setDependentParameterList(selectedParameters);

        boolean done = false;

        while (!done) {
            int result = linkParameterDialog.showDialog();
            if (result == JOptionPane.OK_OPTION && linkParameterDialog.validateModelName()) {
                done = true;
            }
            if (result == JOptionPane.CANCEL_OPTION) {
                return;
            }
        }

        linkedParameter.setName(linkParameterDialog.getName());

        linkedParameter.linkDependentParameters(linkParameterDialog.getDependentParameterList());

        linkParameterDialog.getArguments(linkedParameter.getArgumentParameter());

        for (Parameter parameter : linkParameterDialog.getDependentParameterList()) {
            parameter.setPriorEdited(true);
        }

        priorTableModel.fireTableDataChanged();
        frame.setAllOptions();
    }

    private void editPrior(Parameter parameter) {
        if (priorDialog == null) {
            priorDialog = new PriorDialog(frame);
        }

        priorDialog.setParameter(parameter);

        int result;

        do {
            result = priorDialog.showDialog();
        } while (result == JOptionPane.OK_OPTION && priorDialog.hasInvalidInput(true));

        if (result == JOptionPane.OK_OPTION) {
            // Only do this if OK button is pressed (not cancel):

            // If this was part of a linked parameter, remove it from that...
            removeFromJointPrior(parameter);

            // move to individual Dialog, otherwise it will change if Cancel
            priorDialog.getArguments(parameter);

            parameter.setPriorEdited(true);

            if (isDefaultOnly) {
                setParametersList(options);
            }

            if (parameter.getBaseName().endsWith("treeModel.rootHeight") || parameter.taxaId != null) { // param.taxa != null is TMRCA

                if (options.treeModelOptions.isNodeCalibrated(parameter)) {
                    List<ClockModelGroup> groupList;
                    if (options.useStarBEAST) {
                        groupList = options.clockModelOptions.getClockModelGroups();
                    } else {
                        groupList = options.clockModelOptions.getClockModelGroups(options.getDataPartitions(parameter.getOptions()));
                    }

                    for (ClockModelGroup clockModelGroup : groupList) {
                        options.clockModelOptions.nodeCalibration(clockModelGroup);
                    }
                    frame.setAllOptions();
//        	} else {
//        		options.clockModelOptions.fixRateOfFirstClockPartition();
                }
            }

            priorTableModel.fireTableDataChanged();
            frame.setAllOptions();
        }
    }

    private void removeFromJointPrior(Parameter parameter) {
        LinkedParameterComponentOptions comp = (LinkedParameterComponentOptions)
                options.getComponentOptions(LinkedParameterComponentOptions.class);

        LinkedParameter linkedParameter = comp.getLinkedParameter(parameter);
        if (linkedParameter != null) {
            comp.removeDependentParameter(parameter);
        }

        // Possibly remove parameter from its HPM
        HierarchicalModelComponentOptions hmco = (HierarchicalModelComponentOptions)
                options.getComponentOptions(HierarchicalModelComponentOptions.class);
        if (hmco.isHierarchicalParameter(parameter)) {
            if (hmco.removeParameter(this, parameter, true) == JOptionPane.NO_OPTION) {
                // Bail out
                System.err.println("Bailing out of modification");
                return;
            }
            System.err.println("Parameter removed from an HPM");
        }

    }

    public void getOptions(BeautiOptions options) {
    }

    public JComponent getExportableComponent() {
        return priorTable;
    }

    NumberFormatter formatter = new NumberFormatter(4);

    class DoubleRenderer extends TableRenderer {

        private static final long serialVersionUID = -2614341608257369805L;

        public DoubleRenderer(int alignment, Insets insets) {

            super(true, alignment, insets);
        }

        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                       boolean hasFocus, int row, int column) {

            String s;
            if (((Double) value).isNaN()) {
                s = "random";
            } else {
                s = formatter.format((Double) value);
            }
            return super.getTableCellRendererComponent(table, s, isSelected, hasFocus, row, column);

        }
    }

    public class ButtonRenderer extends JButton implements TableCellRenderer {
        protected Color undefinedColour = new Color(0xE4, 0x22, 0x17);
        protected Color improperColour = new Color(0xB4, 0xB4, 0x17);


        private static final long serialVersionUID = -2416184092883649169L;

        public ButtonRenderer(int alignment, Insets insets) {
            setOpaque(true);
            setHorizontalAlignment(alignment);
            setMargin(insets);

//            setFont(UIManager.getFont("SmallSystemFont"));
//            putClientProperty("JComponent.sizeVariant", "small");
//            putClientProperty("JButton.buttonType", "square");
        }

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

            String text = (value == null) ? "" : value.toString();
            if (text.toString().startsWith("?")) {
                setForeground(undefinedColour);
            } else if (text.toString().startsWith("!")) {
                setForeground(improperColour);
            } else {
                setForeground(UIManager.getColor("Button.foreground"));
            }

            setText(text);

            return this;
        }
    }

    public class ButtonEditor extends DefaultCellEditor {

        private static final long serialVersionUID = 6372738480075411674L;
        protected JButton button;
        private String label;
        private boolean isPushed;
        private int row;

        public ButtonEditor(int alignment, Insets insets) {
            super(new JCheckBox());
            button = new JButton();
            button.setOpaque(true);
            button.setHorizontalAlignment(alignment);
            button.setMargin(insets);
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    fireEditingStopped();
                }
            });
//            button.setFont(UIManager.getFont("SmallSystemFont"));
//            button.putClientProperty("JComponent.sizeVariant", "small");
//            button.putClientProperty("JButton.buttonType", "square");
        }

        public Component getTableCellEditorComponent(JTable table, Object value,
                                                     boolean isSelected, int row, int column) {
            button.setEnabled(table.isEnabled());
            button.setFont(table.getFont());
            if (isSelected) {
//                button.setForeground(table.getSelectionForeground());
                button.setBackground(table.getSelectionBackground());
            } else {
//                button.setForeground(table.getForeground());
                button.setBackground(UIManager.getColor("Button.background"));
            }
            label = (value == null) ? "" : value.toString();
            button.setText(label);
            isPushed = true;
            this.row = row;
            return button;
        }

        public Object getCellEditorValue() {
            if (isPushed) {
                priorButtonPressed(row);
            }
            isPushed = false;
            return label;
        }

        public boolean stopCellEditing() {
            isPushed = false;
            return super.stopCellEditing();
        }

        protected void fireEditingStopped() {
            super.fireEditingStopped();
        }
    }


}
