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

import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.jivesoftware.Spark;

import org.jivesoftware.resource.Res;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.component.CheckNode;
import org.jivesoftware.spark.component.CheckTree;
import org.jivesoftware.spark.component.TitlePanel;
import org.jivesoftware.spark.ui.ChatInputEditor;
import org.jivesoftware.spark.ui.ContactGroup;
import org.jivesoftware.spark.ui.ContactItem;
import org.jivesoftware.spark.ui.ContactList;
import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.layout.LayoutSettingsManager;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.impl.JidCreate;

/**
 * Allows for better selective broadcasting.
 *
 * @author Derek DeMoro
 */
public class BroadcastDialog extends JPanel {
    private final ChatInputEditor messageBox;
    private final JCheckBox OfflineUsers = new JCheckBox(Res.getString("checkbox.broadcast.hide.offline.user"));
    private final JRadioButton normalMessageButton;
    
    private final ArrayList<ArrayList<Object>> NodesGroups = new ArrayList<>();
    private final List<CheckNode> nodes = new ArrayList<>();
    private final List<CheckNode> groupNodes = new ArrayList<>();
    private final CheckNode rosterNode;
    private final CheckTree checkTree;
    private Integer OfflineGroup;

    public BroadcastDialog() {
        setLayout(new GridBagLayout());
        rosterNode = new CheckNode(Res.getString("title.roster"));
        checkTree = new CheckTree(rosterNode);
        final ContactList contactList = SparkManager.getWorkspace().getContactList();
        
        // creates the List for the Online Users
        String groupName = Res.getString("status.online");
        CheckNode groupNode = new CheckNode(groupName);
        groupNodes.add(groupNode);
        rosterNode.add(groupNode);
        List<String> onlineJIDs = new ArrayList<>();
        //ContactGroup groupp;
        for(ContactGroup group : contactList.getContactGroups())
	        for (ContactItem item : group.getContactItems()) 
	        {
	      	  if(item.isAvailable() && !onlineJIDs.contains(item.getJid().toString()))
	      	  {
	           CheckNode itemNode = new CheckNode(item.getDisplayName(), false, item.getIcon());
	           itemNode.setAssociatedObject(item.getJid().toString());
	           groupNode.add(itemNode);
	           nodes.add(itemNode);
	           onlineJIDs.add(item.getJid().toString());
	      	  }
	        }
	        
        // Build out from Roster
       
        for (ContactGroup group : contactList.getContactGroups()) {
            groupName = group.getGroupName();
            if (!group.hasAvailableContacts()) {
                continue;
            }
            groupNode = new CheckNode(groupName);
            groupNodes.add(groupNode);
            rosterNode.add(groupNode);
            
            // Now add contact items from contact group.
            for (ContactItem item : group.getContactItems()) {
                CheckNode itemNode = new CheckNode(item.getDisplayName(), false, item.getIcon());
                itemNode.setAssociatedObject(item.getJid().toString());
                groupNode.add(itemNode);
                nodes.add(itemNode);
            }

            for (ContactItem item : group.getOfflineContacts()) {
                CheckNode itemNode = new CheckNode(item.getDisplayName(), false, item.getIcon());
                itemNode.setAssociatedObject(item.getJid().toString());
                groupNode.add(itemNode);
                nodes.add(itemNode);
            }
        }

        messageBox = new ChatInputEditor();
        normalMessageButton = new JRadioButton(Res.getString("message.normal"));
        JRadioButton alertMessageButton = new JRadioButton(Res.getString("message.alert.notify"));

        ButtonGroup group = new ButtonGroup();
        group.add(normalMessageButton);
        group.add(alertMessageButton);

        final JScrollPane pane = new JScrollPane(messageBox);
        pane.setBorder(BorderFactory.createTitledBorder(Res.getString("label.message").replace("&", "")));

        final JScrollPane treePane = new JScrollPane(checkTree);
        treePane.setBorder(BorderFactory.createTitledBorder(Res.getString("message.send.to.these.people")));
        treePane.getVerticalScrollBar().setBlockIncrement(200);
        treePane.getVerticalScrollBar().setUnitIncrement(20);
        // Add to UI
        add(pane, new GridBagConstraints(0, 0, 1, 1, 0.5, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
        add(normalMessageButton, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 5, 5, 5), 0, 0));
        add(alertMessageButton, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(2, 5, 2, 5), 0, 0));
        add(treePane, new GridBagConstraints(1, 0, 1, 3, 0.5, 1.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(2, 5, 2, 5), 0, 0));
        add(OfflineUsers, new GridBagConstraints(1, 3, 1, 0, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(2, 5, 2, 5), 0, 0));

        OfflineUsers.addActionListener( e -> hideOfflineUsers() );

        normalMessageButton.setSelected(true);
        checkTree.expandTree();

        // get list of selected users
        java.util.Collection<ContactItem> selectedUsers = contactList.getSelectedUsers();
        // if selected users is 1 or less,
        //    don't per-select in dialog window (see SPARK-1088)
        if (selectedUsers.size() > 1) {
            // Iterate through selected users.
            for (ContactItem item : selectedUsers) {
                for (CheckNode node : nodes) {
                    if (node.getAssociatedObject().toString().equals(item.getJid().toString())) {
                        node.setSelected(true);
                    }
                }
            }
        }
    }

    public void invokeDialog(ContactGroup group) {
        for (CheckNode node : groupNodes) {
            if (node.getUserObject().toString().equals(group.getGroupName())) {
                node.setSelected(true);
            }
        }

        invokeDialog();
    }

    /**
     * Displays the broadcast dialog.
     */
    public void invokeDialog() {
        final JDialog dlg;
        TitlePanel titlePanel;
        // Create the title panel for this dialog
        titlePanel = new TitlePanel(Res.getString("title.broadcast.message"), Res.getString("message.enter.broadcast.message"), null, true);

        // Construct main panel w/ layout.
        final JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());
        mainPanel.add(titlePanel, BorderLayout.NORTH);

        // The user should only be able to close this dialog.
        JButton okButton = new JButton(Res.getString("ok"));
        JButton closeButton = new JButton(Res.getString("close"));
        
        mainPanel.add(this,BorderLayout.CENTER);
        
        JPanel buttonpanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        buttonpanel.add(okButton);
        buttonpanel.add(closeButton);
        
        mainPanel.add(buttonpanel,BorderLayout.SOUTH);

        dlg = new JDialog(SparkManager.getMainWindow(), Res.getString("broadcast"));
        dlg.setContentPane(mainPanel);
        dlg.pack();


        final Rectangle bounds = LayoutSettingsManager.getLayoutSettings().getBroadcastMessageBounds();
        if ( bounds == null || bounds.width <= 0 || bounds.height <= 0 )
        {
            // Use default settings.
            dlg.setSize( 800, 600 );
            dlg.setLocationRelativeTo( null );
        }
        else
        {
            dlg.setBounds( bounds );
        }

        dlg.addComponentListener( new ComponentAdapter()
        {
            @Override
            public void componentResized( ComponentEvent e )
            {
                LayoutSettingsManager.getLayoutSettings().setBroadcastMessageBounds( dlg.getBounds() );
            }

            @Override
            public void componentMoved( ComponentEvent e )
            {
                LayoutSettingsManager.getLayoutSettings().setBroadcastMessageBounds( dlg.getBounds() );
            }
        } );

        dlg.setResizable(false);

        // Add listener
        okButton.addActionListener(e -> {
            try {
                if (sendBroadcasts(dlg)) {
                    dlg.setVisible(false);
                }
            } catch (SmackException.NotConnectedException e1) {
                Log.warning("Unable to broadcast.", e1);
            }
        });

        closeButton.addActionListener( e -> dlg.setVisible(false) );

        dlg.setVisible(true);
        dlg.toFront();
        dlg.requestFocus();

        messageBox.requestFocus();        
    }

    private void hideOfflineUsers() {
        if (OfflineUsers.isSelected()) {
            ContactList contactList = SparkManager.getWorkspace().getContactList();
            int i = 0;
            for (CheckNode node : nodes) {
                String jidStr = (String)node.getAssociatedObject();
                if (contactList.getContactItemByJID(jidStr).getPresence().getType() == Presence.Type.unavailable) {
                    if (node.getParent() == null) {
                        continue;
                    }
                    TreeNode parent = node.getParent();
                    TreeNode[] path = ((DefaultTreeModel) checkTree.getTree().getModel()).getPathToRoot(parent);
                    ((DefaultTreeModel) checkTree.getTree().getModel()).removeNodeFromParent(node);
                    checkTree.getTree().setSelectionPath(new TreePath(path));
                    NodesGroups.add(new ArrayList<>());
                    NodesGroups.get(i).add(parent);
                    NodesGroups.get(i).add(node);
                    i++;
                }
            }
            for (int x = 0; x < groupNodes.size(); x++) {
                if (groupNodes.get(x).toString().equals(Res.getString("group.offline"))) {
                    OfflineGroup = x;
                    TreeNode parent = groupNodes.get(x).getParent();
                    TreeNode[] path = ((DefaultTreeModel) checkTree.getTree().getModel()).getPathToRoot(parent);
                    ((DefaultTreeModel) checkTree.getTree().getModel()).removeNodeFromParent(groupNodes.get(x));
                    checkTree.getTree().setSelectionPath(new TreePath(path));
                }
            }
        } else {
            DefaultMutableTreeNode child = groupNodes.get(OfflineGroup);
            ((DefaultTreeModel) checkTree.getTree().getModel()).insertNodeInto(child, rosterNode, rosterNode.getChildCount());
            TreeNode[] path = ((DefaultTreeModel) checkTree.getTree().getModel()).getPathToRoot(rosterNode);
            checkTree.getTree().expandPath(new TreePath(path));
            checkTree.expandTree();
            int i = 0;
            for (CheckNode node : nodes) {
                if (node.getParent() != null) {
                    continue;
                }
                child = (CheckNode) NodesGroups.get(i).get(1);
                CheckNode parent = (CheckNode) NodesGroups.get(i).get(0);
                ((DefaultTreeModel) checkTree.getTree().getModel()).insertNodeInto(child, parent, parent.getChildCount());
                path = ((DefaultTreeModel) checkTree.getTree().getModel()).getPathToRoot(node);
                checkTree.getTree().expandPath(new TreePath(path));
                checkTree.expandTree();
                i++;
            }
        }
    }
    
    /**
     * Sends a broadcast message to all users selected.
     */
    private boolean sendBroadcasts(JDialog dlg) throws SmackException.NotConnectedException
    {
        Set<BareJid> jids = new HashSet<>();
        for (CheckNode node : nodes) {
            if (node.isSelected()) {
                String jid = (String)node.getAssociatedObject();
                jids.add(JidCreate.bareFromOrThrowUnchecked(jid));
            }
        }
        if (jids.isEmpty())
        {
            JOptionPane.showMessageDialog(dlg, Res.getString("message.broadcast.no.user.selected"), Res.getString("title.error"), JOptionPane.ERROR_MESSAGE);
            return false;
        }
        String text = messageBox.getText();
        if (!ModelUtil.hasLength(text)) {
            JOptionPane.showMessageDialog(dlg, Res.getString("message.broadcast.no.text"), Res.getString("title.error"), JOptionPane.ERROR_MESSAGE);
            return false;
        }
        List<String> recipients = new ArrayList<>(jids.size());
        for (BareJid jid : jids) {
            MessageBuilder messageBuilder = StanzaBuilder.buildMessage()
                .ofType(normalMessageButton.isSelected() ? Message.Type.normal : Message.Type.headline)
                .to(jid)
                .setBody(text);
            Message message = messageBuilder.build();

            try {
                SparkManager.getConnection().sendStanza(message);
            } catch (InterruptedException e) {
                Log.error("Error sending broadcast message to " + jid, e);
                break;
            }
            String nickname = SparkManager.getUserManager().getUserNicknameFromJID(jid);
            recipients.add(nickname);
        }
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"+" - "+"HH:mm");
        Date date = new Date();
        String out=dateFormat.format(date)+" ("+String.join(", ", recipients)+"): "+text+"\n";
        try {
            addDataToFile(out);
        } catch (IOException ex) {
            Log.error("Couldn't add data to file", ex);
        }
        return true;
    }

    private  void addDataToFile(String data) throws IOException
    {
         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM");
        Date date = new Date();
        String fileName = "broadcast_history." + dateFormat.format(date) + ".txt";
        File transcriptsFolder = SparkManager.getTranscriptDir();
        File file = new File(transcriptsFolder, fileName);
        if (!file.exists()) {
            //noinspection ResultOfMethodCallIgnored
            file.createNewFile();
        }
        FileWriter fileWriter = new FileWriter(file, true);
        BufferedWriter out = new BufferedWriter(fileWriter);
        out.write(data);
        out.close();
    }
}
