
package uk.co.wingpath.modbusgui;

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import uk.co.wingpath.util.*;
import uk.co.wingpath.modbus.*;
import uk.co.wingpath.gui.*;
import uk.co.wingpath.event.*;

public class CommandValuePanel
    extends JPanel
    implements TableVerifier
{
    private final JTable table;
    private final CommandTableModel model;
    private final StatusBar statusBar;
    private final MirrorField mirror;
    private final JLabel label;
    private final ValueEventSource listeners;
    private boolean highlight;
    private final boolean isEditor;

    public CommandValuePanel (String title, final CommandTableModel model,
        final StatusBar statusBar, MirrorField mirror, int visibleRows,
        boolean isEditor)
    {
        this.model = model;
        this.statusBar = statusBar;
        this.isEditor = isEditor;
        this.mirror = mirror;
        listeners = new ValueEventSource ();
        table = new WTable (model);
        table.setSelectionMode (ListSelectionModel.SINGLE_SELECTION);
        table.setRowSelectionAllowed (false);
        highlight = true;

        int columns = model.getColumnCount ();

        WTableCellRenderer renderer =
            new WTableCellRenderer ()
            {
                public Component getTableCellRendererComponent (
                    JTable table, Object value, boolean isSelected,
                    boolean hasFocus, int row, int column)
                {
                    Component component =
                         super.getTableCellRendererComponent (table, value,
                            isSelected, hasFocus, row, column);
                    int col = table.convertColumnIndexToModel (column);
                    component.setBackground (
                        highlight && model.isError (row, col) ?
                            Gui.COLOUR_BACKGROUND_ERROR :
                        model.isUnapplied (row, col) ?
                            Gui.COLOUR_BACKGROUND_UNAPPLIED :
                        isSelected ? Gui.COLOUR_BACKGROUND_SELECTED :
                            Gui.COLOUR_BACKGROUND_NORMAL);
                    return component;
                }
            };
        ((JLabel) renderer).setHorizontalAlignment (SwingConstants.RIGHT);

        int totalWidth = 0;

        for (int col = 0 ; col < columns ; col++)
        {
            int width = model.getColumnWidth (col);
            totalWidth += width;
            TableColumn column = table.getColumnModel ().getColumn (col);
            column.setPreferredWidth (width);
            column.setCellRenderer (renderer);

            if (col != 0)
            {
                WCellEditor cellEditor = new WCellEditor (listeners);
                cellEditor.setVerifier (this);
                cellEditor.setMirror (mirror);
                column.setCellEditor (cellEditor);
            }
        }

        table.setPreferredScrollableViewportSize (
            new Dimension (totalWidth, visibleRows * table.getRowHeight ()));
        setLayout (new BorderLayout ());
        if (title != null)
        {
            label = new JLabel (title + ":");
            add (label, BorderLayout.NORTH);
            label.setLabelFor (table);
        }
        else
        {
            label = null;
        }
        JScrollPane scrollPane = new JScrollPane (table,
            JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
            JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        add (scrollPane, BorderLayout.CENTER);
    }

    public boolean haveActualValuesChanged (Numeric.Value [] values)
    {
        boolean changed = false;
        TableCellEditor cellEditor = table.getCellEditor ();
        if (cellEditor instanceof WCellEditor &&
            ((WCellEditor) cellEditor).hasValueChanged ())
        {
            changed = true;
        }
        if (model.haveActualValuesChanged (values))
            changed = true;
        return changed;
    }

    public boolean haveExpectedValuesChanged (Numeric.PatVal [] values)
    {
        boolean changed = false;
        TableCellEditor cellEditor = table.getCellEditor ();
        if (cellEditor instanceof WCellEditor &&
            ((WCellEditor) cellEditor).hasValueChanged ())
        {
            changed = true;
        }
        if (model.haveExpectedValuesChanged (values))
            changed = true;
        return changed;
    }

    @Override
    public void setEnabled (boolean enabled)
    {
        if (label != null)
            label.setEnabled (enabled);
        table.setEnabled (enabled);
    }

    public void setMnemonic (int mnemonic)
    {
        if (label != null)
            label.setDisplayedMnemonic (mnemonic);
    }

    public void setMnemonic (int mnemonic, int index)
    {
        if (label != null)
        {
            label.setDisplayedMnemonic (mnemonic);
            label.setDisplayedMnemonicIndex (index);
        }
    }

    public String verify (String str, int row, int col, boolean isChanging)
    {
        if (isChanging)
        {
            statusBar.clear ();
            return str;
        }
        try
        {
            str = model.fromString (str, row, col);
            statusBar.clear ();
            return str;
        }
        catch (ValueException e)
        {
            statusBar.showException (e);
            return null;
        }
    }

    public boolean stopEditing ()
    {
        TableCellEditor cellEditor = table.getCellEditor ();
        if (cellEditor != null)
            return cellEditor.stopCellEditing ();
        return true;
    }

    public void cancelEditing ()
    {
        TableCellEditor cellEditor = table.getCellEditor ();
        if (cellEditor != null)
            cellEditor.cancelCellEditing ();
    }

    public void highlightErrors (boolean highlight)
    {
        this.highlight = highlight;
        model.fireTableRowsUpdated (0, model.getRowCount () - 1);
    }

    public void addValueListener (ValueListener l)
    {
        listeners.addListener (l);
    }

    public void removeValueListener (ValueListener l)
    {
        listeners.removeListener (l);
    }
}

