/**
 * 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.sparkplugin.ui.call;

import net.java.sipmack.sip.Call;
import net.java.sipmack.sip.InterlocutorUI;
import net.java.sipmack.sip.event.CallListener;
import net.java.sipmack.sip.event.CallStateEvent;
import net.java.sipmack.softphone.SoftPhoneManager;
import net.java.sipmack.softphone.listeners.InterlocutorListener;
import org.jivesoftware.resource.Res;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.StanzaBuilder;
import org.jivesoftware.smackx.vcardtemp.packet.VCard;
import org.jivesoftware.spark.ChatManager;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.component.tabbedPane.SparkTab;
import org.jivesoftware.spark.component.tabbedPane.SparkTabbedPaneListener;
import org.jivesoftware.spark.phone.PhoneManager;
import org.jivesoftware.spark.plugin.phone.resource.PhoneRes;
import org.jivesoftware.spark.ui.ChatContainer;
import org.jivesoftware.spark.ui.ChatRoom;
import org.jivesoftware.spark.util.SwingWorker;
import org.jivesoftware.spark.util.log.Log;
import org.jivesoftware.sparkimpl.plugin.alerts.SparkToaster;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;

/**
 */
public class CallManager implements InterlocutorListener {

    private static CallManager singleton;
    private static final Object LOCK = new Object();

    private SoftPhoneManager softPhone;

    private final Map<String, PhonePanel> calls = new HashMap<String, PhonePanel>();

    private Presence offPhonePresence;

    private Map<InterlocutorUI, SparkToaster> toasters = new HashMap<InterlocutorUI, SparkToaster>();

    /**
     * Returns the singleton instance of <CODE>CallManager</CODE>,
     * creating it if necessary.
     * <p/>
     *
     * @return the singleton instance of <Code>CallManager</CODE>
     */
    public static CallManager getInstance() {
        // Synchronize on LOCK to ensure that we don't end up creating
        // two singletons.
        synchronized (LOCK) {
            if (null == singleton) {
                CallManager controller = new CallManager();
                singleton = controller;
                return controller;
            }
        }
        return singleton;
    }


    private CallManager() {
        softPhone = SoftPhoneManager.getInstance();
        softPhone.addInterlocutorListener(this);

        SparkManager.getChatManager().getChatContainer().addSparkTabbedPaneListener(new SparkTabbedPaneListener() {
            public void tabRemoved(SparkTab tab, Component component, int index) {
                removePhonePanel(component);
            }

            public void tabAdded(SparkTab tab, Component component, int index) {

            }

            public void tabSelected(SparkTab tab, Component component, int index) {
            }

            public void allTabsRemoved() {
            }


            public boolean canTabClose(SparkTab tab, Component component) {
                PhonePanel phonePanel = null;
                if (component instanceof PhonePanel) {
                    phonePanel = (PhonePanel) component;
                }

                if (phonePanel == null && component instanceof ChatRoom) {
                    final ChatRoom chatRoom = (ChatRoom) component;
                    Component rightComponent = chatRoom.getSplitPane().getRightComponent();
                    if (rightComponent instanceof PhonePanel) {
                        phonePanel = (PhonePanel) rightComponent;
                    }
                }

                if (phonePanel == null) {
                    return true;
                }

                if (component instanceof PhonePanel) {
                    final InterlocutorUI ic = phonePanel.getActiveCall();
                    if (ic.getCallState().equals(Call.CONNECTED)) {
                        // Prompt to close this window
                        int ok = JOptionPane.showConfirmDialog(component, PhoneRes.getIString("phone.closeconfirm"), PhoneRes.getIString("phone.confirmation"), JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
                        boolean close = (ok == JOptionPane.YES_OPTION);
                        if (close) {
                            // End Call
                            softPhone.getDefaultGuiManager().hangup(ic);
                            return true;
                        } else {
                            return false;
                        }
                    }
                }

                return true;
            }
        });
    }


    public void busy() {

    }

    private void showIncomingCall(final InterlocutorUI ic) {
   	 
   	 try {
	   	 EventQueue.invokeAndWait(new Runnable(){
	   		 public void run()
	   		 {
	   			 final SparkToaster toasterManager = new SparkToaster();
	   			 toasters.put(ic, toasterManager);
	   			 final IncomingCallUI incomingCall = new IncomingCallUI(ic);
	   			 toasterManager.setToasterHeight(230);
	   			 toasterManager.setToasterWidth(300);
	   			 toasterManager.setDisplayTime(500000000);
	   			 
	   			 toasterManager.showToaster(PhoneRes.getIString("phone.incomingcall"), incomingCall);
	   			 toasterManager.hideTitle();
	   			 
	   			 toasterManager.setHidable(false);
	   			 incomingCall.getAcceptButton().addActionListener(new ActionListener() {
	   				 public void actionPerformed(ActionEvent e) {
	   					 SparkManager.getMainWindow().bringFrameIntoFocus();
	   					 closeToaster(ic);
	   					 SoftPhoneManager.getInstance().getDefaultGuiManager().answer();
	   					 
	   				 }
	   			 });
	   			 incomingCall.getRejectButton().addActionListener(new ActionListener() {
	   				 public void actionPerformed(ActionEvent e) {
	   					 closeToaster(ic);
	   					 SoftPhoneManager.getInstance().getDefaultGuiManager().hangup(ic);
	   				 }
	   			 });
	   		 }
	   	 });
   	 }
   	 catch(Exception e) {
   		 Log.error(e);
   	 }


    }

    private void showOutgoingCall(final InterlocutorUI ic) {
        final SparkToaster toasterManager = new SparkToaster();
        toasters.put(ic, toasterManager);
        final OutgoingCallUI incomingCall = new OutgoingCallUI(ic);
        toasterManager.setToasterHeight(230);
        toasterManager.setToasterWidth(300);
        toasterManager.setDisplayTime(500000000);
        toasterManager.setHidable(false);

        toasterManager.showToaster(PhoneRes.getIString("phone.incomingcall"), incomingCall);
        toasterManager.hideTitle();

        incomingCall.getRejectButton().addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                closeToaster(ic);
                SoftPhoneManager.getInstance().getDefaultGuiManager().hangup(ic);
            }
        });
    }


    public void interlocutorAdded(final InterlocutorUI interlocutorUI) {

        Presence current = SparkManager.getWorkspace().getStatusBar().getPresence();

        // Set offline presence if necessary.
        if (offPhonePresence == null || !(current.getType().equals(Presence.Type.available) && current.getStatus().equals("On the phone") && current.getMode().equals(Presence.Mode.away))) {
            offPhonePresence = current;
        }

        // Send on phone presence
        Presence onPhonePresence = StanzaBuilder.buildPresence()
            .ofType(Presence.Type.available)
            .setStatus("On the phone")
            .setPriority(1)
            .setMode(Presence.Mode.away)
            .build();

        SparkManager.getSessionManager().changePresence(onPhonePresence);

        interlocutorUI.getCall().addStateChangeListener(new CallListener() {
            public void callStateChanged(CallStateEvent evt) {
                final String callState = evt.getNewState();

                String callNumber = interlocutorUI.getCall().getNumber();
                callNumber = SoftPhoneManager.getNumbersFromPhone(callNumber);
                PhonePanel panel = calls.get(callNumber);

                if (callState.equals(Call.CONNECTED)) {
                    closeToaster(interlocutorUI);
                    showCallAnswered(interlocutorUI);
                } else if (callState.equals(Call.DISCONNECTED)) {
                    closeToaster(interlocutorUI);

                    if (panel != null) {
                        panel.callEnded();
                    }

                    PhoneManager.getInstance().removeCurrentCall(callNumber);
                } else if (callState.equals(Call.RINGING)) {

                } else if (callState.equals(Call.BUSY)) {
                    closeToaster(interlocutorUI);
                } else if (callState.equals(Call.ALERTING)) {
                    showIncomingCall(interlocutorUI);
                    PhoneManager.getInstance().addCurrentCall(callNumber);
                }
            }
        });

        final String callState = interlocutorUI.getCallState();
        if (callState != null && (callState.equals(Call.DIALING)) || callState.equals(Call.RINGING)) {
            showOutgoingCall(interlocutorUI);
        }
    }

    private void showCallAnswered(final InterlocutorUI interlocutorUI) {
        final ChatManager chatManager = SparkManager.getChatManager();
        try {
        
	        EventQueue.invokeAndWait(new Runnable(){
	      	  public void run()
	      	  {
			        String phoneNumber = interlocutorUI.getCall().getNumber();
			        phoneNumber = SoftPhoneManager.getNumbersFromPhone(phoneNumber);
			
			        PhonePanel panel = calls.get(phoneNumber);
			        ChatRoom chatRoom = null;
			        if (panel == null) {
			
			            // Let's check to see if the Contact Exists
			            final VCard vcard = SparkManager.getVCardManager().searchPhoneNumber(phoneNumber);
			            if (vcard != null) {
			                panel = new RosterMemberPanel();
			                panel.setInterlocutorUI(interlocutorUI);
			
			                String jid = vcard.getJabberId();
			                String nickname = SparkManager.getUserManager().getUserNicknameFromJID(jid);
			                chatRoom = chatManager.createChatRoom(jid, nickname, nickname);
			                chatRoom.getSplitPane().setRightComponent(panel);
			                chatRoom.getSplitPane().setResizeWeight(.80);
			                chatRoom.getSplitPane().setDividerSize(5);
			
			                chatManager.getChatContainer().activateChatRoom(chatRoom);
			            } else {
			                panel = new NonRosterPanel();
			                panel.setInterlocutorUI(interlocutorUI);
			                chatManager.getChatContainer().addContainerComponent(panel);
			                chatManager.getChatContainer().activateComponent(panel);
			            }
			
			            calls.put(phoneNumber, panel);
			        } else {
			            panel.setInterlocutorUI(interlocutorUI);
			        }
			
			        if (chatRoom != null) {
			            SoftPhoneManager.getInstance().addCallSession(chatRoom, SoftPhoneManager.CallRoomState.inCall);
			            SparkManager.getChatManager().notifySparkTabHandlers(chatRoom);
			        } else {
			            SoftPhoneManager.getInstance().addCallSession(panel, SoftPhoneManager.CallRoomState.inCall);
			            SparkManager.getChatManager().notifySparkTabHandlers(panel);
			        }
	      	  }
	        });
        }
        catch(Exception e){
      	  Log.error(e);
        }
    }

    public void interlocutorRemoved(final InterlocutorUI interlocutorUI) {
        if (softPhone.getInterlocutors().size() == 0) {

            if (offPhonePresence == null) {

                // Set user to available when all phone calls are hung up.
                Presence availablePresence = StanzaBuilder.buildPresence()
                    .ofType(Presence.Type.available)
                    .setStatus("Online")
                    .setPriority(1)
                    .setMode(Presence.Mode.available)
                    .build();
                SparkManager.getSessionManager().changePresence(availablePresence);

            } else {

                // Set user presence to last one choosed
                SparkManager.getSessionManager().changePresence(offPhonePresence);

            }

            offPhonePresence = null;

        }

        final SwingWorker delay = new SwingWorker() {
            public Object construct() {
                try {
                    Thread.sleep(2000);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return true;
            }

            public void finished() {
                String phoneNumber = interlocutorUI.getCall().getNumber();
                phoneNumber = SoftPhoneManager.getNumbersFromPhone(phoneNumber);

                final PhonePanel phonePanel = calls.get(phoneNumber);
                if (phonePanel == null) {
                    return;
                }

                for (ChatRoom room : SparkManager.getChatManager().getChatContainer().getChatRooms()) {
                    Component comp = room.getSplitPane().getRightComponent();
                    if (comp != null && comp instanceof PhonePanel) {
                        final PhonePanel cp = (PhonePanel) comp;
                        if (cp == phonePanel) {
                            room.getSplitPane().setRightComponent(null);
                            removePhonePanel(cp);

                            // Remove state mapping
                            softPhone.removeCallSession(room);

                            SparkManager.getChatManager().notifySparkTabHandlers(room);
                        }
                        return;
                    }
                }
            }
        };

        delay.start();
    }

    /**
     * Removes PhonePanel from ChatContainer or associated ChatRoom.
     *
     * @param component the PhonePanel or ChatRoom containing the PhonePanel.
     */
    private void removePhonePanel(Component component) {
        String phoneNumber = null;
        if (component instanceof PhonePanel) {
            final PhonePanel phonePanel = (PhonePanel) component;
            phoneNumber = phonePanel.getPhoneNumber();
            phoneNumber = SoftPhoneManager.getNumbersFromPhone(phoneNumber);
        } else if (component instanceof ChatRoom) {
            ChatRoom chatRoom = (ChatRoom) component;
            Component comp = chatRoom.getSplitPane().getRightComponent();
            if (comp != null && comp instanceof PhonePanel) {
                final PhonePanel phonePanel = (PhonePanel) comp;
                phoneNumber = phonePanel.getPhoneNumber();
                phoneNumber = SoftPhoneManager.getNumbersFromPhone(phoneNumber);
            }
        }

        if (phoneNumber != null) {
            calls.remove(phoneNumber);
        }
    }

    /**
     * Retrieves the <code>PhonePanel</code> object from the <code>ChatRoom</code>.
     * If no PhonePanel exists, then null is returned.
     *
     * @param chatRoom the ChatRoom.
     * @return the PhonePanel.
     */
    public PhonePanel getPhonePanel(ChatRoom chatRoom) {
        Component comp = chatRoom.getSplitPane().getRightComponent();
        if (comp != null && comp instanceof PhonePanel) {
            final PhonePanel phonePanel = (PhonePanel) comp;
            return phonePanel;
        }

        return null;
    }

    /**
     * Returns the associated Chat Room if one exists.
     *
     * @param phonePanel the <code>PhonePanel</code>.
     * @return the ChatRoom.
     */
    public ChatRoom getAssociatedChatRoom(PhonePanel phonePanel) {
        for (ChatRoom chatRoom : SparkManager.getChatManager().getChatContainer().getChatRooms()) {
            Component comp = chatRoom.getSplitPane().getRightComponent();
            if (phonePanel.equals(comp)) {
                return chatRoom;
            }
        }

        return null;
    }

    /**
     * Returns the <code>SparkTab</code> that acts as a container to this PhonePanel.
     *
     * @param phonePanel the <code>PhonePanel</code>
     * @return the SparkTab.
     */
    public SparkTab getSparkTab(PhonePanel phonePanel) {
        final ChatContainer chatContainer = SparkManager.getChatManager().getChatContainer();
        int tabCount = chatContainer.getTabCount();
        for (int i = 0; i < tabCount; i++) {
            SparkTab sparkTab = chatContainer.getTabAt(i);
            Component containerComponent = chatContainer.getComponentInTab(sparkTab);
            if (containerComponent.equals(phonePanel)) {
                return sparkTab;
            }

            if (containerComponent instanceof ChatRoom) {
                ChatRoom chatRoom = (ChatRoom) containerComponent;
                Component rightComponent = chatRoom.getSplitPane().getRightComponent();
                if (phonePanel.equals(rightComponent)) {
                    return sparkTab;
                }
            }
        }


        return null;
    }

    private void closeToaster(InterlocutorUI ic) {
        SparkToaster toaster = toasters.get(ic);
        if (toaster != null) {
            toaster.close();
            toasters.remove(ic);
        }
    }

}
