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

import org.jivesoftware.phone.client.*;
import org.jivesoftware.phone.client.action.PhoneActionIQProvider;
import org.jivesoftware.phone.client.event.PhoneEventPacketExtensionProvider;
import org.jivesoftware.resource.Res;
import org.jivesoftware.resource.SparkRes;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.phone.PhoneManager;
import org.jivesoftware.spark.plugin.ContextMenuListener;
import org.jivesoftware.spark.plugin.Plugin;
import org.jivesoftware.spark.ui.*;
import org.jivesoftware.spark.ui.rooms.ChatRoomImpl;
import org.jivesoftware.spark.util.*;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.alerts.SparkToaster;
import org.jivesoftware.sparkimpl.plugin.idle.UserIdlePlugin;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
import org.jxmpp.util.XmppStringUtils;

import javax.swing.*;
import java.awt.event.*;
import java.util.TimerTask;

public class PhonePlugin implements Plugin {
    public static PhoneClient phoneClient;
    private DialPanel dialPanel;
    //    private Alert incomingDialog;
    private JFrame dialDialog;

    public static Presence offPhonePresence;
    public static Presence onPhonePresence;
    public void initialize() {
        ProviderManager.addExtensionProvider("phone-event", "http://jivesoftware.com/xmlns/phone", new PhoneEventPacketExtensionProvider());
        ProviderManager.addIQProvider("phone-action", "http://jivesoftware.com/xmlns/phone", new PhoneActionIQProvider());

        final XMPPConnection con = SparkManager.getConnection();


        SwingWorker worker = new SwingWorker() {
            public Object construct() {
                try {
                    phoneClient = new PhoneClient(con);

                    // Add BaseListener
                    phoneClient.addEventListener(new PhoneListener());
                }
                catch (Exception e) {
                    // Ignore because the user does not have support.
                    //Log.debug(e);
                }
                return phoneClient;
            }

            public void finished() {
                if (phoneClient != null) {
                    setupPhoneSystem();
                }
            }
        };

        worker.start();
    }

    private void setupPhoneSystem() {
        // Add Dial Menu
        final JMenu viewMenu = SparkManager.getMainWindow().getMenuByName(Res.getString("menuitem.actions"));
        JMenuItem dialNumberMenu = new JMenuItem(SparkRes.getImageIcon(SparkRes.ON_PHONE_IMAGE));
        ResourceUtils.resButton(dialNumberMenu, Res.getString("button.dial.number"));

        // Add Listener
        dialNumberMenu.addActionListener( e -> {
            dialPanel = new DialPanel();
            dialPanel.getDialButton().addActionListener( e1 -> {
                String number = dialPanel.getNumberToDial();
                if (ModelUtil.hasLength(number)) {
                    dialPanel.setText(Res.getString("message.calling", number));
                    dialPanel.changeToRinging();
                    callExtension(number);

                }

            } );

            dialDialog = PhoneDialog.invoke(dialPanel, Res.getString("title.dial.phone"), Res.getString("message.number.to.call"), null);
            dialPanel.getDialField().requestFocusInWindow();

            dialPanel.getDialField().addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent e) {
                    if (e.getKeyChar() == KeyEvent.VK_ENTER) {
                        try {
                            String number = dialPanel.getNumberToDial();
                            if (ModelUtil.hasLength(number)) {
                                dialPanel.setText(Res.getString("message.calling", number));
                                dialPanel.changeToRinging();
                                callExtension(number);

                            }
                            e.consume();
                        }
                        catch (Exception ex) {
                            Log.error(ex);
                        }
                    }
                }
            });
        } );
        viewMenu.add(dialNumberMenu);

        // Add ChatRoomListener to call users based on JID
        SparkManager.getChatManager().addChatRoomListener(new ChatRoomListenerAdapter() {
            public void chatRoomOpened(final ChatRoom room) {
                if (room instanceof ChatRoomImpl) {
                    final ChatRoomButton callButton = new ChatRoomButton("", SparkRes.getImageIcon(SparkRes.TELEPHONE_24x24));
                    callButton.setToolTipText(Res.getString("tooltip.place.a.call"));
                    final ChatRoomImpl chatRoom = (ChatRoomImpl)room;
                    boolean phoneEnabled = false;
                    try {
                        phoneEnabled = phoneClient.isPhoneEnabled( XmppStringUtils.parseBareJid(chatRoom.getParticipantJID()));
                    }
                    catch (Exception e) {
                        Log.error(e);
                    }

                    if (phoneEnabled) {
                        room.addChatRoomButton(callButton);
                        callButton.addActionListener( e -> callJID(chatRoom.getParticipantJID()) );
                    }
                }
            }
        });

        ContactList contactList = SparkManager.getWorkspace().getContactList();
        contactList.addContextMenuListener(new ContextMenuListener() {
            public void poppingUp(Object object, final JPopupMenu popup) {
                if (object instanceof ContactItem) {
                    final ContactItem item = (ContactItem)object;

                    boolean phoneEnabled = false;


                    try {
                        phoneEnabled = phoneClient.isPhoneEnabled(item.getJID());
                    }
                    catch (Exception e) {
                        Log.error("There was an error retrieving phone information.", e);
                    }

                    if (phoneEnabled) {
                        Action callAction = new AbstractAction() {
							private static final long serialVersionUID = 7221741748743018431L;

							public void actionPerformed(ActionEvent e) {
                                callJID(item.getJID());
                            }
                        };

                        callAction.putValue(Action.NAME, "Call");
                        callAction.putValue(Action.SMALL_ICON, SparkRes.getImageIcon(SparkRes.ON_PHONE_IMAGE));
                        popup.add(callAction);
                    }
                }
            }

            public void poppingDown(JPopupMenu popup) {

            }

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

    private class PhoneListener extends BasePhoneEventListener {

        public void handleOnPhone(OnPhoneEvent event) {
            if (dialDialog != null) {
                dialDialog.setVisible(false);
            }

            // Get current presence if necessary.
            offPhonePresence = SparkManager.getWorkspace().getStatusBar().getPresence();


            // Send "on the phone" presence
            onPhonePresence = new Presence(Presence.Type.available, "On the phone", 1, Presence.Mode.away);
            SparkManager.getSessionManager().changePresence(onPhonePresence);
        }


        public void handleHangUp(HangUpEvent event) {
            onPhonePresence = null;
            if (dialDialog != null) {
                dialDialog.setVisible(false);
            }

            if (offPhonePresence != null) {

                if (((offPhonePresence.getMode().equals(Presence.Mode.away)) && (!UserIdlePlugin.getDesktopLockStatus())
                        && (UserIdlePlugin.latestPresence != null)) && (!UserIdlePlugin.latestPresence.getStatus().contentEquals("On the Phone"))) {
                    SparkManager.getSessionManager().changePresence(UserIdlePlugin.latestPresence);
                    Log.debug("PhonePlugin: Setting presence from UserIdlePlugin");

                } else if ((UserIdlePlugin.getDesktopLockStatus()) && ((offPhonePresence.getStatus().equals("Online"))
                        || (offPhonePresence.getStatus().equals("Free to chat")))) {
                    Presence presence = new Presence(Presence.Type.available, UserIdlePlugin.pref.getIdleMessage(), 1, Presence.Mode.away);
                    SparkManager.getSessionManager().changePresence(presence);
                    Log.debug("PhonePlugin: Desktop is Locked - Setting presence from pref.idle message");

                } else if (UserIdlePlugin.getDesktopLockStatus() && (!offPhonePresence.isAway())) {
                    Presence presence = new Presence(Presence.Type.available, offPhonePresence.getStatus(), 1, Presence.Mode.away);
                    SparkManager.getSessionManager().changePresence(presence);
                    Log.debug("PhonePlugin: Desktop is Locked - Setting presence from user defined presence");

                } else {
                    // Set user to previous presence state when all phone calls are hung up.
                    SparkManager.getSessionManager().changePresence(offPhonePresence);
                    Log.debug("PhonePlugin: Setting Presence from PhonePlugin.");
                }


            } else {
                // If no previous state available, set status to Available
                Presence availablePresence = new Presence(Presence.Type.available, "Online", 1, Presence.Mode.available);

                SparkManager.getSessionManager().changePresence(availablePresence);
                Log.debug("no previous state available from Phone Plugin..setting to Online");

            }
        }

        public void handleRing(final RingEvent event) {
            final TimerTask task = new SwingTimerTask() {
                public void doRun() {
                    String callerID = event.getCallerID();
                    if (ModelUtil.hasLength(callerID)) {
                        String number = PhoneManager.getNumbersFromPhone(callerID);
                        if (PhoneManager.getInstance().containsCurrentCall(number)) {
                            return;
                        }
                    }
                    displayRingUI(event);
                }
            };

            TaskEngine.getInstance().schedule(task, 1000);
        }
    }

    private void displayRingUI(RingEvent event) {
        IncomingCall incomingCall = new IncomingCall();
        boolean idExists = false;
        if (ModelUtil.hasLength(event.getCallerIDName())) {
            incomingCall.setCallerName(event.getCallerIDName());
            idExists = true;
        }

        if (ModelUtil.hasLength(event.getCallerID())) {
            incomingCall.setCallerNumber(event.getCallerID());
            idExists = true;
        }

        if (!idExists) {
            incomingCall.setCallerName(Res.getString("message.no.caller.id"));
        }

        if (!SettingsManager.getLocalPreferences().getDisableAsteriskToasterPopup()) {
            SparkToaster toasterManager = new SparkToaster();
            toasterManager.setTitle("Incoming Phone Call");
            toasterManager.setDisplayTime(15000);
            toasterManager.showToaster(SparkRes.getImageIcon(SparkRes.ON_PHONE_IMAGE));
            toasterManager.setComponent(incomingCall);

        }
    }


    public void callExtension(final String number) {
        final Runnable caller = () -> {
            try {
                phoneClient.dialByExtension(number);
            }
            catch (PhoneActionException e) {
                Log.error(e);
            }
        };

        TaskEngine.getInstance().submit(caller);
    }

    public void callJID(final String jid) {
        final Runnable caller = () -> {
            try {
                phoneClient.dialByJID(jid);
            }
            catch (PhoneActionException e) {
                Log.error(e);
            }
        };

        TaskEngine.getInstance().submit(caller);
    }


    public static PhoneClient getPhoneClient() {
        return phoneClient;
    }

    public void shutdown() {

    }

    public boolean canShutDown() {
        return true;
    }

    public void uninstall() {
        // Do nothing.
    }


}
