
package uk.co.wingpath.modbusgui;

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

public class LogPanel
    implements TreeCard
{
    private final boolean debugging;

    private class LevelSelector
        extends WComboBox<Reporter.Level>
    {
        private LevelSelector (String label, String name, int mnemonic)
        {
            super (label,
                debugging ?
                    new Reporter.Level []
                    {
                        Reporter.Level.NONE,
                        Reporter.Level.FATAL,
                        Reporter.Level.ERROR,
                        Reporter.Level.WARN,
                        Reporter.Level.INFO,
                        Reporter.Level.TRACE,
                        Reporter.Level.DEBUG
                    } :
                    new Reporter.Level []
                    {
                        Reporter.Level.NONE,
                        Reporter.Level.FATAL,
                        Reporter.Level.ERROR,
                        Reporter.Level.WARN,
                        Reporter.Level.INFO,
                        Reporter.Level.TRACE
                    },
                debugging ?
                    new String []
                    {
                        "None",
                        "Fatal",
                        "Error",
                        "Warn",
                        "Info",
                        "Trace",
                        "Debug"
                    } :
                    new String []
                    {
                        "None",
                        "Fatal",
                        "Error",
                        "Warn",
                        "Info",
                        "Trace"
                    });
            setToolTipText ("Level to log to " + name + " - 'None' to disable");
            setMnemonic (mnemonic);
        }

        @Override
        public void setValue (Reporter.Level value)
        {
            if (!debugging && value == Reporter.Level.DEBUG)
                value = Reporter.Level.TRACE;
            super.setValue (value);
        }
    }

    private final Frontend frontend;
    private final LogSettings logSettings;
    private final boolean includeSyslog;

    private final LevelSelector terminalSelector;
    private final LevelSelector fileSelector;
    private final WComponent<Integer> fileSizeField;
    private final WComponent<String> fileNameField;
    private final LevelSelector syslogSelector;
    private final LevelSelector windowsSelector;
    private final WFileChooser fileChooser;
    private final JButton applyButton;
    private final JButton resetButton;
    private final JButton browseButton;
    private final JPanel outerPanel;
    private final StatusBar statusBar;
    private final ValueEventSource statusListeners;
    private final boolean isWindows;

    public LogPanel (final Frontend frontend, LogSettings logSettings,
        boolean debugging, boolean includeSyslog)
    {
        this.frontend = frontend;
        this.debugging = debugging;
        this.logSettings = logSettings;
        this.includeSyslog = includeSyslog;
        statusListeners = new ValueEventSource ();
        isWindows = Installer.isWindows ();

        statusBar = new StatusBar ("log", frontend.getHelpViewer ());
        statusBar.addStatusListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    fireStatusChanged ();
                }
            });

        outerPanel = new JPanel ();
        outerPanel.setLayout (new BorderLayout ());
        JLabel heading = Gui.createDialogHeading ("Log Settings");
        outerPanel.add (heading, BorderLayout.NORTH);
        outerPanel.add (statusBar, BorderLayout.SOUTH);

        JPanel mainPanel = new JPanel ();
        outerPanel.add (mainPanel, BorderLayout.CENTER);
        mainPanel.setLayout (new BorderLayout ());
        ButtonPanel buttonPanel = new ButtonPanel ();
        mainPanel.add (buttonPanel, BorderLayout.SOUTH);

        GridBagPanel panel = new GridBagPanel ();
        mainPanel.add (panel, BorderLayout.CENTER);
        int spacing = 25;

        terminalSelector =
            new LevelSelector ("Terminal Level", "terminal", KeyEvent.VK_T);
        panel.addComponent (terminalSelector);

        panel.addSpacer (spacing);

        fileSelector = 
            new LevelSelector ("File Level", "file", KeyEvent.VK_F);
        panel.addComponent (fileSelector);

        fileChooser = new WFileChooser (frontend.getMainFrame ());

        browseButton = new JButton ("Browse");
        browseButton.addActionListener (
            new ActionListener ()
            {
                public void actionPerformed (ActionEvent e)
                {
                    File file = fileChooser.chooseAppendFile (
                        frontend.getProduct ().getName () + ": Choose Log File",
                        "Select",
                        "Select file for logging output");
                    if (file != null)
                    {
                        fileNameField.setValue (file.getPath ());
                        setEnabled ();
                    }
                }
            });

        GridBagConstraints constraints = panel.createConstraints ();
        fileNameField = new WTextField (statusBar, "File Name", false);
        fileNameField.setToolTipText (
            "Name of file to which log messages should be appended");
        fileNameField.setWidthChars (30);
        panel.add (fileNameField.getLabel (), constraints);
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.gridx = 1;
        panel.add (fileNameField.getComponent (), constraints);
        constraints.fill = GridBagConstraints.NONE;
        constraints.gridx = 2;
        panel.add (browseButton, constraints);

        fileSizeField = new WIntegerField (statusBar, "Maximum file size (MB)",
            0, LogSettings.MAX_FILE_SIZE, 0);
        fileSizeField.setToolTipText (
            "Maximum size of log file in MB. 0 means unlimited");
        fileSizeField.setMnemonic (KeyEvent.VK_M);
        fileSizeField.setWidthChars (10);
        panel.addComponent (fileSizeField);

        syslogSelector = new LevelSelector ("Syslog Level", "Unix/Linux syslog",
            KeyEvent.VK_S);
        if (includeSyslog)
        {
            panel.addSpacer (spacing);
            panel.addComponent (syslogSelector);
        }

        windowsSelector = new LevelSelector ("Windows Level",
            "Windows Event Logger", KeyEvent.VK_W);
        if (includeSyslog)
        {
            panel.addSpacer (spacing);
            panel.addComponent (windowsSelector);
        }

        ValueListener guiListener =
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    statusBar.clear ();
                    setEnabled ();
                }
            };

        terminalSelector.addValueListener (guiListener);
        fileSelector.addValueListener (guiListener);
        fileNameField.addValueListener (guiListener);
        fileSizeField.addValueListener (guiListener);
        syslogSelector.addValueListener (guiListener);
        windowsSelector.addValueListener (guiListener);

        applyButton = buttonPanel.addButton ("Apply", null,
            new ActionListener ()
            {
                public void actionPerformed (ActionEvent e)
                {
                    statusBar.clear ();
                    if (checkValues ())
                        putValuesToModel ();
                }
            });

        resetButton = buttonPanel.addButton ("Reset", null,
            new ActionListener ()
            {
                public void actionPerformed (ActionEvent e)
                {
                    reset ();
                }
            });
        resetButton.setMnemonic (KeyEvent.VK_R);

        buttonPanel.addButton (getHelpAction ());

        frontend.addBackendStateListener (
            new BackendState.Listener ()
            {
                public void stateChanged (BackendState state)
                {
                    setEnabled ();
                }
            });

        logSettings.addValueListener (
            new ValueListener ()
            {
                public void valueChanged (ValueEvent e)
                {
                    getValuesFromModel ();
                }
            });

        getValuesFromModel ();
    }

    private void setEnabled ()
    {
        // boolean en = frontend.isStopped ();
        boolean en = true;
        terminalSelector.setEnabled (en);
        fileSelector.setEnabled (en);
        fileNameField.setEnabled (en &&
            fileSelector.getValue () != Reporter.Level.NONE);
        fileSizeField.setEnabled (en &&
            fileSelector.getValue () != Reporter.Level.NONE);
        Gui.setEnabled (browseButton,
            en && fileSelector.getValue () != Reporter.Level.NONE);
        syslogSelector.setEnabled (en && !isWindows);
        windowsSelector.setEnabled (en && isWindows);
        setButtonsEnabled (en && hasUnappliedChanges ());
    }

    public JPanel getPanel ()
    {
        return outerPanel;
    }

    public String getTag ()
    {
        return "log";
    }

    public String getName ()
    {
        return "Logging";
    }

    public Action getHelpAction ()
    {
        return frontend.getHelpAction ("logging");
    }

    public JButton getDefaultButton ()
    {
        return applyButton;
    }

    public String getToolTipText ()
    {
        return null;
    }

    @Override
    public void selected ()
    {
    }

    @Override
    public void reset ()
    {
        getValuesFromModel ();
    }

    public boolean hasUnappliedChanges ()
    {
        boolean changed = false;
        if (terminalSelector.hasValueChanged (
            logSettings.getTerminalLevel ().getValue ()))
        {
            changed = true;
        }
        if (fileSelector.hasValueChanged (
            logSettings.getFileLevel ().getValue ()))
        {
            changed = true;
        }
        if (fileSizeField.hasValueChanged (
            logSettings.getFileSize ().getValue ()))
        {
            changed = true;
        }
        if (fileNameField.hasValueChanged (logSettings.getFileName ()))
            changed = true;
        if (syslogSelector.hasValueChanged (logSettings.getSyslogLevel ()))
            changed = true;
        if (windowsSelector.hasValueChanged (logSettings.getWindowsLevel ()))
            changed = true;
        return changed;
    }

    public boolean hasError ()
    {
        return statusBar.hasError ();
    }

    private boolean checkValues ()
    {
        if (fileSelector.getValue () != Reporter.Level.NONE)
        {
            if (!fileNameField.checkValue ())
                return false;
            String fileName = fileNameField.getValue ().trim ();
            if (fileName.equals (""))
            {
                statusBar.showError ("You must enter a file name");
                fileNameField.requestFocusInWindow ();
                return false;
            }
            if (!fileName.equals (logSettings.getFileName ()))
            {
                try
                {
                    FileOutputStream fos =
                        new FileOutputStream (fileName, true);
                    fos.close ();
                }
                catch (FileNotFoundException e)
                {
                    statusBar.showError ("Can't open log file " +
                        Exceptions.getMessage (e));
                    return false;
                }
                catch (IOException e)
                {
                }
            }
            if (!fileSizeField.checkValue ())
                return false;
        }
        if (terminalSelector.getValue () == Reporter.Level.NONE &&
            fileSelector.getValue () == Reporter.Level.NONE &&
            syslogSelector.getValue () == Reporter.Level.NONE &&
            windowsSelector.getValue () == Reporter.Level.NONE)
        {
            terminalSelector.setValue (Reporter.Level.ERROR);
        }
        return true;
    }

    private void setButtonsEnabled (boolean enabled)
    {
        Gui.setEnabled (applyButton, enabled);
        Gui.setEnabled (resetButton, enabled);
        fireStatusChanged ();
    }

    private void getValuesFromModel ()
    {
        statusBar.clear ();
        terminalSelector.setValue (logSettings.getTerminalLevel ().getValue ());
        fileSelector.setValue (logSettings.getFileLevel ().getValue ());
        fileSizeField.setValue (logSettings.getFileSize ().getValue ());
        fileNameField.setValue (logSettings.getFileName ());
        fileChooser.setSelectedFile (new File (logSettings.getFileName ()));
        syslogSelector.setValue (logSettings.getSyslogLevel ());
        windowsSelector.setValue (logSettings.getWindowsLevel ());
        setEnabled ();
    }

    private void putValuesToModel ()
    {
        logSettings.getTerminalLevel ().setValue (
            terminalSelector.getValue ());
        logSettings.getFileLevel ().setValue (fileSelector.getValue ());
        logSettings.getFileSize ().setValue (fileSizeField.getValue ());
        logSettings.setFileName (fileNameField.getValue ());
        logSettings.setSyslogLevel (syslogSelector.getValue ());
        logSettings.setWindowsLevel (windowsSelector.getValue ());
        logSettings.valuesUpdated ();
        setButtonsEnabled (false);
    }

    private void fireStatusChanged ()
    {
        statusListeners.fireValueChanged (this);
    }

    public void addStatusListener (ValueListener l)
    {
        statusListeners.addListener (l);
    }

    public void removeStatusListener (ValueListener l)
    {
        statusListeners.removeListener (l);
    }
}

