/**
 * 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 net.java.sipmack.softphone;

import net.java.sipmack.common.DialSoundManager;
import net.java.sipmack.common.Log;
import net.java.sipmack.events.UserActionListener;
import net.java.sipmack.media.*;
import net.java.sipmack.sip.*;
import net.java.sipmack.sip.event.*;
import net.java.sipmack.softphone.gui.DefaultGuiManager;
import net.java.sipmack.softphone.gui.GuiManager;
import net.java.sipmack.softphone.listeners.InterlocutorListener;
import net.java.sipmack.softphone.listeners.RegisterEvent;
import net.java.sipmack.softphone.listeners.SoftPhoneListener;
import org.jivesoftware.resource.Res;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.vcardtemp.packet.VCard;
import org.jivesoftware.spark.SparkManager;
import org.jivesoftware.spark.phone.PhoneManager;
import org.jivesoftware.spark.plugin.phone.resource.PhoneRes;
import org.jivesoftware.spark.preference.PreferenceManager;
import org.jivesoftware.spark.util.ModelUtil;
import org.jivesoftware.sparkimpl.settings.local.SettingsManager;
import org.jivesoftware.sparkplugin.calllog.LogManager;
import org.jivesoftware.sparkplugin.calllog.LogManagerImpl;
import org.jivesoftware.sparkplugin.calllog.LogPacket;
import org.jivesoftware.sparkplugin.preferences.SipPreference;
import org.jivesoftware.sparkplugin.preferences.SipPreferences;
import org.jivesoftware.sparkplugin.sipaccount.SipAccount;
import org.jivesoftware.sparkplugin.sipaccount.SipAccountPacket;
import org.jivesoftware.sparkplugin.ui.call.MissedCalls;
import org.jxmpp.util.XmppStringUtils;

import javax.sdp.MediaDescription;
import javax.sdp.SessionDescription;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Title: SIPark
 * Description:JAIN-SIP Audio/Video phone application
 *
 * @author Thiago Rocha Camargo (thiago@jivesoftware.com) The
 *         <code>SoftPhoneManager</code> class that Manage SIP Stack, Sessions
 *         and Calls
 *         Use getInstance() to get a SoftPhone instance to use
 * @version 1.0, 20/07/2006
 */

public class SoftPhoneManager implements CommunicationsListener, CallListener, UserActionListener, SoftPhone {

	
    private static SoftPhoneManager singleton;
    private static final Object LOCK = new Object();
    private SipAccount saccount;

    private final List<SoftPhoneListener> softPhoneListeners = new CopyOnWriteArrayList<SoftPhoneListener>();

    // TODO REMOVE
    @SuppressWarnings("unused")
    private SoftPhoneMedia softPhoneMedia = null;

    private SoftPhoneSecurity softPhoneSecurity = null;

    private SipManager sipManager = null;

    private JmfMediaManager mediaManager = null;

    private GuiManager guiManager = null;

    private String authUserName = "";

    private String username = "";

    private String password = null;// new char[10];

    public String server = "";

    protected String msgBuffer = "";

    protected Integer unregistrationLock = Integer.valueOf(0);

    private SipRegisterStatus status = SipRegisterStatus.Unregistered;

    public static final String userAgent = "SIPSpark";

    public String callTo = "";

    private LogManager logManager;

    private SipPreferences preferences;

    private SipPreference preference;

//    private int lines = 1;

    private DialSoundManager dtmfSounds;

    private MissedCalls missedCalls;

    // Define UIs
    private JCheckBoxMenuItem registerMenu;

    private Map<Component, CallRoomState> callRooms = new HashMap<Component, CallRoomState>();

    /**
     * Private constructor of the class.
     */
    private SoftPhoneManager() {
    	mediaManager = new JmfMediaManager();
    }

    /**
     * Initializes the core phone objects.
     */
    private void initializePhone() {
       // Load Preferences
       loadPreferences();

       if (preferences == null) {
           return;
       }


       guiManager = new GuiManager();
       guiManager.addUserActionListener(this);
       logManager = new LogManagerImpl(this);

       this.getLogManager().setRemoteLogging(true);

    	 try {
				EventQueue.invokeAndWait(new Runnable() {
		 				@Override
		 				public void run() {
		 					registerMenu = new JCheckBoxMenuItem(PhoneRes.getIString("phone.enabled"));
		 				}
		 			});
	    }
	    catch(Exception e){
	   	 Log.error(e);
	    }
        
        registerMenu.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent actionEvent) {
                if (getStatus() == SipRegisterStatus.Unregistered ||
                        getStatus() == SipRegisterStatus.RegistrationFailed) {

                    register();
                } else {
                    handleUnregisterRequest();
                }
            }
        });


        SIPConfig.setPreferredNetworkAddress(preferences.getPreferredAddress());
        NetworkAddressManager.start();

     	 try {
				EventQueue.invokeAndWait(new Runnable() {
		 				@Override
		 				public void run() {
		 					 // Initialize Missed calls
		 					 missedCalls = new MissedCalls();
		 				}
		 			});
	    }
	    catch(Exception e){
	   	 Log.error(e);
	    }
       

        final JMenu actionsMenu = SparkManager.getMainWindow().getMenuByName(Res.getString("menuitem.actions"));
        actionsMenu.add(registerMenu);
    }


    /**
     * Type of states a jingle call can be in.
     */
    public static enum CallRoomState {
        /**
         * The component is in a call.
         */
        inCall,

        /**
         * The components call has ended.
         */
        callWasEnded,

        /**
         * The call is muted.
         */
        muted,

        /**
         * The call is on hold.
         */
        onHold
    }

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

    /**
     * Add SoftPhoneListener
     *
     * @param softPhoneListener the listener.
     */
    public void addSoftPhoneListener(SoftPhoneListener softPhoneListener) {
        softPhoneListeners.add(softPhoneListener);
    }

    /**
     * Add InterlocutorListener
     *
     * @param interlocutorListener the listener.
     */
    public void addInterlocutorListener(InterlocutorListener interlocutorListener) {
        guiManager.addInterlocutorListener(interlocutorListener);
    }

    /**
     * Remove InterlocutorListener
     *
     * @param interlocutorListener the listener
     */
    public void removeInterlocutorListener(InterlocutorListener interlocutorListener) {
        guiManager.removeInterlocutorListener(interlocutorListener);
    }

    /**
     * Return the DefaultGuiManager
     */
    public DefaultGuiManager getDefaultGuiManager() {
        return guiManager;
    }

    /**
     * Create the softphone handlers and stack
     */
    public void createSoftPhone(String server) throws MediaException {

        this.server = server;
        SIPConfig.setServer(server);

        if (sipManager != null) {
            destroySoftPhone();
        }

        sipManager = new SipManager();
        softPhoneMedia = new SoftPhoneMedia();
        softPhoneSecurity = new SoftPhoneSecurity();

        sipManager.addCommunicationsListener(this);
        sipManager.setSecurityAuthority(softPhoneSecurity);

        try {
            // put in a seperate thread
            sipManager.start();
            if (sipManager.isStarted()) {
                Log.debug("createSoftPhone", "SIP STARTED");
            }
        }
        catch (CommunicationsException exc) {
            Log.error("createSoftPhone", exc);
        }

    }

    /**
     * Destroys the softphone handlers and stack
     */
    public void destroySoftPhone() {

        try {
            sipManager.stop();
        }
        catch (Exception exc) {
            Log.error("destroySoftPhone", exc);
        }
    }

    /**
     * Return the current SIP connection status
     *
     * @return status
     */
    public SipRegisterStatus getStatus() {
        return status;
    }

    /**
     * Gets the current username
     *
     * @return The current connection username
     */
    public String getUsername() {
        return username;
    }

    /**
     * Gets the current server
     *
     * @return The current connection server
     */
    public String getServer() {
        return server;
    }

    /**
     * Handle an UnregisterRequest
     */
    public void handleUnregisterRequest() {
        if (sipManager != null) {
            try {
                sipManager.endAllCalls();
                sipManager.unregister();
            }
            catch (Exception e) {
                Log.error("handleUnregisterRequest", e);
            }
        }
    }

    /**
     * Handle a Re-Register Request.
     * This method will only have effect if the user has successfully registered beforeat least once before.
     */
    public void handleReRegisterRequest() {
        if (this.password != null && !this.username.equals("")) {
            try {
                sipManager.startRegisterProcess(username, authUserName, password);
            }
            catch (CommunicationsException exc) {
                Log.error("handleRegisterRequest", exc);
            }
        }
    }

    /**
     * Handle an RegisterRequest
     *
     * @param username username
     * @param password password
     */
    public void handleRegisterRequest(String username, String password) {
        handleRegisterRequest(username, null, password);
    }

    /**
     * Handle an RegisterRequest
     *
     * @param username     username
     * @param authUserName Authorization username
     * @param password     password
     */
    public void handleRegisterRequest(String username, String authUserName, String password) {

        this.authUserName = authUserName;
        this.username = username;
        this.password = password;

        try {
            sipManager.startRegisterProcess(username, authUserName, password);
        }
        catch (CommunicationsException exc) {
            Log.error("handleRegisterRequest", exc);
        }
    }

    /**
     * Add a CommunicationsListener
     *
     * @param communicationsListener CommunicationsListener
     */
    public void addCommunicationsListener(CommunicationsListener communicationsListener) {
        sipManager.addCommunicationsListener(communicationsListener);
    }

    /**
     * Fired when a call ends
     *
     * @param evt CallEvent
     */
    public void callEnded(CallEvent evt) {
        AudioMediaSession audioMediaSession = evt.getSourceCall().getAudioMediaSession();
        if (audioMediaSession != null) {
            audioMediaSession.close();
        }
        VideoMediaSession videoMediaSession = evt.getSourceCall().getVideoMediaSession();
        if (videoMediaSession != null) {
            videoMediaSession.stopTrasmit();
            videoMediaSession.stopReceive();
        }
    }

    /**
     * Fired when a call is received
     *
     * @param evt CallEvent
     */
    public void callReceived(CallEvent evt) {
        try {
            Call call = evt.getSourceCall();
            Interlocutor interlocutor = new Interlocutor();
            interlocutor.setCall(call);
            guiManager.addInterlocutor(interlocutor);
            call.addStateChangeListener(this);
            // handleAnswerRequest(interlocutor);
        }
        catch (Exception e) {
            Log.error("callReceived", e);
        }
    }

    /**
     * Fired when a message is received
     *
     * @param evt MessageEvent
     */
    public void messageReceived(MessageEvent evt) {
        for (SoftPhoneListener sfl : softPhoneListeners) {
            sfl.messageReceived(evt);
        }
    }

    /**
     * Fired when a call is rejected locally
     *
     * @param evt CallRejectedEvent
     */
    public void callRejectedLocally(CallRejectedEvent evt) {
//        String reason = evt.getReason();
//        String detailedReason = evt.getDetailedReason();
    }

    /**
     * Fired when a call is rejected remotly
     *
     * @param evt CallRejectedEvent
     */
    public void callRejectedRemotely(CallRejectedEvent evt) {
        for (SoftPhoneListener softPhoneListener : softPhoneListeners) {
            softPhoneListener.callRejectedRemotely(evt);
        }
    }

    /**
     * Fired when softphone is register sucessfully
     *
     * @param evt RegistrationEvent
     */
    public void registered(RegistrationEvent evt) {
        status = SipRegisterStatus.Registered;

        preferences.setPreferredAddress(NetworkAddressManager.getLocalHost().getHostAddress());
        //preference.commit();

        registerStatusChanged(new RegisterEvent(this, SipRegisterStatus.Registered, evt.getReason()));

        if (callTo.length() > 2) {
            handleDialRequest(callTo);
        }
        registerMenu.setSelected(true);
    }

    public void registrationFailed(RegistrationEvent evt) {
        status = SipRegisterStatus.RegistrationFailed;
        registerStatusChanged(new RegisterEvent(this, SipRegisterStatus.RegistrationFailed, evt.getReason()));
        registerMenu.setSelected(false);
    }

    /**
     * Fired when softphone is registering
     *
     * @param evt RegistrationEvent
     */
    public void registering(RegistrationEvent evt) {
        status = SipRegisterStatus.Registering;
        registerStatusChanged(new RegisterEvent(this, SipRegisterStatus.Registering, evt.getReason()));
    }

    /**
     * Fired when softphone is unregistered sucessfully
     *
     * @param evt RegistrationEvent
     */
    public void unregistered(RegistrationEvent evt) {
        try {
            status = SipRegisterStatus.Unregistered;

            registerStatusChanged(new RegisterEvent(this, SipRegisterStatus.Unregistered, evt.getReason()));

            // we could now exit
            synchronized (unregistrationLock) {
                unregistrationLock.notifyAll();
            }

            destroySoftPhone();
            registerMenu.setSelected(false);
        }
        catch (Exception e) {
            Log.error("unregistered", e);
        }
    }

    /**
     * Fired when softphone is unregistering
     *
     * @param evt RegistrationEvent
     */
    public void unregistering(RegistrationEvent evt) {
        status = SipRegisterStatus.Unregistered;

        registerStatusChanged(new RegisterEvent(this, SipRegisterStatus.Unregistering, evt.getReason()));

        int waitUnreg = SIPConfig.getWaitUnregistration();
        if (waitUnreg != -1) {
            try {
                int delay = waitUnreg;
                // we get here through a _synchronous_ call from shutdown so
                // let's try
                // and wait for unregistrations confirmation in case the
                // registrar has requested authorization
                // before conriming unregistration
                if (delay > 0)
                    synchronized (unregistrationLock) {
                        unregistrationLock.wait(delay);
                    }
            }
            catch (InterruptedException ex) {
                Log.error("unregistering", ex);
            }
            catch (NumberFormatException ex) {
                Log.error("unregistering", ex);
            }

        }
    }

    private void registerStatusChanged(RegisterEvent evt) {
        for (SoftPhoneListener sfl : softPhoneListeners) {
            sfl.registerStatusChanged(evt);
        }
    }

    /**
     * Returns the current interlocutors
     *
     * @return List<InterlocutorUI>
     */
    public List<InterlocutorUI> getInterlocutors() {
        return guiManager.getInterlocutors();
    }

    /**
     * Fired when softphone receive a unknown sip message
     *
     * @param evt RegistrationEvent
     */
    public void receivedUnknownMessage(UnknownMessageEvent evt) {
        // TODO Do something with the error.
    }

    /**
     * Fired when communications Errors Occurred
     *
     * @param evt CommunicationsErrorEvent
     */
    public void communicationsErrorOccurred(CommunicationsErrorEvent evt) {

    }

    /**
     * Fired when call state changes
     *
     * @param evt CallStateEvent
     */
    public void callStateChanged(CallStateEvent evt) {
        try {

            for (SoftPhoneListener sfl : softPhoneListeners) {
                sfl.callStateChanged(evt);
            }

            Call call = evt.getSourceCall();
            Log.debug("callStateChanged", evt.getOldState() + " -> "
                    + evt.getNewState());
            if (evt.getNewState() == Call.CONNECTED) {
                //sipManager.setBusy(true);

                if (call.getAudioReceiverChannel() != null)
                    call.getAudioReceiverChannel().stop();

                if (evt.getOldState() == Call.MOVING_REMOTELY) {
                    AudioMediaSession audioMediaSession = evt.getSourceCall().getAudioMediaSession();
                    if (call.getAudioReceiverChannel() != null)
                        call.getAudioReceiverChannel().stop();

                    if (audioMediaSession != null) {
                        audioMediaSession.stopTrasmit();
                        audioMediaSession.stopReceive();
                    }
                    VideoMediaSession videoMediaSession = evt.getSourceCall().getVideoMediaSession();
                    if (videoMediaSession != null) {
                        videoMediaSession.stopTrasmit();
                        videoMediaSession.stopReceive();
                    }
                    PhoneManager.setUsingMediaLocator(false);
                }

                int localAudioPort = -1;
                int localVideoPort = -1;
                
                Vector<MediaDescription> mediaDescriptions = call.getLocalSdpDescription().getMediaDescriptions(true);
                for (MediaDescription mediaDescription : mediaDescriptions)
                {
                	if (mediaDescription.getMedia().getMediaType().equals("audio"))
                		localAudioPort = mediaDescription.getMedia().getMediaPort();
                	else if (mediaDescription.getMedia().getMediaType().equals("video"))
                		localVideoPort = mediaDescription.getMedia().getMediaPort();

                }
                
                
                AudioMediaSession audioMediaSession = mediaManager.createAudioMediaSession(call.getRemoteSdpDescription().toString(), localAudioPort);                
                call.setAudioMediaSession(audioMediaSession);

                if (audioMediaSession != null) {
                    audioMediaSession.startTrasmit();
                    audioMediaSession.startReceive();
                }
                
                // If remote client have video
                if (localVideoPort > 0)
                {
                	if (SettingsManager.getLocalPreferences().getVideoDevice() != null && !"".equals(SettingsManager.getLocalPreferences().getVideoDevice()))
                	{
		                VideoMediaSession videoMediaSession = mediaManager.createVideoMediaSession(call.getRemoteSdpDescription().toString(), localVideoPort);
		                if (videoMediaSession != null) {
		                	videoMediaSession.startTrasmit();
		                	videoMediaSession.startReceive();
                            call.setVideoMediaSession(videoMediaSession);
		                }
                	}
                }

                
                evt.getSourceCall().start();
                
                Log.debug("MEDIA STREAMS OPENED");

            } else if (evt.getNewState() == Call.RINGING) {

                if (call.getRemoteSdpDescription() != null
                        && !call.getRemoteSdpDescription().equals("")) {

                    Log.debug("STATE", call.getRemoteSdpDescription().toString());

                    int localPort = ((MediaDescription) (call.getLocalSdpDescription().getMediaDescriptions(true).get(0))).getMedia().getMediaPort();
                    int destPort = ((MediaDescription) (call.getRemoteSdpDescription().getMediaDescriptions(true).get(0))).getMedia().getMediaPort();
                    String destIp = call.getRemoteSdpDescription().getConnection().getAddress();

                    AudioReceiverChannel audioReceiverChannel = mediaManager.createAudioReceiverChannel(localPort, destIp, destPort, (destPort + 1));
                    call.setAudioReceiverChannel(audioReceiverChannel);

                    if (audioReceiverChannel != null)
                        audioReceiverChannel.start();

                }

            } else if (evt.getNewState() == Call.DISCONNECTED) {
                sipManager.setBusy(false);

                AudioMediaSession audioMediaSession = evt.getSourceCall().getAudioMediaSession();
                if (audioMediaSession != null) {
                    audioMediaSession.stopTrasmit();
                    audioMediaSession.stopReceive();
                }
                if (call.getAudioReceiverChannel() != null)
                    call.getAudioReceiverChannel().stop();
                VideoMediaSession videoMediaSession = evt.getSourceCall().getVideoMediaSession();
                if (videoMediaSession != null) {
                    videoMediaSession.stopTrasmit();
                    videoMediaSession.stopReceive();
                }

                PhoneManager.setUsingMediaLocator(false);

            } else if (evt.getNewState() == Call.FAILED) {
                call.setState(Call.DISCONNECTED);
                if (call.getAudioReceiverChannel() != null)
                    call.getAudioReceiverChannel().stop();

                CallRejectedEvent rejectEvt = new CallRejectedEvent("Disconnected", call.getLastRequest(), call);

                for (SoftPhoneListener softPhoneListener : softPhoneListeners) {
                    softPhoneListener.callRejectedRemotely(rejectEvt);
                }

                PhoneManager.setUsingMediaLocator(false);

            }
        }
        catch (Exception e) {
            Log.error("callStateChanged", e);
        }

    }

    /**
     * Handle a exit request
     */
    public void handleExitRequest() {
        if (mediaManager != null) {
        }
        // SIP unregister
        if (sipManager != null) {
            try {
                sipManager.endAllCalls();
            }
            catch (CommunicationsException exc) {
                Log.error("handleExitRequest", exc);
            }
            catch (Throwable exc) {
                Log.error("handleExitRequest", exc);
            }
            try {
                sipManager.unregister();
            }
            catch (CommunicationsException exc) {
                Log.error("handleExitRequest", exc);
            }
            catch (Throwable exc) {
                Log.error("handleExitRequest", exc);
            }
            try {
                sipManager.stop();
            }
            catch (Exception exc) {
                Log.error("handleExitRequest", exc);
            }
        }
        NetworkAddressManager.shutDown();
    }

    /**
     * Handle a Hold request
     *
     * @param iui the InterlocutorUI
     * @param mic true to place on hold.
     * @param cam true to place camera on hold.
     */
    public void handleHold(InterlocutorUI iui, boolean mic, boolean cam) {

        try {
            sipManager.hold(iui.getID(), mediaManager.generateHoldSdpDescription(mic, mic, iui.getCall()), mic, cam);
        }
        catch (Exception e) {
            Log.error("handleHold", e);
        }

    }

    public void handleTransfer(int callID, String callee) {

        sipManager.transfer(callID, callee);

    }

    /**
     * Handle a Mute request
     *
     * @param iui the InterlocutorUI
     * @param mic true to place on mute.
     */
    public void handleMute(InterlocutorUI iui, boolean mic) {
        try {
            AudioMediaSession audioMediaSession = iui.getCall().getAudioMediaSession();
            if (audioMediaSession != null) audioMediaSession.setTrasmit(mic);
        }
        catch (Exception e) {
            Log.error("handleHold", e);
        }
    }

    /**
     * Handle when users press a dtmf button
     *
     * @param iui the InterlocutorUI
     */
    public void handleDTMF(InterlocutorUI iui, String digit) {
        try {
            sendDTMFDigit(iui.getID(), digit);
        }
        catch (Exception e) {
            Log.error("sendDTMFDigit", e);
        }
    }

    /**
     * Send the dtmf digit to the sip server.
     *
     * @param callID the caller id
     * @param digit  the digit typed.
     */
    void sendDTMFDigit(int callID, String digit) {
        try {
            sipManager.sendDTMF(callID, digit);
        }
        catch (CommunicationsException exc) {
            Log.error("sendDTMFDigit", exc);
        }
    }

    /**
     * Handle a answer request
     */
    public boolean handleAnswerRequest(Interlocutor interlocutor) {

        // cancel call request if no Media Locator
        if (PhoneManager.isUseStaticLocator() && PhoneManager.isUsingMediaLocator()) {
            return false;
        }

        PhoneManager.setUsingMediaLocator(true);
        
        SessionDescription sdpData = null;
        try {
            sdpData = mediaManager.generateSdpDescription();
            interlocutor.getCall().setLocalSdpDescription(sdpData);
        }
        catch (MediaException ex) {
            try {
                sipManager.sendServerInternalError(interlocutor.getID());
            }
            catch (CommunicationsException ex1) {
                Log.error("handleAnswerRequest", ex1);
            }
            return false;
        }
        try {
            sipManager.answerCall(interlocutor.getID(), sdpData.toString());
        }
        catch (CommunicationsException exc) {
            Log.error("handleAnswerRequest", exc);
            return false;
        }
        return true;
    }

    /**
     * Handle a dial request
     */
    public void handleDialRequest(String phoneNumber) {
        try {

            System.err.println("Audio Static:" + PhoneManager.isUseStaticLocator() + " Using:" + PhoneManager.isUsingMediaLocator());

            // cancel call request if no Media Locator
            if (PhoneManager.isUseStaticLocator() && PhoneManager.isUsingMediaLocator()) {
                return;
            }

            PhoneManager.setUsingMediaLocator(true);

            SessionDescription sdpData = mediaManager.generateSdpDescription();

            Call call = sipManager.establishCall(phoneNumber, sdpData.toString());

            if (call == null) return;

            call.setLocalSdpDescription(sdpData);

            call.addStateChangeListener(this);
            Interlocutor interlocutor = new Interlocutor();
            interlocutor.setCall(call);

            guiManager.addInterlocutor(interlocutor);
        }
        catch (Exception e) {
            Log.error("handleDialRequest", e);
        }
    }

    /**
     * Handle a hangup request
     */
    public boolean handleHangupRequest(Interlocutor interlocutor) {
        boolean hangupOk = true;
        try {
            sipManager.endCall(interlocutor.getID());
        }
        catch (CommunicationsException exc) {
            guiManager.remove(interlocutor);
            hangupOk = false;
            Log.error("handleHangupRequest", exc);
        }
        return hangupOk;
    }

    /**
     * Register the softPhone with the Spark preferrence settings
     */
    public void register() {
        Thread registerThread = new Thread(new Runnable() {
            public void run() {
                if (preferences != null) {
                    String user = preferences.getUserName();
                    String authUser = preferences.getAuthUserName();
                    String server = preferences.getServer();
                    String pass = preferences.getPassword();

                    try {
                        createSoftPhone(server);
                    }
                    catch (MediaException e) {
                        Log.error("Media Exception", e);

                    }

                    handleRegisterRequest(user, authUser, pass);
                }
            }
        });

        registerThread.start();
    }

    /**
     * Loads Preferences, either from server or locally.
     */
    private void loadPreferences() {
        boolean serverPluginInstalled = SipAccountPacket.isSoftPhonePluginInstalled(SparkManager.getConnection());
        if (serverPluginInstalled) {
            setupRemotePreferences(SparkManager.getConnection());
        }
    }

    /**
     * Returns the LogManager associated with this SoftPhone.
     *
     * @return the LogManager.
     */
    public LogManager getLogManager() {
        return logManager;
    }
    
    /**
     * Set the LogManager associated with this SoftPhone.
     */
    public void setLogManager(LogManager logmanager) {
   	  // removes the existing listener 
   	  softPhoneListeners.remove(this.logManager);
        logManager = logmanager;
    }

    /**
     * Setup the Preferences according to remote server
     *
     * @param con XMPPConnection connection
     */
    private void setupRemotePreferences(XMPPConnection con) {

        try {
            ProviderManager.addIQProvider(SipAccountPacket.ELEMENT, SipAccountPacket.NAMESPACE, new SipAccountPacket.Provider());
            ProviderManager.addIQProvider(LogPacket.ELEMENT, LogPacket.NAMESPACE, new LogPacket.Provider());

            SipAccountPacket sp = SipAccountPacket.getSipSettings(con);
            if (sp != null) {
                SipAccount sipAccount = sp.getSipAccount();
                if (sipAccount != null) {
                    this.saccount = sipAccount;
                    preference = new SipPreference();
                    preference.setShowGUI(false);
                    preference.setCommitSettings(false);

                    preferences = new SipPreferences();

                    preferences.setUserName(sipAccount.getSipUsername());
                    preferences.setAuthUserName(sipAccount.getAuthUsername());
                    preferences.setServer(sipAccount.getServer());
                    preferences.setPassword(sipAccount.getPassword());
                    preferences.setRegisterAtStart(true);

                    preferences.setStunServer(sipAccount.getStunServer());
                    preferences.setStunPort(sipAccount.getStunPort());
                    preferences.setUseStun(sipAccount.isUseStun());
                    preferences.setVoicemail(sipAccount.getVoiceMailNumber());
                    preferences.setOutboundproxy(sipAccount.getOutboundproxy());
                    preferences.setPromptCredentials(sipAccount.isPromptCredentials());

                    SIPConfig.setUseStun(preferences.isUseStun());
                    SIPConfig.setStunServer(preferences.getStunServer());
                    SIPConfig.setStunPort(preferences.getStunPort());
                    SIPConfig.setOutboundProxy(preferences.getOutboundproxy());

                    preference.setData(preferences);
                    SIPConfig.setPreferredNetworkAddress(NetworkAddressManager.getLocalHost(false).getHostAddress());

                    if (preferences.isRegisterAtStart()) {
                        register();
                    }

                    if (preferences.isPromptCredentials()) {
                        loadLocalPreferences();
                    }

                } else {

                }
            }

        }
        catch (Exception e) {
            Log.error("setupRemotePreferences", e);
        }

    }

    private void loadLocalPreferences() {
        preference = new SipPreference();

        PreferenceManager pm = SparkManager.getPreferenceManager();
        pm.addPreference(preference);

        preferences = (SipPreferences) preference.getData();

        SIPConfig.setUseStun(preferences.isUseStun());
        SIPConfig.setStunServer(preferences.getStunServer());
        SIPConfig.setStunPort(preferences.getStunPort());
        SIPConfig.setPreferredNetworkAddress(preferences.getPreferredAddress());

        preference.setCommitSettings(true);

        if (preferences.isRegisterAtStart()) {
            register();
        }
    }


    /**
     * Calls an individual user by their VCard information.
     *
     * @param jid the users JID.
     */
    public void callByJID(String jid) {
        if (getStatus() == SipRegisterStatus.Registered) {
            final VCard vcard = SparkManager.getVCardManager().getVCard(XmppStringUtils.parseBareJid(jid));

            if (vcard != null) {
                String number = vcard.getPhoneWork("VOICE");
                if (!ModelUtil.hasLength(number)) {
                    number = vcard.getPhoneHome("VOICE");
                }

                if (ModelUtil.hasLength(number)) {
                    getDefaultGuiManager().dial(number);
                }
            }
        }
    }

    /**
     * Returns the SoundManager for Dial Tones.
     *
     * @return the SoundManager.
     */
    public DialSoundManager getDTMFSounds() {
        if (dtmfSounds == null) {
            dtmfSounds = new DialSoundManager();
        }
        return dtmfSounds;
    }


    public SipPreference getPreference() {
        return preference;
    }

    public MissedCalls getMissedCalls() {
        return missedCalls;
    }

    public static String getNumbersFromPhone(String number) {
        if (number == null) {
            return null;
        }

        number = number.replace("-", "");
        number = number.replace("(", "");
        number = number.replace(")", "");
        number = number.replace(" ", "");
        if (number.startsWith("1")) {
            number = number.substring(1);
        }

        return number;
    }

    public SipAccount getSipAccount() {
        return saccount;
    }

    /**
     * Adds a new CallRoomState.
     *
     * @param component the component where the call is taking place.
     * @param state     the state of the call.
     */
    public void addCallSession(Component component, CallRoomState state) {
        callRooms.put(component, state);
    }

    /**
     * Removes a CallRoomState.
     *
     * @param component the component where the call is taking place.
     */
    public void removeCallSession(Component component) {
        callRooms.remove(component);
    }

    /**
     * Returns the state of a <code>Component</code>. If no call is taking place,
     * this method will return null.
     *
     * @param component the <code>Component</code>.
     * @return the CallRoomState.
     */
    public CallRoomState getCallRoomState(Component component) {
        return callRooms.get(component);
    }

    /**
     * Returns true if the user is connected.
     *
     * @return true if the user is connected.
     */
    public boolean isPhoneEnabled() {
        return getPreference() != null;
    }
    
    public JmfMediaManager getJmfMediaManager() {
   	 return mediaManager;
    }
}
