/**
 * Copyright (C) 2004-2011 Jive Software. All rights reserved.
 *
 * 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 org.jivesoftware.sparkimpl.plugin.chat;

import org.jivesoftware.resource.Res;
import org.jivesoftware.resource.Default;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.RosterGroup;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.spark.PresenceManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.plugin.ContextMenuListener;
import org.jivesoftware.spark.plugin.Plugin;
import org.jivesoftware.spark.ui.ContactGroup;
import org.jivesoftware.spark.ui.ContactItem;
import org.jivesoftware.spark.ui.ContactList;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.UIComponentRegistry;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.manager.Enterprise;
import org.jivesoftware.sparkimpl.settings.local.LocalPreferences;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;

import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * Adds extra functionallity to the <code>ContactList</code>.
 * This includes copying and moving of <code>ContactItem</code>.
 */
public class ContactListAssistantPlugin implements Plugin {
    private JMenu moveToMenu;
    private JMenu copyToMenu;
    private final LocalPreferences localPreferences = SettingsManager.getLocalPreferences();
    
    @Override
    public void initialize() {
        moveToMenu = new JMenu(Res.getString("menuitem.move.to"));
        copyToMenu = new JMenu(Res.getString("menuitem.copy.to"));
        final ContactList contactList = SparkManager.getContactList();
        contactList.addContextMenuListener(new ContextMenuListener() {
            @Override
            public void poppingUp(Object object, final JPopupMenu popup) {
                List<ContactItem> contactItems = contactList.getSelectedUsers();
                if (!contactItems.isEmpty()) {
                    final List<ContactGroup> contactGroups = contactList.getContactGroups();
                    for (final ContactGroup group : contactGroups) {
                        if (group.isUnfiledGroup() || group.isOfflineGroup()) {
                            continue;
                        }
                        if (isContactItemInGroup(contactItems, group)) {
                        	continue;
                        }
                        final Action moveAction = new AbstractAction() {
			    @Override
                            public void actionPerformed(ActionEvent actionEvent) {
                                moveItems(contactItems, group.getGroupName());
                            }
                        };

                        final Action copyAction = new AbstractAction() {
			    @Override
                            public void actionPerformed(ActionEvent actionEvent) {
                                copyItems(contactItems, group.getGroupName());
                            }
                        };

                        moveAction.putValue(Action.NAME, group.getGroupName());
                        moveToMenu.add(moveAction);

                        copyAction.putValue(Action.NAME, group.getGroupName());
                        copyToMenu.add(copyAction);
                    }

                    popup.addPopupMenuListener(new PopupMenuListener() {
                        @Override
                        public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) {
                        }

                        @Override
                        public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent) {
                            moveToMenu.removeAll();
                            copyToMenu.removeAll();
                            popup.removePopupMenuListener(this);
                        }

                        @Override
                        public void popupMenuCanceled(PopupMenuEvent popupMenuEvent) {
                            moveToMenu.removeAll();
                            copyToMenu.removeAll();
                            popup.removePopupMenuListener(this);
                        }
                    });

                    int index = -1;
                    if (!Default.getBoolean(Default.DISABLE_RENAMES) && Enterprise.containsFeature(Enterprise.RENAMES_FEATURE)) {
                    	for (int i = 0; i < popup.getComponentCount(); i++) {
                    		Object o = popup.getComponent(i);
                    		if (o instanceof JMenuItem && ((JMenuItem)o).getText().equals(Res.getString("menuitem.rename"))) {
                    			index = i;
                    			break;
                    		}
                    	}

                    } else index = 3;

                    if (contactItems.size() == 1) {
                        // Add MOVE/COPY options right after the RENAME option or in it's place if it doesn't exist.
                        if (index != -1) {
                        	// See if we should disable the "Move to" and "Copy to" menu options
                        	if (!Default.getBoolean(Default.DISABLE_MOVE_AND_COPY) && Enterprise.containsFeature(Enterprise.MOVE_COPY_FEATURE)) {
                        		popup.add(moveToMenu, index + 1);
                        		popup.add(copyToMenu, index + 2);
                        	}
                        }
                    }
                    else if (contactItems.size() > 1) {
                    	// See if we should disable the "Move to" and "Copy to" menu options
                    	if (!Default.getBoolean(Default.DISABLE_MOVE_AND_COPY) && Enterprise.containsFeature(Enterprise.MOVE_COPY_FEATURE)) {
                    		popup.addSeparator();
                    		popup.add(moveToMenu);
                    		popup.add(copyToMenu);
                   	}                        

                        // Clean up the extra separator if "Broadcast" menu items are disabled
                        if (!Default.getBoolean(Default.DISABLE_BROADCAST_MENU_ITEM) && Enterprise.containsFeature(Enterprise.BROADCAST_FEATURE)) popup.addSeparator();
                    }
                }
            }

            @Override
            public boolean handleDefaultAction(MouseEvent e) {
                return false;
            }
        });

        updateAvatarsInContactList();

        SettingsManager.addPreferenceListener( preference -> updateAvatarsInContactList() );
    }
    
	private boolean isContactItemInGroup(Collection<ContactItem> contactItems, ContactGroup group) {
		for (ContactItem ci : contactItems) {
			if (group.getContactItemByJID(ci.getJid(), true) != null) {
				return true;
			}
		}
		return false;
	}

    /**
     * Moves a collection of <code>ContactItem</code>s to the specified group.
     *
     * @param contactItems the contact items to move.
     * @param groupName    the name of the group to move to.
     */
    private void moveItems(Collection<ContactItem> contactItems, String groupName) {
        final ContactGroup contactGroup = getContactGroup(groupName);
        ContactGroup oldGroup;
        for (ContactItem contactItem : contactItems) {
        	oldGroup = getContactGroup(contactItem.getGroupName());
        	if (oldGroup.isSharedGroup()) {
        		continue;
        	}
            addContactItem(contactGroup, contactItem, true);
        }
    }

    /**
     * Copies a collection of <code>ContactItem</code>s to a specified group.
     *
     * @param contactItems the collection of contact items.
     * @param groupName    the name of the group to move to.
     */
    private void copyItems(Collection<ContactItem> contactItems, String groupName) {
        final ContactGroup contactGroup = getContactGroup(groupName);
        for (ContactItem contactItem : contactItems) {
            addContactItem(contactGroup, contactItem, false);
        }
    }

    @Override
    public void shutdown() {
    }

    @Override
    public boolean canShutDown() {
        return false;
    }

    @Override
    public void uninstall() {
    }

    private void updateContactItem(ContactItem contactItem) {
        contactItem.updateAvatarInSideIcon();
    }

    private void updateAvatarsInContactList() {
        final ContactList contactList = SparkManager.getContactList();
        for (ContactGroup contactGroup : contactList.getContactGroups()) {
            if (contactGroup.isOfflineGroup()) {
                continue;
            }

            for (ContactItem contactItem : contactGroup.getContactItems()) {
                updateContactItem(contactItem);
            }
        }
    }

    /**
     * Copies or moves a new <code>ContactItem</code> into the <code>ContactGroup</code>.
     *
     * @param contactGroup the ContactGroup.
     * @param item         the ContactItem to move.
     * @param move         true if the ContactItem should be moved, otherwise false.
     */
    private void addContactItem(final ContactGroup contactGroup, final ContactItem item, final boolean move) {
        ContactItem newContact = UIComponentRegistry.createContactItem(item.getAlias(), item.getNickname(), item.getJid());
        newContact.setPresence(item.getPresence());
        newContact.setIcon(item.getIcon());
        newContact.getNicknameLabel().setFont(item.getNicknameLabel().getFont());
        boolean groupHadAvailableContacts = false;
        
        // Do not copy/move a contact item only if it is not already in the Group.
        if (contactGroup.getContactItemByJID(item.getJid(), true) != null) {
            return;
        }

        if (!PresenceManager.isOnline(item.getJid())) {
            contactGroup.addOfflineContactItem(item.getAlias(), item.getNickname(), item.getJid(), null);
        }
        else {
            groupHadAvailableContacts = contactGroup.hasAvailableContacts();
            contactGroup.addContactItem(newContact);
        }
        contactGroup.clearSelection();
        contactGroup.fireContactGroupUpdated(); //Updating group title

        final ContactGroup oldGroup = getContactGroup(item.getGroupName());

        
        final boolean groupAvailableContacts = groupHadAvailableContacts;
        SwingWorker worker = new SwingWorker() {
            @Override
            public Object construct() {
                Roster roster = SparkManager.getRoster();
                RosterEntry entry = roster.getEntry(item.getJid());
                RosterGroup groupFound = null;
                for (RosterGroup group : roster.getGroups()) {
                    if (group.getName().equals(contactGroup.getGroupName())) {
                        try {
                            groupFound = group;
                            if (!groupAvailableContacts)
                            {
                        	SparkManager.getContactList().toggleGroupVisibility(groupFound.getName(), true);
                            }
                            group.addEntry(entry);
                        }
                        catch (XMPPException | SmackException | InterruptedException e1) {
                            Log.error(e1);
                            return false;
                        }
                    }
                }

                // This is a new group
                if (groupFound == null) {
                    groupFound = roster.createGroup(contactGroup.getGroupName());
                    try {
                	groupFound.addEntry(entry);
                        if (!groupAvailableContacts)
                        {
                    	SparkManager.getContactList().toggleGroupVisibility(groupFound.getName(), true);
                        }  
                    }
                    catch (XMPPException | SmackException | InterruptedException e) {
                        Log.error(e);
                    }
                }
                return true;
            }

            @Override
            public void finished() {
                if ((Boolean)get()) {
                    // Now try and remove the group from the old one.
                    if (move) {
                        removeContactItem(oldGroup, item);
                       if (!localPreferences.isEmptyGroupsShown() && !oldGroup.hasAvailableContacts())
                        {
                          SparkManager.getContactList().toggleGroupVisibility(oldGroup.getGroupName(),false);
                        }
                    }
                }
            }

        };

        worker.start();
    }


    public boolean removeContactItem(ContactGroup contactGroup, ContactItem item) {
        if (contactGroup.isSharedGroup()) {
            return false;
        }

        if (contactGroup.isUnfiledGroup()) {
            contactGroup.removeContactItem(item);
            contactGroup.fireContactGroupUpdated();
            return true;
        }

        // Remove entry from Roster Group
        Roster roster = SparkManager.getRoster();
        RosterEntry entry = roster.getEntry(item.getJid());
        RosterGroup rosterGroup = null;
        for (RosterGroup group : roster.getGroups()) {
            if (group.getName().equals(contactGroup.getGroupName())) {
                try {
                    rosterGroup = group;
                    group.removeEntry(entry);
                }
                catch (XMPPException | SmackException | InterruptedException e1) {
                    return false;
                }
            }
        }

        if (rosterGroup == null) {
            return false;
        }

        if (!rosterGroup.contains(entry)) {
            contactGroup.removeContactItem(item);
            contactGroup.fireContactGroupUpdated(); //Updating group title
            return true;
        }

        return false;
    }

    private ContactGroup getContactGroup(String groupName) {
        ContactList contactList = SparkManager.getWorkspace().getContactList();
        return contactList.getContactGroup(groupName);
    }

}
