/*
 * 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.jabberaccregwizz;

import java.awt.*;
import java.awt.event.*;

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

import net.java.sip.communicator.util.*;

import net.java.sip.communicator.plugin.desktoputil.*;
import net.java.sip.communicator.service.protocol.*;
/**
 * @author Yana Stamcheva
 */
public class AccountPanel
    extends TransparentPanel
    implements  DocumentListener,
                ValidatingPanel
{
    /**
     * Serial version UID.
     */
    private static final long serialVersionUID = 0L;

    private static final Logger logger = Logger.getLogger(AccountPanel.class);

    private final JPanel userIDPassPanel
        = new TransparentPanel(new BorderLayout(10, 10));

    private final JPanel labelsPanel = new TransparentPanel();

    private final JPanel valuesPanel = new TransparentPanel();

    private final JLabel passLabel
        = new JLabel(Resources.getString("service.gui.PASSWORD"));

    private final JPanel emptyPanel = new TransparentPanel();

    final JTextField userIDField = new TrimTextField();

    private final JPasswordField passField = new JPasswordField();

    private final JCheckBox rememberPassBox = new SIPCommCheckBox(Resources
        .getString("service.gui.REMEMBER_PASSWORD"));

    /**
     * Panel to hold the "change password" button
     */
    private final JPanel changePasswordPanel
            = new TransparentPanel(new BorderLayout(10,10));

    /**
     * "Change password" button
     */
    private final JButton changePasswordButton
        = new JButton(Resources.getString(
            "plugin.jabberaccregwizz.CHANGE_PASSWORD"));

    /**
     * A pane to show a message in the "change password" panel
     */
    private final JEditorPane changePasswordMessagePane = new JEditorPane();

    private final JabberAccountRegistrationForm parentForm;

    private final JRadioButton existingAccountButton;

    private final JRadioButton createAccountButton;

    private final JPanel mainPanel
        = new TransparentPanel(new BorderLayout(5, 5));

    private Component registrationForm;

    private Component registerChoicePanel;

    /**
     * Creates an instance of <tt>AccountPanel</tt> by specifying the parent
     * wizard page, where it's contained.
     * @param parentForm the parent form where this panel is contained
     */
    public AccountPanel(final JabberAccountRegistrationForm parentForm)
    {
        super(new BorderLayout());

        this.parentForm = parentForm;
        this.parentForm.addValidatingPanel(this);

        JLabel userIDLabel
            = new JLabel(parentForm.getUsernameLabel());

        JLabel userIDExampleLabel = new JLabel(parentForm.getUsernameExample());

        labelsPanel.setLayout(new BoxLayout(labelsPanel, BoxLayout.Y_AXIS));

        valuesPanel.setLayout(new BoxLayout(valuesPanel, BoxLayout.Y_AXIS));

        userIDField.getDocument().addDocumentListener(this);
        rememberPassBox.setSelected(true);

        existingAccountButton = new JRadioButton(
            parentForm.getExistingAccountLabel());

        createAccountButton = new JRadioButton(
            parentForm.getCreateAccountLabel());

        userIDExampleLabel.setForeground(Color.GRAY);
        userIDExampleLabel.setFont(userIDExampleLabel.getFont().deriveFont(8));
        emptyPanel.setMaximumSize(new Dimension(40, 35));
        userIDExampleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));

        labelsPanel.add(userIDLabel);
        labelsPanel.add(emptyPanel);
        labelsPanel.add(passLabel);

        valuesPanel.add(userIDField);
        valuesPanel.add(userIDExampleLabel);
        valuesPanel.add(passField);

        userIDPassPanel.add(labelsPanel, BorderLayout.WEST);
        userIDPassPanel.add(valuesPanel, BorderLayout.CENTER);
        userIDPassPanel.setBorder(
                BorderFactory.createTitledBorder(
                        Resources.getString(
                            "plugin.jabberaccregwizz.USERNAME_AND_PASSWORD")));

        JPanel southPanel = new TransparentPanel(new BorderLayout());
        southPanel.add(rememberPassBox, BorderLayout.WEST);

        String homeLinkString = parentForm.getHomeLinkLabel();

        if (homeLinkString != null && homeLinkString.length() > 0)
        {
            String homeLink = Resources.getSettingsString(
                    "service.gui.APPLICATION_WEB_SITE");

            southPanel.add( createHomeLink(homeLinkString, homeLink),
                            BorderLayout.EAST);
        }

        userIDPassPanel.add(southPanel, BorderLayout.SOUTH);

        changePasswordPanel.setBorder(BorderFactory.createTitledBorder(
                Resources.getString(
                    "plugin.jabberaccregwizz.CHANGE_PASSWORD")));

        changePasswordButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                JabberPasswordChangeDialog passwordChangeDialog
                        = new JabberPasswordChangeDialog();
                passwordChangeDialog.setVisible(true);
            }
        });

        changePasswordMessagePane.setOpaque(false);
        changePasswordMessagePane.setBorder(BorderFactory.
                createEmptyBorder(0, 5, 10, 0));
        changePasswordMessagePane.setEditable(false);

        changePasswordPanel.add(changePasswordMessagePane, BorderLayout.NORTH);
        changePasswordPanel.add(changePasswordButton, BorderLayout.SOUTH);

        //we only show that when showChangePasswordPanel is called
        changePasswordPanel.setVisible(false);

        this.add(mainPanel, BorderLayout.NORTH);
    }
    /**
     * Creates a register choice panel.
     * @return the created component
     */
    private Component createRegisterChoicePanel()
    {
        JPanel registerChoicePanel = new TransparentPanel(new GridLayout(0, 1));

        existingAccountButton.addChangeListener(new ChangeListener()
        {
            public void stateChanged(ChangeEvent e)
            {
                if (existingAccountButton.isSelected())
                {
                    mainPanel.remove(registrationForm);
                    mainPanel.add(userIDPassPanel, BorderLayout.CENTER);

                    Window window
                        = SwingUtilities.getWindowAncestor(AccountPanel.this);

                    if (window != null)
                        window.pack();
                }
            }
        });

        createAccountButton.addChangeListener(new ChangeListener()
        {
            public void stateChanged(ChangeEvent e)
            {
                if (createAccountButton.isSelected())
                {
                    mainPanel.remove(userIDPassPanel);
                    mainPanel.add(registrationForm, BorderLayout.CENTER);
                    SwingUtilities.getWindowAncestor(AccountPanel.this).pack();
                }
            }
        });

        ButtonGroup buttonGroup = new ButtonGroup();

        existingAccountButton.setOpaque(false);
        createAccountButton.setOpaque(false);

        buttonGroup.add(existingAccountButton);
        buttonGroup.add(createAccountButton);

        registerChoicePanel.add(existingAccountButton);
        registerChoicePanel.add(createAccountButton);

        // By default we select the existing account button.
        existingAccountButton.setSelected(true);

        return registerChoicePanel;
    }

    /**
     * Creates the home link label.
     *
     * @param homeLinkText the text of the home link
     * @param homeLink the link
     * @return the created component
     */
    private Component createHomeLink(   String homeLinkText,
                                        final String homeLink)
    {
        JLabel homeLinkLabel =
            new JLabel("<html><a href='"+ homeLink +"'>"
                + homeLinkText + "</a></html>",
                JLabel.RIGHT);

        homeLinkLabel.setFont(homeLinkLabel.getFont().deriveFont(10f));
        homeLinkLabel.setCursor(new Cursor(Cursor.HAND_CURSOR));
        homeLinkLabel.setToolTipText(
                Resources.getString(
                "plugin.simpleaccregwizz.SPECIAL_SIGNUP"));
        homeLinkLabel.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                try
                {
                    JabberAccRegWizzActivator.getBrowserLauncher()
                        .openURL(homeLink);
                }
                catch (UnsupportedOperationException ex)
                {
                    // This should not happen, because we check if the
                    // operation is supported, before adding the sign
                    // up.
                    logger.error("The web sign up is not supported.",
                        ex);
                }
            }
        });

        return homeLinkLabel;
    }

    /**
     * Handles the <tt>DocumentEvent</tt> triggered when user types in the
     * UserID field. Enables or disables the "Next" wizard button according to
     * whether the UserID field is empty.
     *
     * @param evt the document event that has triggered this method call.
     */
    public void insertUpdate(DocumentEvent evt)
    {
        parentForm.setServerFieldAccordingToUIN(userIDField.getText());
        parentForm.reValidateInput();
    }

    /**
     * Handles the <tt>DocumentEvent</tt> triggered when user deletes letters
     * from the User ID field. Enables or disables the "Next" wizard button
     * according to whether the User ID field is empty.
     *
     * @param evt the document event that has triggered this method call.
     */
    public void removeUpdate(DocumentEvent evt)
    {
        parentForm.setServerFieldAccordingToUIN(userIDField.getText());
        parentForm.reValidateInput();
    }

    public void changedUpdate(DocumentEvent evt) {}

    /**
     * Returns the username entered in this panel.
     * @return the username entered in this panel
     */
    String getUsername()
    {
        return userIDField.getText();
    }

    /**
     * Sets the username to display in the username field.
     * @param username the username to set
     */
    void setUsername(String username)
    {
        userIDField.setText(username);
    }

    /**
     * Returns the password entered in this panel.
     * @return the password entered in this panel
     */
    char[] getPassword()
    {
        return passField.getPassword();
    }

    /**
     * Sets the password to display in the password field of this panel.
     * @param password the password to set
     */
    void setPassword(String password)
    {
        passField.setText(password);
    }

    /**
     * Indicates if the remember password box is selected.
     * @return <tt>true</tt> if the remember password check box is selected,
     * otherwise returns <tt>false</tt>
     */
    boolean isRememberPassword()
    {
        return rememberPassBox.isSelected();
    }

    /**
     * Selects/deselects the remember password check box depending on the given
     * <tt>isRemember</tt> parameter.
     * @param isRemember indicates if the remember password checkbox should be
     * selected or not
     */
    void setRememberPassword(boolean isRemember)
    {
        rememberPassBox.setSelected(isRemember);
    }

    /**
     * Whether current inserted values into the panel are valid and enough
     * to continue with account creation/modification.
     *
     * @return whether the input values are ok to continue with account
     * creation/modification.
     */
    public boolean isValidated()
    {
        return userIDField.getText() != null
                && userIDField.getText().length() > 0;
    }

    /**
     * Sets to <tt>true</tt> if this panel is opened in a simple form and
     * <tt>false</tt> if it's opened in an advanced form.
     *
     * @param isSimpleForm indicates if this panel is opened in a simple form or
     * in an advanced form
     */
    void setSimpleForm(boolean isSimpleForm)
    {
        JabberAccountCreationFormService createAccountService
            = parentForm.getCreateAccountService();

        mainPanel.removeAll();

        if (isSimpleForm)
        {
            if (createAccountService != null)
            {
                registrationForm = createAccountService.getForm();
                registerChoicePanel = createRegisterChoicePanel();

                mainPanel.add(registerChoicePanel, BorderLayout.NORTH);
            }
            else
            {
                JPanel registerPanel = new TransparentPanel();

                registerPanel.setLayout(
                    new BoxLayout(registerPanel, BoxLayout.Y_AXIS));

                String createAccountInfoString
                    = parentForm.getCreateAccountLabel();

                if (createAccountInfoString != null
                    && createAccountInfoString.length() > 0)
                {
                    registerPanel.add(
                        createRegisterArea(createAccountInfoString));
                }

                String createAccountString
                    = parentForm.getCreateAccountButtonLabel();

                if (createAccountString != null
                        && createAccountString.length() > 0)
                {
                    JPanel buttonPanel = new TransparentPanel(
                            new FlowLayout(FlowLayout.CENTER));

                    buttonPanel.add(createRegisterButton(createAccountString));

                    registerPanel.add(buttonPanel);
                }

                mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
                mainPanel.add(userIDPassPanel);
//                mainPanel.add(Box.createVerticalStrut(10));
//
//                if (registerPanel.getComponentCount() > 0)
//                {
//                    registerPanel.setBorder(
//                        BorderFactory.createTitledBorder(""));
//
//                    mainPanel.add(registerPanel);
//                }
            }
        }
        else
        {
            mainPanel.add(userIDPassPanel, BorderLayout.NORTH);
            mainPanel.add(changePasswordPanel, BorderLayout.SOUTH);
        }
    }

    /**
     * Indicates if the account information provided by this form is for new
     * account or an existing one.
     * @return <tt>true</tt> if the account information provided by this form
     * is for new account or <tt>false</tt> if it's for an existing one
     */
    boolean isCreateAccount()
    {
        return createAccountButton.isSelected();
    }

    /**
     * Creates the register area.
     *
     * @param text the text to show to the user
     * @return the created component
     */
    private Component createRegisterArea(String text)
    {
        JEditorPane registerArea = new JEditorPane();

        registerArea.setAlignmentX(JEditorPane.CENTER_ALIGNMENT);
        registerArea.setOpaque(false);
        registerArea.setContentType("text/html");
        registerArea.setEditable(false);
        registerArea.setText(text);
        /* Display the description with the font we use elsewhere in the UI. */
        registerArea.putClientProperty(
                JEditorPane.HONOR_DISPLAY_PROPERTIES,
                true);
        registerArea.addHyperlinkListener(new HyperlinkListener()
            {
                public void hyperlinkUpdate(HyperlinkEvent e)
                {
                    if (e.getEventType()
                            .equals(HyperlinkEvent.EventType.ACTIVATED))
                    {
                        JabberAccRegWizzActivator
                            .getBrowserLauncher().openURL(e.getURL().toString());
                    }
                }
            });

        return registerArea;
    }

    /**
     * Creates the register button.
     *
     * @param text the text of the button
     * @return the created component
     */
    private Component createRegisterButton(String text)
    {
        JButton registerButton = new JButton(text);

        registerButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent evt)
            {
                if (logger.isDebugEnabled())
                    logger.debug("Reg OK");

                if (parentForm.isWebSignupSupported())
                {
                    parentForm.webSignup();
                }
            }
        });

        return registerButton;
    }
    /**
     * Shows or hides the "change password" panel
     */
    public void showChangePasswordPanel(boolean show)
    {
        if(!show)
        {
            changePasswordPanel.setVisible(false);
            return;
        }
        else
        {
            ProtocolProviderService protocolProvider =
                        parentForm.getWizard().getProtocolProvider();

            //the button will only be enabled if our preliminary checks (the
            //account is logged in the server supports inband registers) succeed
            changePasswordButton.setEnabled(false);
            if (protocolProvider == null)
            {
                //we shouldn't get here, because this dialog only shows
                //when editing an existing account
                logger.warn("protocolProvider is null");
                changePasswordMessagePane.setText(
                    "Cannot change password for this account");
            }
            else if (!protocolProvider.isRegistered())
            {
                changePasswordMessagePane.setText(Resources.getString(
                            "plugin.jabberaccregwizz.HAS_TO_BE_LOGGED_IN"));
            }
            else if (!protocolProvider.getOperationSet(
                OperationSetChangePassword.class).supportsPasswordChange())
            {
                changePasswordMessagePane.setText(Resources.getString(
                    "plugin.jabberaccregwizz.SERVER_NOT_SUPPORT_PASSWORD_CHANGE"));
            }
            else
            {
                changePasswordMessagePane.setVisible(false);
                changePasswordButton.setEnabled(true);
            }

            changePasswordPanel.setVisible(true);
        }
    }

    /**
     * A "change password" dialog.
     */
    private class JabberPasswordChangeDialog extends PasswordChangeDialog
    {

        /**
         * Default constructor.
         */
        public JabberPasswordChangeDialog()
        {
            super(false);

            setTitle(Resources.
                       getString("plugin.jabberaccregwizz.CHANGE_PASSWORD"));
            setInfoText(Resources.
                       getString("plugin.jabberaccregwizz.ENTER_NEW_PASSWORD"));

            this.getOkButton().addActionListener(okButtonListener);
        }

        /**
         * This is the ActionListener for the "ok" button.
         */
        private final ActionListener okButtonListener = new ActionListener()
        {
            /**
             * Action for the "ok" button. Checks whether the account is logged
             * in, tries to change the password and displays the appropriate
             * messages.
             */
            public void actionPerformed(ActionEvent e)
            {
                String newPass = getNewPassword();

                ProtocolProviderService protocolProvider =
                        parentForm.getWizard().getProtocolProvider();

                if (protocolProvider == null)
                {
                    //we shouldn't get here, because this dialog only shows
                    //when editing an existing account
                    logger.warn("protocolProvider is null in change"
                            + " password dialog");
                    displayPopupError("Could not change password");
                    return;
                }
                else if (!protocolProvider.isRegistered())
                {
                    //editing an account, which is not logged in
                    displayPopupError(Resources.getString(
                            "plugin.jabberaccregwizz.HAS_TO_BE_LOGGED_IN"));
                    return;
                }
                else if (protocolProvider.getTransportProtocol()
                        != TransportProtocol.TLS)
                {
                    //XEP-0077 advices agains changing password unless
                    //the underlying stream is encrypted
                    displayPopupError(Resources.getString(
                            "plugin.jabberaccregwizz.TLS_REQUIRED"));
                    return;
                }
                else //try to change
                {
                    if (logger.isInfoEnabled())
                    {
                        logger.info("Trying to change password for jabber"
                                + " account "
                                + protocolProvider.
                                getAccountID().getAccountAddress());
                    }

                    OperationSetChangePassword opset =
                            protocolProvider.getOperationSet(
                            OperationSetChangePassword.class);
                    try
                    {
                        opset.changePassword(newPass);

                        /**
                         * If the old password was stored, update it with the
                         * new one. If the old password was not stored, leave it
                         * like it is.
                         */
                        if (isRememberPassword())
                        {
                            try
                            {
                                if(logger.isInfoEnabled())
                                    logger.info("Storing new password for"
                                            + " account " + protocolProvider.
                                            getAccountID().getAccountAddress());

                                storeNewPassword(newPass);
                            }
                            catch (IllegalArgumentException ex)
                            {
                                //If we get here its a bug, and showing a
                                //message like this might be unappropriate. But
                                //the user would want to know about it!
                                logger.warn("Failed to store password for"
                                            + " account " + protocolProvider.
                                            getAccountID().getAccountAddress(),
                                        ex);
                                displayPopupError(Resources.getString(
                                    "plugin.jabberaccregwizz.PASSWORD_NOT_STORED"));
                            }
                        }
                        //now update the password field in AccountPanel,
                        //because it still has the old pass and if the user
                        //completes the wizard it will store it.
                        passField.setText(newPass);

                        displayPopupInfo(Resources.getString(
                                "plugin.jabberaccregwizz.PASSWORD_CHANGED"));
                        dispose();

                    }
                    catch (IllegalStateException ex) {
                        //we already checked for this, but if the connection
                        //has since been lost smack will throw this
                        displayPopupError(Resources.getString(
                                "plugin.jabberaccregwizz.HAS_TO_BE_LOGGED_IN"));
                    }
                    catch (OperationFailedException ex) {
                        displayPopupError(Resources.getString(
                            "plugin.jabberaccregwizz.SERVER_NOT_SUPPORT_PASSWORD_CHANGE"));
                    }
                }
            }
        };

        /**
         * Stores the new password in the account configuration
         * @param newPass The new password
         * @throws IllegalArgumentException on failure (from
         * ProtocolProviderFactory.storePassword)
         */
        void storeNewPassword(String newPass) throws IllegalArgumentException
        {
            AccountID accountID = parentForm.getWizard().getProtocolProvider().
                    getAccountID();
            ProtocolProviderFactory protocolProviderFactory =
                JabberAccRegWizzActivator.getJabberProtocolProviderFactory();

            protocolProviderFactory.storePassword(accountID, newPass);
        }
    }
}
