/*
 *  Kontalk Java client
 *  Copyright (C) 2016 Kontalk Devteam <devteam@kontalk.org>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.kontalk.view;

import javax.swing.Box;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Optional;

import com.alee.extended.layout.FormLayout;
import com.alee.extended.panel.GroupPanel;
import com.alee.extended.panel.GroupingType;
import com.alee.laf.button.WebButton;
import com.alee.laf.checkbox.WebCheckBox;
import com.alee.laf.label.WebLabel;
import com.alee.laf.optionpane.WebOptionPane;
import com.alee.laf.panel.WebPanel;
import com.alee.laf.separator.WebSeparator;
import com.alee.laf.text.WebTextArea;
import com.alee.laf.text.WebTextField;
import com.alee.managers.tooltip.TooltipManager;
import org.kontalk.misc.JID;
import org.kontalk.model.Contact;
import org.kontalk.util.Tr;
import org.kontalk.view.AvatarLoader.AvatarImg;
import org.kontalk.view.ComponentUtils.LabelTextField;

/**
 * Show and edit contact details.
 *
 * @author Alexander Bikadorov {@literal <bikaejkb@mail.tu-berlin.de>}
 */
final class ContactDetails extends WebPanel implements ObserverTrait {

    private static final Map<Contact, ContactDetails> CACHE = new HashMap<>();

    private final View mView;
    private final Contact mContact;
    private final ComponentUtils.EditableAvatarImage mAvatarImage;
    private final WebTextField mNameField;
    private final WebLabel mSubscrStatus;
    private final WebButton mSubscrButton;
    private final WebLabel mKeyStatus;
    private final WebLabel mFPLabel;
    private final WebButton mUpdateButton;
    private final WebTextArea mFPArea;
    private final WebCheckBox mEncryptionBox;

    private ContactDetails(View view, Contact contact) {
        mView = view;
        mContact = contact;

        GroupPanel groupPanel = new GroupPanel(View.GAP_BIG, false);
        groupPanel.setMargin(View.MARGIN_BIG);

        groupPanel.add(new WebLabel(Tr.tr("Contact details")).setBoldFont());
        groupPanel.add(new WebSeparator(true, true));

        WebPanel mainPanel = new WebPanel(new FormLayout(View.GAP_DEFAULT, View.GAP_DEFAULT));

        // editable components
        mAvatarImage = new ComponentUtils.EditableAvatarImage(View.AVATAR_DETAIL_SIZE) {
            @Override
            void onImageChange(Optional<BufferedImage> optImage) {
                if (optImage.isPresent())
                    mView.getControl().setCustomContactAvatar(mContact, optImage.get());
                else
                    mView.getControl().unsetCustomContactAvatar(mContact);
            }
            @Override
            AvatarImg defaultImage() {
                return AvatarLoader.load(mContact, View.AVATAR_DETAIL_SIZE);
            }
            @Override
            boolean canRemove() {
                return mContact.hasCustomAvatarSet();
            }
            @Override
            protected void update() {
                if (mContact.hasCustomAvatarSet())
                    return;

                super.update();
            }
        };

        int size = View.AVATAR_DETAIL_SIZE + View.MARGIN_DEFAULT;
        mainPanel.add(new WebPanel(false, mAvatarImage)
                .setPreferredWidth(size).setPreferredHeight(size));
        mainPanel.add(Box.createGlue());

        mainPanel.add(new WebLabel(Tr.tr("Display Name:")));
        mNameField = new LabelTextField(View.MAX_NAME_LENGTH, 15, this) {
            @Override
            protected String labelText() {
                return mContact.getName();
            }
            @Override
            protected String editText() {
                return mContact.getName();
            }
            @Override
            protected void onFocusLost() {
                ContactDetails.this.saveName(this.getText().trim());
            }
        };
        mNameField.setFontSizeAndStyle(14, true, false);
        mainPanel.add(mNameField);

        mainPanel.add(new WebLabel("Jabber ID:"));
        LabelTextField jidField =
                new LabelTextField(View.MAX_JID_LENGTH, 20, this) {
            @Override
            protected String labelText() {
                return Utils.jid(mContact.getJID(), View.PRETTY_JID_LENGTH);
            }
            @Override
            protected String editText() {
                return mContact.getJID().asUnescapedString();
            }
            @Override
            protected void onFocusLost() {
                ContactDetails.this.saveJID(JID.bare(this.getText().trim()));
            }
        };
        String jidText = Tr.tr("The unique address of this contact");
        TooltipManager.addTooltip(jidField, jidText);
        mainPanel.add(jidField);

        mainPanel.add(new WebLabel(Tr.tr("Authorization:")));
        mSubscrStatus = new WebLabel();
        String subscrText = Tr.tr("Permission to view presence status and public key");
        TooltipManager.addTooltip(mSubscrStatus, subscrText);

        mSubscrButton = new WebButton(Tr.tr("Request"));
        String reqText = Tr.tr("Request status authorization from contact");
        TooltipManager.addTooltip(mSubscrButton, reqText);
        mSubscrButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mView.getControl().sendSubscriptionRequest(mContact);
            }
        });
        mainPanel.add(new GroupPanel(GroupingType.fillFirst,
                View.GAP_DEFAULT, mSubscrStatus, mSubscrButton));

        groupPanel.add(mainPanel);

        groupPanel.add(new WebSeparator(true, true));

        WebPanel keyPanel = new WebPanel(new FormLayout(View.GAP_DEFAULT, View.GAP_DEFAULT));

        keyPanel.add(new WebLabel(Tr.tr("Public Key")+":"));
        mKeyStatus = new WebLabel();
        mUpdateButton = new WebButton(Utils.getIcon("ic_ui_reload.png"));
        String updText = Tr.tr("Update key");
        TooltipManager.addTooltip(mUpdateButton, updText);
        mUpdateButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                mView.getControl().requestKey(ContactDetails.this.mContact);
            }
        });
        keyPanel.add(new GroupPanel(GroupingType.fillFirst,
                View.GAP_DEFAULT, mKeyStatus, mUpdateButton));

        mFPLabel = new WebLabel(Tr.tr("Fingerprint:"));
        keyPanel.add(mFPLabel);
        mFPArea = Utils.createFingerprintArea();
        String fpText = Tr.tr("The unique ID of this contact's key");
        TooltipManager.addTooltip(mFPArea, fpText);
        mFPLabel.setAlignmentY(Component.TOP_ALIGNMENT);
        keyPanel.add(mFPArea);

        // set everything that can change
        this.updateOnEDT(null, null);

        groupPanel.add(keyPanel);

        mEncryptionBox = new WebCheckBox(Tr.tr("Use Encryption"));
        mEncryptionBox.setAnimated(false);
        mEncryptionBox.setSelected(mContact.getEncrypted());
        String encText = Tr.tr("Encrypt and sign all messages send to this contact");
        TooltipManager.addTooltip(mEncryptionBox, encText);
        mEncryptionBox.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                mContact.setEncrypted(mEncryptionBox.isSelected());
            }
        });
        groupPanel.add(new GroupPanel(mEncryptionBox, Box.createGlue()));

        this.add(groupPanel, BorderLayout.WEST);

        WebPanel gradientPanel = new WebPanel(false) {
             @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                int w = this.getWidth();
                int h = this.getHeight();
                BufferedImage mCached = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
                Graphics2D cachedG = mCached.createGraphics();
                GradientPaint p2 = new GradientPaint(
                        0, 0, this.getBackground(),
                        w, 0, Color.LIGHT_GRAY);
                cachedG.setPaint(p2);
                cachedG.fillRect(0, 0, w, h);
                g.drawImage(mCached, 0, 0, this.getWidth(), this.getHeight(), null);
            }
        };
        this.add(gradientPanel, BorderLayout.CENTER);
    }

    void setRenameFocus() {
        mNameField.requestFocusInWindow();
    }

    @Override
    public void updateOnEDT(Observable o, Object arg) {
        // may have changed: avatar...
        mAvatarImage.update();
        // ...contact name...
        mNameField.setText(mContact.getName());
        mNameField.setInputPrompt(mContact.getName());
        Contact.Subscription subscription = mContact.getSubScription();
        String auth = Tr.tr("Unknown");
        switch(subscription) {
            case PENDING: auth = Tr.tr("Awaiting reply"); break;
            case SUBSCRIBED: auth = Tr.tr("Authorized"); break;
            case UNSUBSCRIBED: auth = Tr.tr("Not authorized"); break;
        }
        // ...subscription...
        mSubscrButton.setVisible(subscription != Contact.Subscription.SUBSCRIBED);
        mSubscrButton.setEnabled(subscription == Contact.Subscription.UNSUBSCRIBED);
        mSubscrStatus.setText(auth);
        // ...and/or key
        String hasKey = "<html>";
        if (mContact.hasKey()) {
            hasKey += Tr.tr("Available")+"</html>";
            TooltipManager.removeTooltips(mKeyStatus);
            mFPArea.setText(Utils.fingerprint(mContact.getFingerprint()));
            mFPLabel.setVisible(true);
            mFPArea.setVisible(true);
        } else {
            hasKey += "<font color='red'>"+Tr.tr("Not Available")+"</font></html>";
            String keyText = Tr.tr("The public key for this contact could not yet be received");
            TooltipManager.addTooltip(mKeyStatus, keyText);
            mFPLabel.setVisible(false);
            mFPArea.setVisible(false);
        }
        mKeyStatus.setText(hasKey);
        mUpdateButton.setEnabled(mContact.isKontalkUser() &&
                subscription == Contact.Subscription.SUBSCRIBED);
    }

    private void saveName(String name) {
        if (name.equals(mContact.getName()))
            return;

        mView.getControl().changeName(mContact, name);
    }

    private void saveJID(JID jid) {
        if (!jid.isValid() || jid.equals(mContact.getJID()))
            // TODO feedback for invalid jid
            return;

        String warningText =
                Tr.tr("Changing the JID is only useful in very rare cases. Are you sure?");
        int selectedOption = WebOptionPane.showConfirmDialog(this,
                warningText,
                Tr.tr("Please Confirm"),
                WebOptionPane.OK_CANCEL_OPTION,
                WebOptionPane.WARNING_MESSAGE);
        if (selectedOption == WebOptionPane.OK_OPTION)
            mView.getControl().changeJID(mContact, jid);
    }

    static ContactDetails instance(View view, Contact contact) {
        if (!CACHE.containsKey(contact)) {
            ContactDetails newContactDetails = new ContactDetails(view, contact);
            contact.addObserver(newContactDetails);
            CACHE.put(contact, newContactDetails);
        }

        return CACHE.get(contact);
    }
}
