/*
 * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.java.sip.communicator.plugin.loggingutils;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;

import net.java.sip.communicator.service.httputil.*;
import net.java.sip.communicator.service.notification.*;
import net.java.sip.communicator.util.Logger;
import net.java.sip.communicator.plugin.desktoputil.*;

import org.apache.commons.lang3.StringUtils;
import org.jitsi.service.packetlogging.*;
import org.jitsi.service.resources.*;
import org.jitsi.utils.*;

/**
 * The Logging configuration form.
 * @author Damian Minkov
 */
public class LoggingConfigForm
    extends TransparentPanel
    implements ActionListener,
        DocumentListener
{
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 0L;

    /**
     * Our Logger.
     */
    private static final Logger logger
            = Logger.getLogger(LoggingConfigForm.class);

    /**
     * Upload location property.
     */
    private static final String UPLOAD_LOCATION_PROPETY =
            "plugin.loggingutils.uploadlocation";

    /**
     * The enable packet logging check box.
     */
    private JCheckBox enableCheckBox;

    /**
     * Check box to enable/disable packet debug of sip protocol.
     */
    private JCheckBox sipProtocolCheckBox;

    /**
     * Check box to enable/disable packet debug of jabber protocol.
     */
    private JCheckBox jabberProtocolCheckBox;

    /**
     * Check box to enable/disable packet debug of media protocol/RTP.
     */
    private JCheckBox rtpProtocolCheckBox;

    /**
     * Check box to enable/disable packet debug of Ice4J.
     */
    private JCheckBox ice4jProtocolCheckBox;

    /**
     * The file count label.
     */
    private JLabel fileCountLabel;

    /**
     * The filed for file count value.
     */
    private JTextField fileCountField = new JTextField();

    /**
     * The file size label.
     */
    private JLabel fileSizeLabel;

    /**
     * The filed for file size value.
     */
    private JTextField fileSizeField = new JTextField();

    /**
     * Notification event.
     */
    private static final String LOGFILES_ARCHIVED = "LogFilesArchived";

    /**
     * Archive logs button.
     */
    private JButton archiveButton;

    /**
     * Archive logs button.
     */
    private JButton uploadLogsButton;

    /**
     * Creates Packet Logging Config form.
     */
    public LoggingConfigForm()
    {
        super(new BorderLayout());

        init();
        loadValues();

        // Register notification for saved calls.
        if(LoggingUtilsActivator.getNotificationService() != null)
            LoggingUtilsActivator.getNotificationService()
                .registerDefaultNotificationForEvent(
                    LOGFILES_ARCHIVED,
                    NotificationAction.ACTION_POPUP_MESSAGE,
                    null,
                    null);
    }

    /**
     * Creating the configuration form
     */
    private void init()
    {
        ResourceManagementService resources =
                LoggingUtilsActivator.getResourceService();

        enableCheckBox = new SIPCommCheckBox(
            resources.getI18NString("plugin.loggingutils.ENABLE_DISABLE"));
        enableCheckBox.addActionListener(this);

        sipProtocolCheckBox = new SIPCommCheckBox(
            resources.getI18NString("plugin.sipaccregwizz.PROTOCOL_NAME"));
        sipProtocolCheckBox.addActionListener(this);

        jabberProtocolCheckBox = new SIPCommCheckBox(
            resources.getI18NString("plugin.jabberaccregwizz.PROTOCOL_NAME"));
        jabberProtocolCheckBox.addActionListener(this);

        String rtpDescription = resources.getI18NString(
            "plugin.loggingutils.PACKET_LOGGING_RTP_DESCRIPTION");
        rtpProtocolCheckBox = new SIPCommCheckBox(
            resources.getI18NString("plugin.loggingutils.PACKET_LOGGING_RTP")
            + " " + rtpDescription);
        rtpProtocolCheckBox.addActionListener(this);
        rtpProtocolCheckBox.setToolTipText(rtpDescription);

        ice4jProtocolCheckBox = new SIPCommCheckBox(
            resources.getI18NString("plugin.loggingutils.PACKET_LOGGING_ICE4J"));
        ice4jProtocolCheckBox.addActionListener(this);

        JPanel mainPanel = new TransparentPanel();

        add(mainPanel, BorderLayout.NORTH);

        mainPanel.setLayout(new GridBagLayout());

        GridBagConstraints c = new GridBagConstraints();

        enableCheckBox.setAlignmentX(Component.LEFT_ALIGNMENT);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 1.0;
        c.gridx = 0;
        c.gridy = 0;
        mainPanel.add(enableCheckBox, c);

        String label = resources.getI18NString(
                "plugin.loggingutils.PACKET_LOGGING_DESCRIPTION");
        JLabel descriptionLabel = new JLabel(label);
        descriptionLabel.setToolTipText(label);
        enableCheckBox.setToolTipText(label);
        descriptionLabel.setForeground(Color.GRAY);
        descriptionLabel.setFont(descriptionLabel.getFont().deriveFont(8));
        c.gridy = 1;
        c.insets = new Insets(0, 25, 10, 0);
        mainPanel.add(descriptionLabel, c);

        final JPanel loggersButtonPanel
            = new TransparentPanel(new GridLayout(0, 1));

        loggersButtonPanel.setBorder(BorderFactory.createTitledBorder(
            resources.getI18NString("service.gui.PROTOCOL")));

        loggersButtonPanel.add(sipProtocolCheckBox);
        loggersButtonPanel.add(jabberProtocolCheckBox);
        loggersButtonPanel.add(rtpProtocolCheckBox);
        loggersButtonPanel.add(ice4jProtocolCheckBox);

        c.insets = new Insets(0, 20, 10, 0);
        c.gridy = 2;
        mainPanel.add(loggersButtonPanel, c);

        final JPanel advancedPanel
            = new TransparentPanel(new GridLayout(0, 2));

        advancedPanel.setBorder(BorderFactory.createTitledBorder(
            resources.getI18NString("service.gui.ADVANCED")));

        fileCountField.getDocument().addDocumentListener(this);
        fileSizeField.getDocument().addDocumentListener(this);

        fileCountLabel = new JLabel(resources.getI18NString(
                "plugin.loggingutils.PACKET_LOGGING_FILE_COUNT"));
        advancedPanel.add(fileCountLabel);
        advancedPanel.add(fileCountField);
        fileSizeLabel = new JLabel(resources.getI18NString(
                "plugin.loggingutils.PACKET_LOGGING_FILE_SIZE"));
        advancedPanel.add(fileSizeLabel);
        advancedPanel.add(fileSizeField);

        c.gridy = 3;
        mainPanel.add(advancedPanel, c);

        archiveButton = new JButton(
            resources.getI18NString("plugin.loggingutils.ARCHIVE_BUTTON"));
        archiveButton.addActionListener(this);

        c = new GridBagConstraints();
        c.anchor = GridBagConstraints.LINE_START;
        c.weightx = 0;
        c.gridx = 0;
        c.gridy = 4;
        mainPanel.add(archiveButton, c);

        if(StringUtils.isNotEmpty(getUploadLocation()))
        {
            uploadLogsButton = new JButton(
                resources.getI18NString("plugin.loggingutils.UPLOAD_LOGS_BUTTON"));
            uploadLogsButton.addActionListener(this);

            c.insets = new Insets(10, 0, 0, 0);
            c.gridy = 5;
            mainPanel.add(uploadLogsButton, c);
        }
    }

    /**
     * Checks the property in configuration service and if missing there get it
     * from default settings.
     * @return the upload location.
     */
    static String getUploadLocation()
    {
        // check first in configuration it can be manually set
        // or by provisioning
        String uploadLocation =
            LoggingUtilsActivator.getConfigurationService()
                .getString(UPLOAD_LOCATION_PROPETY);
        // if missing check default settings
        if(uploadLocation == null || uploadLocation.length() == 0)
        {
            uploadLocation = LoggingUtilsActivator.getResourceService()
                .getSettingsString(UPLOAD_LOCATION_PROPETY);
        }

        return uploadLocation;
    }

    /**
     * Loading the values stored into configuration form
     */
    private void loadValues()
    {
        PacketLoggingService packetLogging
            = LoggingUtilsActivator.getPacketLoggingService();
        PacketLoggingConfiguration cfg = packetLogging.getConfiguration();

        enableCheckBox.setSelected(cfg.isGlobalLoggingEnabled());

        sipProtocolCheckBox.setSelected(cfg.isSipLoggingEnabled());
        jabberProtocolCheckBox.setSelected(cfg.isJabberLoggingEnabled());
        rtpProtocolCheckBox.setSelected(cfg.isRTPLoggingEnabled());
        ice4jProtocolCheckBox.setSelected(cfg.isIce4JLoggingEnabled());
        fileCountField.setText(String.valueOf(cfg.getLogfileCount()));
        fileSizeField.setText(String.valueOf(cfg.getLimit() / 1000));

        updateButtonsState();
    }

    /**
     * Update button enable/disable state according enableCheckBox.
     */
    private void updateButtonsState()
    {
        sipProtocolCheckBox.setEnabled(enableCheckBox.isSelected());
        jabberProtocolCheckBox.setEnabled(enableCheckBox.isSelected());
        rtpProtocolCheckBox.setEnabled(enableCheckBox.isSelected());
        ice4jProtocolCheckBox.setEnabled(enableCheckBox.isSelected());
        fileCountField.setEnabled(enableCheckBox.isSelected());
        fileSizeField.setEnabled(enableCheckBox.isSelected());
        fileSizeLabel.setEnabled(enableCheckBox.isSelected());
        fileCountLabel.setEnabled(enableCheckBox.isSelected());
    }

    /**
     * Invoked when an action occurs.
     */
    public void actionPerformed(ActionEvent e)
    {
        Object source = e.getSource();

        PacketLoggingService packetLogging =
                LoggingUtilsActivator.getPacketLoggingService();

        if(source.equals(enableCheckBox))
        {
            // turn it on/off in activator
            packetLogging.getConfiguration().setGlobalLoggingEnabled(
                    enableCheckBox.isSelected());
            updateButtonsState();
        }
        else if(source.equals(sipProtocolCheckBox))
        {
            packetLogging.getConfiguration().setSipLoggingEnabled(
                    sipProtocolCheckBox.isSelected());
        }
        else if(source.equals(jabberProtocolCheckBox))
        {
            packetLogging.getConfiguration().setJabberLoggingEnabled(
                    jabberProtocolCheckBox.isSelected());
        }
        else if(source.equals(rtpProtocolCheckBox))
        {
            packetLogging.getConfiguration().setRTPLoggingEnabled(
                    rtpProtocolCheckBox.isSelected());
        }
        else if(source.equals(ice4jProtocolCheckBox))
        {
            packetLogging.getConfiguration().setIce4JLoggingEnabled(
                    ice4jProtocolCheckBox.isSelected());
        }
        else if(source.equals(archiveButton))
        {
            // don't block the UI thread
            new Thread(new Runnable()
            {
                public void run()
                {
                    collectLogs();
                }
            }).start();
        }
        else if(source.equals(uploadLogsButton))
        {
            // don't block the UI thread
            new Thread(new Runnable()
            {
                public void run()
                {
                    uploadLogs();
                }
            }).start();
        }
    }

    /**
     * Gives notification that there was an insert into the document.  The
     * range given by the DocumentEvent bounds the freshly inserted region.
     *
     * @param e the document event
     */
    public void insertUpdate(DocumentEvent e)
    {
        documentChanged(e);
    }

    /**
     * Gives notification that a portion of the document has been
     * removed.  The range is given in terms of what the view last
     * saw (that is, before updating sticky positions).
     *
     * @param e the document event
     */
    public void removeUpdate(DocumentEvent e)
    {
        documentChanged(e);
    }

    /**
     * Not used.
     *
     * @param e the document event
     */
    public void changedUpdate(DocumentEvent e)
    {}

    /**
     * A change in the text fields.
     * @param e the document event.
     */
    private void documentChanged(DocumentEvent e)
    {
        if(e.getDocument().equals(fileCountField.getDocument()))
        {
            // set file count only if its un integer
            try
            {
                int newFileCount = Integer.valueOf(fileCountField.getText());
                fileCountField.setForeground(Color.black);
                LoggingUtilsActivator.getPacketLoggingService()
                    .getConfiguration().setLogfileCount(newFileCount);
            }
            catch(Throwable t)
            {
                fileCountField.setForeground(Color.red);
            }
        }
        else if(e.getDocument().equals(fileSizeField.getDocument()))
        {
            // set file size only if its un integer
            try
            {
                int newFileSize = Integer.valueOf(fileSizeField.getText());
                fileSizeField.setForeground(Color.black);
                LoggingUtilsActivator.getPacketLoggingService()
                    .getConfiguration().setLimit(newFileSize * 1000);
            }
            catch(Throwable t)
            {
                fileSizeField.setForeground(Color.red);
            }
        }
    }

    /**
     * Asks user for a location to save logs by poping up a file chooser.
     * and archiving logs and saving them on the specified location.
     */
    private void collectLogs()
    {
        ResourceManagementService resources =
                LoggingUtilsActivator.getResourceService();

        SipCommFileChooser fileChooser = GenericFileDialog.create(
            null,
            resources.getI18NString(
                    "plugin.loggingutils.ARCHIVE_FILECHOOSE_TITLE"),
            SipCommFileChooser.SAVE_FILE_OPERATION);
        fileChooser.setSelectionMode(
                SipCommFileChooser.SAVE_FILE_OPERATION);

        String defaultDir = "";
        try
        {
            defaultDir = LoggingUtilsActivator.getFileAccessService()
                .getDefaultDownloadDirectory().getAbsolutePath()
                + File.separator;
        }
        catch(IOException ex){}
        fileChooser.setStartPath(
                 defaultDir + LogsCollector.getDefaultFileName());

        File dest = fileChooser.getFileFromDialog();

        if(dest == null)
            return;

        dest = LogsCollector.collectLogs(dest, null);

        NotificationService notificationService
            = LoggingUtilsActivator.getNotificationService();

        if(notificationService != null)
        {
            String bodyMsgKey
                = (dest == null)
                    ? "plugin.loggingutils.ARCHIVE_MESSAGE_NOTOK"
                    : "plugin.loggingutils.ARCHIVE_MESSAGE_OK";

            notificationService.fireNotification(
                    LOGFILES_ARCHIVED,
                    resources.getI18NString(
                            "plugin.loggingutils.ARCHIVE_BUTTON"),
                    resources.getI18NString(
                            bodyMsgKey,
                            new String[]{dest.getAbsolutePath()}),
                    null);
        }
    }

    /**
     * Shows a dialog with input for logs description.
     */
    private void uploadLogs()
    {
        ResourceManagementService resources =
            LoggingUtilsActivator.getResourceService();

        final SIPCommDialog dialog = new SIPCommDialog(false)
        {
            /**
             * Serial version UID.
             */
            private static final long serialVersionUID = 0L;

            /**
             * Dialog is closed. Do nothing.
             * @param escaped <tt>true</tt> if this dialog has been
             * closed by pressing
             */
            @Override
            protected void close(boolean escaped)
            {}
        };

        dialog.setModal(true);
        dialog.setTitle(resources.getI18NString(
            "plugin.loggingutils.UPLOAD_LOGS_BUTTON"));

        Container container = dialog.getContentPane();
        container.setLayout(new GridBagLayout());

        JLabel descriptionLabel = new JLabel("Add a comment:");
        final JTextArea commentTextArea = new JTextArea();
        commentTextArea.setRows(4);
        final JButton uploadButton = new JButton(
            resources.getI18NString("plugin.loggingutils.UPLOAD_BUTTON"));
        final SIPCommTextField emailField = new SIPCommTextField(resources
            .getI18NString("plugin.loggingutils.ARCHIVE_UPREPORT_EMAIL"));
        final JCheckBox emailCheckBox = new SIPCommCheckBox(
            "Email me when more information is available");
        emailCheckBox.setSelected(true);
        emailCheckBox.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                if(!emailCheckBox.isSelected())
                {
                    uploadButton.setEnabled(true);
                    emailField.setEnabled(false);
                }
                else
                {
                    emailField.setEnabled(true);

                    if(emailField.getText() != null
                        && emailField.getText().trim().length() > 0)
                        uploadButton.setEnabled(true);
                    else
                        uploadButton.setEnabled(false);
                }
            }
        });

        emailField.getDocument().addDocumentListener(new DocumentListener()
        {
            public void insertUpdate(DocumentEvent e)
            {
                updateButtonsState();
            }

            public void removeUpdate(DocumentEvent e)
            {
                updateButtonsState();
            }

            public void changedUpdate(DocumentEvent e){}

            /**
             * Check whether we should enable upload button.
             */
            private void updateButtonsState()
            {
                if(emailCheckBox.isSelected() && emailField.getText() != null
                    && emailField.getText().trim().length() > 0)
                    uploadButton.setEnabled(true);
                else
                    uploadButton.setEnabled(false);
            }
        });

        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.insets = new Insets(10, 10, 3, 10);
        c.weightx = 1.0;
        c.gridx = 0;
        c.gridy = 0;

        container.add(descriptionLabel, c);

        c.insets = new Insets(0, 10, 10, 10);
        c.gridy = 1;
        container.add(new JScrollPane(commentTextArea), c);

        c.insets = new Insets(0, 10, 0, 10);
        c.gridy = 2;
        container.add(emailCheckBox, c);

        c.insets = new Insets(0, 10, 10, 10);
        c.gridy = 3;
        container.add(emailField, c);

        JButton cancelButton = new JButton(
            resources.getI18NString("service.gui.CANCEL"));
        cancelButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                dialog.dispose();
            }
        });

        uploadButton.setEnabled(false);
        uploadButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                try
                {
                    final ArrayList<String> paramNames = new ArrayList<String>();
                    final ArrayList<String> paramValues = new ArrayList<String>();

                    if(emailCheckBox.isSelected())
                    {
                        paramNames.add("Email");
                        paramValues.add(emailField.getText());
                    }

                    paramNames.add("Description");
                    paramValues.add(commentTextArea.getText());

                    // don't block the UI thread we may need to show
                    // some ui for password input if protected area on the way
                    new Thread(new Runnable()
                    {
                        public void run()
                        {
                            uploadLogs(
                                getUploadLocation(),
                                LogsCollector.getDefaultFileName(),
                                paramNames.toArray(new String[]{}),
                                paramValues.toArray(new String[]{}));
                        }
                    }).start();
                }
                finally
                {
                    dialog.dispose();
                }
            }
        });
        JPanel buttonsPanel = new TransparentPanel(
            new FlowLayout(FlowLayout.RIGHT));
        buttonsPanel.add(uploadButton);
        buttonsPanel.add(cancelButton);

        c.anchor = GridBagConstraints.LINE_END;
        c.weightx = 0;
        c.gridy = 4;
        container.add(buttonsPanel, c);

        dialog.setVisible(true);
    }

    /**
     * Upload files to pre-configured url.
     * @param uploadLocation the location we are uploading to.
     * @param fileName the filename we use for the resulting filename we upload.
     * @param params the optional parameter names.
     * @param values the optional parameter values.
     */
    static void uploadLogs(
        String uploadLocation,
        String fileName,
        String[] params,
        String[] values)
    {
        try
        {
            File tempDir = LoggingUtilsActivator.getFileAccessService()
                .getTemporaryDirectory();
            File newDest = new File(tempDir, fileName);

            File optionalFile = null;

            // if we have some description params
            // save them to file and add it to archive
            if(params != null)
            {
                optionalFile = new File(
                    LoggingUtilsActivator.getFileAccessService().
                        getTemporaryDirectory(),
                    "description.txt");
                OutputStream out = new FileOutputStream(optionalFile);
                for(int i = 0; i < params.length; i++)
                {
                    out.write((params[i] + " : "
                        + values[i] + "\r\n").getBytes("UTF-8"));
                }
                out.flush();
                out.close();
            }

            newDest = LogsCollector.collectLogs(newDest, optionalFile);

            // don't leave any unneeded information
            if(optionalFile != null)
                optionalFile.delete();

            if(uploadLocation == null)
                return;

            if(HttpUtils.postFile(uploadLocation, "logs", newDest) != null)
            {
                NotificationService notificationService
                    = LoggingUtilsActivator.getNotificationService();

                if(notificationService != null)
                {
                    ResourceManagementService resources
                        = LoggingUtilsActivator.getResourceService();
                    String bodyMsgKey = "plugin.loggingutils.ARCHIVE_MESSAGE_OK";

                    notificationService.fireNotification(
                            LOGFILES_ARCHIVED,
                            resources.getI18NString(
                                    "plugin.loggingutils.ARCHIVE_BUTTON"),
                            resources.getI18NString(
                                    bodyMsgKey,
                                    new String[]{uploadLocation}),
                            null);
                }
            }
        }
        catch(Throwable e)
        {
            logger.error("Cannot upload file", e);
        }
    }
}
