/**
 * $Revision$
 * $Date$
 *
 * Copyright (C) 2006 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

package org.jivesoftware.wildfire.gateway.protocols.simple;

import java.net.InetAddress;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;
import java.util.TooManyListenersException;

import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ListeningPoint;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipFactory;
import javax.sip.SipProvider;
import javax.sip.SipStack;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionDoesNotExistException;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.wildfire.XMPPServer;
import org.jivesoftware.wildfire.gateway.*;
import org.jivesoftware.wildfire.roster.*;
import org.jivesoftware.wildfire.user.User;
import org.xmpp.packet.JID;
import org.xmpp.packet.Presence;

/**
 * A gateway session to a SIMPLE IM server.
 * @author  Patrick Siu
 * @version 0.0.1
 */
public class SimpleSession extends TransportSession {
	
	private SipFactory sipFactory = null;
	
	private String sipHost;
	private int    sipPort;
	private String username;
	private String sessionId = null;
	private long   seqNum;
	
	private ListeningPoint tcp = null;
	private ListeningPoint udp = null;
	private SipProvider tcpSipProvider;
	private SipProvider udpSipProvider;
	
	private MessageFactory        messageFactory;
	private AddressFactory        addressFactory;
	private HeaderFactory         headerFactory;
	private SipStack              sipStack;
	private SimpleSessionListener myListener;
	private SimpleRoster          myRoster;
	
	
	/**
	 * Constructor utilizing the lesser constructor of the super class.
	 * @see org.jivesoftware.wildfire.gateway.TransportSession#TransportSession(org.jivesoftware.wildfire.gateway.Registration,org.xmpp.packet.JID,org.jivesoftware.wildfire.gateway.BaseTransport)
	 */
	public SimpleSession(Registration registration, JID jid, BaseTransport transport) {
		super(registration, jid, transport);
		
		init();
	}
	
	/**
	 * Constructor utilizing the greater constructor of the super class.
	 * @see org.jivesoftware.wildfire.gateway.TransportSession#TransportSession(org.jivesoftware.wildfire.gateway.Registration,org.xmpp.packet.JID,org.jivesoftware.wildfire.gateway.BaseTransport,java.lang.Integer)
	 */
	public SimpleSession(Registration registration, JID jid, BaseTransport transport, Integer priority) {
		super(registration, jid, transport, priority);
		
		init();
	}
	
	/** This process initializes the session by building (or retrieving) a SIP Stack and adding listeners to it. */
	private void init() {
		// Initialize local variable first!
		myRoster = new SimpleRoster(this);
		seqNum   = 1L;
		
		sipHost = JiveGlobals.getProperty("plugin.gateway.sip.connecthost", "");
		sipPort = ((SimpleTransport) transport).generateListenerPort();
		
		// Initialize the SipFactory
		sipFactory = SipFactory.getInstance();
		if (sipFactory.getPathName() == null || !sipFactory.getPathName().equals("gov.nist"))
			sipFactory.setPathName("gov.nist");
		
		// Initialize the SipStack for this session
		Properties properties = new Properties();
		properties.setProperty("javax.sip.STACK_NAME", jid.getNode());
		properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
		
		try {
			String localIP = InetAddress.getLocalHost().getHostAddress();
			
			sipStack       = sipFactory.createSipStack(properties);
			headerFactory  = sipFactory.createHeaderFactory();
			addressFactory = sipFactory.createAddressFactory();
			messageFactory = sipFactory.createMessageFactory();
			
			Iterator listeningPointIterator = sipStack.getListeningPoints();
			while (listeningPointIterator.hasNext()) {
				ListeningPoint listeningPoint = (ListeningPoint) listeningPointIterator.next();
				
				if (listeningPoint.getIPAddress() != null &&
				    listeningPoint.getIPAddress().equals(localIP) &&
//				    listeningPoint.getPort() == sipPort &&
				    listeningPoint.getTransport().equals(ListeningPoint.TCP)) {
					tcp = listeningPoint;
					sipPort = tcp.getPort();
				}
				
				if (listeningPoint.getIPAddress() != null &&
				    listeningPoint.getIPAddress().equals(localIP) &&
//				    listeningPoint.getPort() == sipPort &&
				    listeningPoint.getTransport().equals(ListeningPoint.UDP)) {
					udp     = listeningPoint;
					sipPort = udp.getPort();
				}
			}
			
			if (tcp == null) {
				tcp = sipStack.createListeningPoint(localIP, sipPort, ListeningPoint.TCP);
				sipPort = tcp.getPort();
			}
			if (udp == null) {
				udp = sipStack.createListeningPoint(localIP, sipPort, ListeningPoint.UDP);
				sipPort = udp.getPort();
			}
			
			Iterator sipProviderIterator = sipStack.getSipProviders();
			while (sipProviderIterator.hasNext()) {
				SipProvider sipProvider = (SipProvider) sipProviderIterator.next();
				
				if (sipProvider.getListeningPoint(ListeningPoint.TCP) != null) {
					tcpSipProvider = sipProvider;
				}
				if (sipProvider.getListeningPoint(ListeningPoint.UDP) != null) {
					udpSipProvider = sipProvider;
				}
			}
			if (tcpSipProvider == null)
				tcpSipProvider = sipStack.createSipProvider(tcp);
			if (udpSipProvider == null)
				udpSipProvider = sipStack.createSipProvider(udp);
		} catch (Exception ex) {
			Log.debug(ex);
		}
		
		try {
			myListener = new SimpleSessionListener(this);
			tcpSipProvider.addSipListener(myListener);
			udpSipProvider.addSipListener(myListener);
		} catch (TooManyListenersException ex) {
			Log.debug(ex);
		}
		
		try {
			sipStack.start();
		} catch (SipException ex) {
			Log.debug(ex);
		}
	}
	
	/**
	 * Perform rollback action once the login fails or logout goes on the way.
	 */
	private void rollback() {
//		if (myListener != null)
		
	}
	
	public void updateStatus(PresenceType presenceType, String verboseStatus) {
		Log.debug("SimpleSession(" + getJID().getNode() + ").updateStatus:  Method commenced!");
		
		SimplePresence simplePresence = ((SimpleTransport) getTransport()).convertJabStatusToSIP(presenceType);
	}
	
	public void addContact(RosterItem item) {
		String nickname = getTransport().convertJIDToID(item.getJid());
        if (item.getNickname() != null && !item.getNickname().equals("")) {
            nickname = item.getNickname();
        }
        
		lockRoster(item.getJid().toString());
		Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Roster of " + item.getJid().toString() + " locked!");
		
		JID    destJid    = item.getJid();
		String destId     = ((SimpleTransport) transport).convertJIDToID(destJid);
		
		Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Starting addContact function for " + destId);
		Request subscribeRequest = null;
		try {
			subscribeRequest = prepareSubscribeRequest(destId);
			subscribeRequest.addHeader(headerFactory.createExpiresHeader(365 * 24 * 60 * 60));
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Unable to prepare SUBSCRIBE request.", e);
			
			unlockRoster(item.getJid().toString());
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Roster of " + item.getJid().toString() + " unlocked!");
			return;
		}
		
		try {
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.ACK));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.BYE));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.CANCEL));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.INFO));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.INVITE));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.MESSAGE));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.NOTIFY));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.OPTIONS));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.REFER));
			subscribeRequest.addHeader(headerFactory.createAllowHeader(Request.SUBSCRIBE));
			subscribeRequest.addHeader(headerFactory.createEventHeader("presence"));
			subscribeRequest.addHeader(headerFactory.createAcceptHeader("application", "pidf+xml"));
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Unable to add a header", e);
		}
		
		try {
			sendRequest(subscribeRequest, ListeningPoint.UDP);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Unable to send request.", e);
			
			unlockRoster(item.getJid().toString());
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Roster of " + item.getJid().toString() + " unlocked!");
			return;
		}
		
		destJid = getTransport().convertIDToJID(destId);
		
		try {
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Adding contact '" + destJid.toString() + "' to roster...");
            getTransport().addOrUpdateRosterItem(getJID(), destJid, nickname, item.getGroups());
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Contact '" + destJid.toString() + "' added!");
        }
		catch (Exception ex) {
			Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Unable to add contact.", ex);
		}
		
		unlockRoster(item.getJid().toString());
		Log.debug("SimpleSession(" + jid.getNode() + ").addContact:  Roster of " + item.getJid().toString() + " unlocked!");
	}
	
	public void removeContact(RosterItem item) {
		String nickname = getTransport().convertJIDToID(item.getJid());
        if (item.getNickname() != null && !item.getNickname().equals("")) {
            nickname = item.getNickname();
        }
        lockRoster(item.getJid().toString());
		Log.debug("SimpleSession(" + jid.getNode() + ").removeContact:  Roster of " + item.getJid().toString() + " locked!");
		
		JID    destJid    = item.getJid();
		String destId     = ((SimpleTransport) transport).convertJIDToID(destJid);
		
		Log.debug("SimpleSession(" +jid.getNode() + ").removeContact:  Starting addContact function for " + destId);
		
		List<Header> customHeaders = new ArrayList<Header>(13);
		try { customHeaders.add(headerFactory.createExpiresHeader(0)); }
		catch (Exception e) {}	// Ignore
		try { customHeaders.add(headerFactory.createEventHeader("presence")); }
		catch (Exception e) {}	// Ignore
		
//		prepareRequest(RequestType.SUBSCRIBE, destId, null, null, 1L, 70, customHeaders, null);
		
		destJid = getTransport().convertIDToJID(destId);
		
		try {
			Log.debug("SimpleSession(" + jid.getNode() + ").removeContact:  Removing contact '" + destJid.toString() + "' from roster...");
            getTransport().removeFromRoster(getJID(), destJid);
			Log.debug("SimpleSession(" + jid.getNode() + ").removeContact:  Contact '" + destJid.toString() + "' removed!");
        }
		catch (Exception ex) {
			Log.debug("SimpleSession(" + jid.getNode() + ").removeContact:  Unable to add contact.", ex);
		}
		
		unlockRoster(item.getJid().toString());
		Log.debug("SimpleSession(" + jid.getNode() + ").removeContact:  Roster of " + item.getJid().toString() + " unlocked!");
	}
	
	public void updateContact(RosterItem item) {
		Log.debug("SimpleSession(" + jid.getNode() + ").updateContact:  I was called!");
	}
	
	public void sendMessage(JID jid, String message) {
		Log.debug("SimpleSession(" + jid.getNode() + "):  Starting message sending process.");
		ContentTypeHeader contentTypeHeader = null;
		
		try {
			contentTypeHeader = headerFactory.createContentTypeHeader("text", "plain");
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").sendMessage:  Unable to initiate ContentType header.", e);
			return;
		}
		Log.debug("SimpleSession(" + jid.getNode() + "):  Finished adding ContentType header.");
		
		MessageContent    content           = new MessageContent(contentTypeHeader, message);
		
		try {
			Request request = prepareMessageRequest(content, ((SimpleTransport) transport).convertJIDToID(jid));
			sendRequest(request, ListeningPoint.UDP);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").sendMessage:  Unable to send message.", e);
		}

//		if (!prepareRequest(RequestType.MESSAGE, ((SimpleTransport) transport).convertJIDToID(jid), null, null, 1L, 70, null, content)) {
//			Log.debug("SimpleSession(" + this.jid.getNode() + ").sendMessage:  Unable to send message!");
//		}
	}
	
	public void sendServerMessage(String message) {
		Log.debug("SimpleSession(" + jid.getNode() + ").sendServerMessage:  I was called!");
	}
	
	public void sendChatState(JID jid, ChatStateType chatState) {
		Log.debug("SimpleSession(" + jid.getNode() + ").sendChatState:  I was called!");
	}
	
	public void retrieveContactStatus(JID jid) {
		Log.debug("SimpleSession(" + this.jid.getNode() + ").retrieveContactStatus:  I was called!");
	}
	
	public void resendContactStatuses(JID jid) {
		Log.debug("SimpleSession(" + this.jid.getNode() + ").resendContactStatuses:  I was called!");
	}
	
	
	// The following are SimpleSession specific methods
	public void login(PresenceType presenceType, String verboseStatus) {
		if (!this.isLoggedIn()) {
			this.setLoginStatus(TransportLoginStatus.LOGGING_IN);
			
			Log.debug("SimpleSession(" + jid.getNode() + ").login:  Start login as " + registration.getUsername() + ".");
			
			Request registerRequest = prepareRegisterRequest();
			
			if (registerRequest.getHeader(CallIdHeader.NAME) == null) {
				Log.debug("SimpleSession(" + getJID().getNode() + ").login:  Unable to create a SIP session ID!!");
				this.setLoginStatus(TransportLoginStatus.LOGGED_OUT);
				return;
			}
			else {
				sessionId = ((CallIdHeader) registerRequest.getHeader(CallIdHeader.NAME)).getCallId();
			}
			
			try {
				registerRequest.addHeader(headerFactory.createExpiresHeader(365 * 24 * 60 * 60));
			}
			catch (Exception e) {
				Log.debug("SimpleSession(" + jid.getNode() + ").login:  " +
				          "Unable to set the expiry interval, which is essential for a login.", e);
				this.setLoginStatus(TransportLoginStatus.LOGGED_OUT);
				return;
			}
			
			try {
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.ACK));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.BYE));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.CANCEL));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.INFO));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.INVITE));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.MESSAGE));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.NOTIFY));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.OPTIONS));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.REFER));
				registerRequest.addHeader(headerFactory.createAllowHeader(Request.SUBSCRIBE));
			}
			catch (Exception e) {} // Ignore
			
			try {
				sendRequest(registerRequest, ListeningPoint.UDP);
			}
			catch (Exception e) {
				Log.debug("SimpleSession(" + jid.getNode() + ").login:  Unable to send login packet.", e);
				this.setLoginStatus(TransportLoginStatus.LOGGED_OUT);
			}
		}
	}
	
	public void logout() {
		this.setLoginStatus(TransportLoginStatus.LOGGING_OUT);
		
		myRoster.deactivate();
		
		// Lots of logout work here...
		Log.debug("SimpleSession(" + getJID().getNode() + ").logout:  Preparing logout packet...");
		
		Request registerRequest = prepareRegisterRequest();
		
		try {
			registerRequest.addHeader(headerFactory.createExpiresHeader(0));
			sendRequest(registerRequest, ListeningPoint.UDP);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").login:  Unable to logout.", e);
			this.setLoginStatus(TransportLoginStatus.LOGGED_IN);
			return;
		}
		
//		List<Header> customHeaders = new ArrayList<Header>(1);
//		try { customHeaders.add(headerFactory.createExpiresHeader(0)); }
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + getJID().getNode() + ").logout:  " +
//			          "Unable to set the expiry interval, which is essential for a logout.", e);
//			return;
//		}
		
//		String myUsername = registration.getUsername();
//		if (myUsername.indexOf("sip:") < 0) myUsername = "sip:" + myUsername;
//		if (myUsername.indexOf("@")    < 0) myUsername = myUsername + "@" + sipHost;
//		
//		prepareRequest(RequestType.REGISTER, myUsername, null, this.sessionId, seqNum++, 70, customHeaders, null);
		this.setLoginStatus(TransportLoginStatus.LOGGED_OUT);
	}
	
	public void sipUserLoggedIn() {
		myRoster.activate();
		
		getRegistration().setLastLogin(new Date());
		setLoginStatus(TransportLoginStatus.LOGGED_IN);
	}
	
	public void sipUserLoggedOut() {
		// If anybody subscribed me, send NOTIFY or subscription termination
		
		myRoster.deactivate();
		
		setLoginStatus(TransportLoginStatus.LOGGED_OUT);
	}
	
	
	public void removeStack() {
		Log.debug("SimpleSession for " + jid.getNode() + " is going to shut down!");
		
		tcpSipProvider.removeSipListener(myListener);
		udpSipProvider.removeSipListener(myListener);
		
		myListener = null;
		
		Log.debug("SimpleSession for " + jid.getNode() + " has their listeners removed.");
		
		try {
			sipStack.deleteSipProvider(tcpSipProvider);
			sipStack.deleteSipProvider(udpSipProvider);
			Log.debug("SimpleSession(" + jid.getNode() + ").shutdown:  SIP Providers deleted.");
			
			sipStack.deleteListeningPoint(tcp);
			sipStack.deleteListeningPoint(udp);
			Log.debug("SimpleSession(" + jid.getNode() + ").shutdown:  Listening points deleted.");
		}
		catch (Exception ex) {
			Log.debug("SimpleSession for " + jid.getNode() + " is unable to gracefully shut down.", ex);
		}
		
		sipStack.stop();
		sipStack = null;
	}
	
	public void finalize() {
		Log.debug("SimpleSession for " + jid.getNode() + ":  Finalize function initialized!");

		if (this.getLoginStatus().equals(TransportLoginStatus.LOGGED_IN)) {
			logout();
		}
	}
	
	
	///// Simple specific functions
	
	/**
	 * An inner enum encapsulating SIP Request types.
	 */
	private enum RequestType {
		ACK      (Request.ACK),
		BYE      (Request.BYE),
		CANCEL   (Request.CANCEL),
		INFO     (Request.INFO),
		INVITE   (Request.INVITE),
		MESSAGE  (Request.MESSAGE),
		NOTIFY   (Request.NOTIFY),
		OPTIONS  (Request.OPTIONS),
		PRACK    (Request.PRACK),
		PUBLISH  (Request.PUBLISH),
		REFER    (Request.REFER),
		REGISTER (Request.REGISTER),
		SUBSCRIBE(Request.SUBSCRIBE),
		UPDATE   (Request.UPDATE);
		
		private String sipReqType;
		
		RequestType(String sipReqType) {
			this.sipReqType = sipReqType;
		}
		
		public String toString() {
			return sipReqType;
		}
	}
	
	/**
	 * An inner class representing the content of a SIP message.
	 */
	private class MessageContent {
		private ContentTypeHeader contentTypeHeader;
		private String            content;
		
		MessageContent(ContentTypeHeader contentTypeHeader, String content) {
			this.contentTypeHeader = contentTypeHeader;
			this.content           = content;
		}
		
		public ContentTypeHeader getContentTypeHeader() {
			return this.contentTypeHeader;
		}
		
		public String getContent() {
			return this.content;
		}
	}
	
	/**
	 * Sends a request with the specified request and transport.
	 * @param request   The request packet.
	 * @param transport The transport protocol used.
	 */
	private void sendRequest(Request request, String transport)
	throws TransactionUnavailableException, TransactionDoesNotExistException, SipException {
		sendRequest(request, transport, null);
	}
	
	/**
	 * Sends a request with the specified request and transport.
	 * @param request   The request packet.
	 * @param transport The transport protocol used.
	 * @param dialog    The dialog for a persistent transaction.
	 *                  Leave it <code>null</code> if no dialog is associated with this request.
	 */
	private void sendRequest(Request request, String transport, Dialog dialog)
	throws TransactionUnavailableException, TransactionDoesNotExistException, SipException {
		for (Iterator sipProviders = sipStack.getSipProviders(); sipProviders.hasNext(); ) {
			SipProvider provider = (SipProvider) sipProviders.next();
			if (provider.getListeningPoint(transport) != null) {
				Log.debug("Sending packet:  \n" + request.toString() + "\n========\n");
				
				ClientTransaction transaction = provider.getNewClientTransaction(request);
				if (dialog != null)
					dialog.sendRequest(transaction);
				else
					transaction.sendRequest();
				
				return;
			}
		}
		
		Log.debug("SimpleSession(" + this.jid.getNode() + "):  No SipProvider found for that transport!");
	}
	
	/**
	 * @param destUri    The SipURI for the destination.  Leave <code>null</code> if a loopback request (e.g. REGISTER) is being made.
	 * @param toTag      The tag for to header.  Can leave null.
	 * @param requestUri The Request URI to set in the message.  Leave null if the default destination SipURI should be used.
	 */
	private Request prepareRequest(RequestType requestType, SipURI destUri, String toTag, SipURI requestUri, String callId, long seqNum) {
		Request request  = null;
		
		String  myJiveId = this.jid.getNode();
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing request packet of type '" + requestType + "'");
		
		try {
			// Prepare request packet first
			request = messageFactory.createRequest(null);
			request.setMethod(requestType.toString());
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing request.", e);
		}
		
		// Prepare "From" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"From\" header...");
		String mySipUsername = registration.getUsername();
		try {
			SipURI     fromUri         = addressFactory.createSipURI(mySipUsername, sipHost);
			Address    fromNameAddress = addressFactory.createAddress(fromUri);
			fromNameAddress.setDisplayName(mySipUsername);
			
			FromHeader fromHeader      = headerFactory.createFromHeader(fromNameAddress, getTag());
			
			// Use "set" because this header is mandatory.
			request.setHeader(fromHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing FromHeader.", e);
			
			return null;
		}
		
		// Prepare "To" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"To\" header...");
		try {
			if (destUri == null)
				destUri = addressFactory.createSipURI(mySipUsername, sipHost);
			
			Address  toNameAddress = addressFactory.createAddress(destUri);
			ToHeader toHeader      = headerFactory.createToHeader(toNameAddress, toTag);
			
			// Use "set" because this header is mandatory.
			request.setHeader(toHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing ToHeader.", e);
			
			return null;
		}
		
		// Prepare "Via" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"Via\" header...");
		try {
			ViaHeader viaHeader = headerFactory.createViaHeader(InetAddress.getLocalHost().getHostAddress(), sipPort, ListeningPoint.UDP, null);
			
			// Use "set" because this header is mandatory.
			request.setHeader(viaHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing ViaHeader.", e);
			
			return null;
		}
		
		// Prepare "CallId" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"CallId\" header...");
		CallIdHeader callIdHeader = null;
		try {
			if (callId != null)
				callIdHeader = headerFactory.createCallIdHeader(callId);
			else
				callIdHeader = udpSipProvider.getNewCallId();
			
			// Use "set" because this header is mandatory.
			request.setHeader(callIdHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing CallIdHeader.", e);
			
			return null;
		}
		
		// Prepare "CSeq" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"CSeq\" header...");
		try {
			CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(seqNum, requestType.toString());
			
			// Use "set" because this header is mandatory.
			request.setHeader(cSeqHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing CSeqHeader.", e);
			
			return null;
		}
		
		// Prepare "MaxForwards" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"MaxForwards\" header...");
		try {
			MaxForwardsHeader maxForwardsHeader = headerFactory.createMaxForwardsHeader(70);
			
			// Use "set" because this header is mandatory.
			request.setHeader(maxForwardsHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing MaxForwardsHeader.", e);
			
			return null;
		}
		
		// Setting Request URI
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  setting request URI...");
		try {
			if (requestUri == null) {
				requestUri = (SipURI) destUri.clone();
				requestUri.setTransportParam(ListeningPoint.UDP);
			}
			request.setRequestURI(requestUri);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when setting request URI.", e);
			
			return null;
		}
		
		// Add "Contact" header
		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"Contact\" header...");
		try {
			SipURI contactURI = addressFactory.createSipURI(mySipUsername, InetAddress.getLocalHost().getHostAddress());
			contactURI.setPort(sipPort);
			
			Address contactAddress      = addressFactory.createAddress(contactURI);
			
			contactAddress.setDisplayName(mySipUsername);
			
			ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
			request.setHeader(contactHeader);
		}
		catch (Exception e) {
			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when adding ContactHeader.", e);
			
			return null;
		}
		
		return request;
	}
	
//	/**
//	 * Prepares a Request packet.
//	 * <br><br>
//	 * The "From", "To", "Via", "Contact", "CSeq", "MaxForwards" and "CallId" headers,
//	 * as well as the content (if provided) are prepared in the method.
//	 * <br>
//	 * Additional headers should be provided as customer headers.  See the "headers" parameter for further details.
//	 * @param requestType An inner request type enum
//	 * @param destination The recipient of this request
//	 * @param toTag       Additional tag code of the destination.  Can usually leave it <code>null</code>.
//	 * @param callId      A String representing an ongoing SIP CallID.  Leave it <code>null</code> if a new CallID should be generated.
//	 * @param seqNum      A sequence number for an ongoing session.
//	 * @param maxForward  Maximum times of forwarding allowed for this packet.
//	 * @param headers     Additional headers that have to be added.  Leave <code>null</code> if no custom header is to be added.
//	 * @param content     An object containing the content of the message, as well as the header defining the content type.
//	 *                    Can leave <code>null</code> if no content is to be specified.
//	 * @see   org.jivesoftware.wildfire.gateway.protocols.simple.SimpleSession.RequestType
//	 */
//	private boolean prepareRequest(
//			RequestType    requestType,
//			String         destination,
//			String         toTag,
//			String         callId,
//			long           seqNum,
//			int            maxForward,
//			List<Header>   headers,
//			MessageContent content) {
//		String myJiveId      = this.jid.getNode();
//		String mySipUsername = registration.getUsername();
//		
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing request packet of type '" + requestType + "'");
//		
//		// Prepare "From" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"From\" header...");
//		FromHeader fromHeader = null;
//		try {
//			SipURI     fromUri         = addressFactory.createSipURI(mySipUsername, sipHost);
//			Address    fromNameAddress = addressFactory.createAddress(fromUri);
//			fromNameAddress.setDisplayName(mySipUsername);
//			
//			fromHeader = headerFactory.createFromHeader(fromNameAddress, getTag());
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing FromHeader.", e);
//			
//			return false;
//		}
//		
//		// Prepare "To" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"To\" header...");
//		ToHeader toHeader = null;
//		
//		String destUsername = "";
//		String destAddress  = "";
//		
//		// Code modification to allow address be input without specifying "sip:"
//		if (destination.startsWith("sip:")) {
//			destination = destination.substring("sip:".length());
//		}
//		
//		if (destination.indexOf("@") > 0) {
//			destUsername = destination.substring(destination.indexOf(":") + 1, destination.indexOf("@"));
//			destAddress  = destination.substring(destination.indexOf("@") + 1);
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  destUsername = '" + destUsername + "';  destAddress = '" + destAddress + "'");
//		}
//		else {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing ToHeader.",
//			          new IllegalArgumentException("The destination specified is not a valid SIP address"));
//			
//			return false;
//		}
//		
//		try {
//			SipURI   toAddress     = addressFactory.createSipURI(destUsername, destAddress);
//			Address  toNameAddress = addressFactory.createAddress(toAddress);
//			
//			String displayName = destUsername;
//			try {
//				RosterItem ri = getRoster().getRosterItem(this.getTransport().convertIDToJID(destination));
//				if (ri != null) displayName = ri.getNickname();
//			}
//			catch (Exception e) {} // Ignore the exception.  We don't need to handle it.
//			
//			toNameAddress.setDisplayName(displayName);
//			
//			toHeader = headerFactory.createToHeader(toNameAddress, toTag);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing ToHeader.", e);
//			
//			return false;
//		}
//		
//		// Prepare "Via" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"Via\" header...");
//		ArrayList viaHeaders = new ArrayList();
//		try {
//			ViaHeader viaHeader  = headerFactory.createViaHeader(InetAddress.getLocalHost().getHostAddress(), sipPort, ListeningPoint.UDP, null);
//			viaHeaders.add(viaHeader);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing ViaHeader.", e);
//			
//			return false;
//		}
//		
//		// Prepare "CallId" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"CallId\" header...");
//		CallIdHeader callIdHeader = null;
//		try {
//			if (callId != null)
//				callIdHeader = headerFactory.createCallIdHeader(callId);
//			else
//				callIdHeader = udpSipProvider.getNewCallId();
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing CallIdHeader.", e);
//			
//			return false;
//		}
//		
//		// Prepare "CSeq" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"CSeq\" header...");
//		CSeqHeader cSeqHeader = null;
//		try {
//			cSeqHeader = headerFactory.createCSeqHeader(seqNum, requestType.toString());
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing CSeqHeader.", e);
//			
//			return false;
//		}
//		
//		// Prepare "MaxForwards" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"MaxForwards\" header...");
//		MaxForwardsHeader maxForwardsHeader = null;
//		try {
//			maxForwardsHeader = headerFactory.createMaxForwardsHeader(maxForward);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing MaxForwardsHeader.", e);
//			
//			return false;
//		}
//
//		// Prepare request URI
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing request URI...");
//		SipURI requestURI = null;
//		try {
//			requestURI = addressFactory.createSipURI(destUsername, destAddress);
//			requestURI.setTransportParam(ListeningPoint.UDP);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when preparing Request URI.", e);
//			
//			return false;
//		}
//		
//		// Instantiate Request packet
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Instantiating Request packet...");
//		Request request = null;
//		try {
//			request = messageFactory.createRequest(
//					requestURI, requestType.toString(),
//					callIdHeader, cSeqHeader,
//					fromHeader, toHeader,
//					viaHeaders, maxForwardsHeader
//					);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when instantiating Request packet.", e);
//			
//			return false;
//		}
//		
//		// Add custom headers
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Start adding custom headers...");
//		int headerCount = 0;
//		if (headers != null) {
//			headerCount = headers.size();
//			for (ListIterator<Header> headersIterator = headers.listIterator(); headersIterator.hasNext(); ) {
//				Header aHeader = headersIterator.next();
//				try {
//					request.addHeader(aHeader);
//				}
//				catch (Exception e) {
//					Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when adding a " +
//					          aHeader.getClass().toString() + " to the request packet.", e);
//					headerCount--;
//				}
//			}
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Finished adding custom headers.  " +
//			          headerCount + " of " + headers.size() + " headers successfully added.");
//		}
//		else {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  The custom headers input is null.  No custom headers to add.");
//		}
//		
//		// Add "Contact" header
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Preparing \"Contact\" header...");
//		try {
//			SipURI contactURI;
//			if (requestType.equals(RequestType.NOTIFY))
//				contactURI = addressFactory.createSipURI(null, InetAddress.getLocalHost().getHostAddress());
//			else
//				contactURI = addressFactory.createSipURI(mySipUsername, InetAddress.getLocalHost().getHostAddress());
//			contactURI.setPort(sipPort);
//			
//			Address contactAddress      = addressFactory.createAddress(contactURI);
//			
//			if (!requestType.equals(RequestType.NOTIFY))
//				contactAddress.setDisplayName(mySipUsername);
//			
//			ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
//			request.addHeader(contactHeader);
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when adding ContactHeader.", e);
//			
//			return false;
//		}
//		
//		if (content != null) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Content is specified.  Adding content...");
//			try {
//				request.setContent(content.getContent(), content.getContentTypeHeader());
//			}
//			catch (Exception e) {
//				Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when adding content to the request packet.", e);
//				// Just tell, then continue the request!
//			}
//		}
//		
//		// Send the request
//		Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Sending Request packet:  \n" + request.toString());
//		try {
//			ClientTransaction clientTransaction = udpSipProvider.getNewClientTransaction(request);
//			clientTransaction.sendRequest();
//		}
//		catch (Exception e) {
//			Log.debug("SimpleSession(" + myJiveId + ").prepareRequest:  Exception occured when sending Request packet.", e);
//			return false;
//		}
//		
//		return true;
//	}
	
	private Request prepareRegisterRequest() {
		return prepareRequest(RequestType.REGISTER, null, null, null, sessionId, seqNum++);
	}
	
	private Request prepareMessageRequest(MessageContent content, String destination) throws InvalidArgumentException, ParseException {
		String destUsername = destination;
		String destHost     = sipHost;
		
		if (destination.indexOf("@") == 0 || destination.indexOf("@") == destination.length() - 1) {
			throw new InvalidArgumentException("The address provided is invalid!");
		}
		else if (destination.indexOf("@") > 0) {
			destUsername = destination.substring(0, destination.indexOf("@"));
			destHost     = destination.substring(destination.indexOf("@") + 1);
		}
		
		SipURI destUri = addressFactory.createSipURI(destUsername, destHost);
		
		Request messageRequest = prepareRequest(RequestType.MESSAGE, destUri, null, destUri, sessionId, seqNum++);
		
		messageRequest.setContent(content.content, content.contentTypeHeader);
		
		return messageRequest;
	}
	
	private Request prepareSubscribeRequest(String destination) throws InvalidArgumentException, ParseException {
		String destUsername = destination;
		String destHost     = sipHost;
		
		if (destination.indexOf("@") == 0 || destination.indexOf("@") == destination.length() - 1) {
			throw new InvalidArgumentException("The address provided is invalid!");
		}
		else if (destination.indexOf("@") > 0) {
			destUsername = destination.substring(0, destination.indexOf("@"));
			destHost     = destination.substring(destination.indexOf("@") + 1);
		}
		
		SipURI destUri = addressFactory.createSipURI(destUsername, destHost);
		
		Request messageRequest = prepareRequest(RequestType.SUBSCRIBE, destUri, null, destUri, null, 1L);
		
		return messageRequest;
	}
	
	private Request prepareNotifyRequest(Dialog dialog) throws ParseException {
		printDialog(dialog);
		
		String  fromTag      = dialog.getRemoteTag();
		Address fromAddress  = dialog.getRemoteParty();
		SipURI  destUri      = (SipURI) fromAddress.getURI();
		
		dialog.incrementLocalSequenceNumber();
		long   seqNum = dialog.getLocalSeqNumber();
		String callId = dialog.getCallId().getCallId();
		
		SipURI fromReqUri = null;
		
		if (dialog != null) {
			Log.debug("Getting request URI from dialog");
			Address fromReqAddr = dialog.getRemoteTarget();
			
			if (fromReqAddr != null && fromReqAddr.getURI() != null && fromReqAddr.getURI() instanceof SipURI)
				fromReqUri = (SipURI) fromReqAddr.getURI();
		}
		
		if (fromReqUri == null) {
			Log.debug("Getting request URI from destination URI");
			fromReqUri = destUri;
		}
		
		// Instantiate request packet
		Request notifyRequest = prepareRequest(RequestType.NOTIFY, destUri, fromTag, fromReqUri, callId, seqNum);
//		Request notifyRequest = dialog.createRequest(Request.NOTIFY);
		
		((FromHeader) notifyRequest.getHeader(FromHeader.NAME)).setTag(dialog.getLocalTag());
		
		// Set "subscription state" header
		SubscriptionStateHeader subscriptionStateHeader = headerFactory.createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE.toLowerCase());
//		if (expires > 0) subscriptionStateHeader.setExpires(expires);
		notifyRequest.setHeader(subscriptionStateHeader);
		
		// Set "event" header
		notifyRequest.setHeader(headerFactory.createEventHeader("presence"));
		
		return notifyRequest;
	}
	
	private Request prepareNotifyRequest(Dialog dialog, SimplePresence simplePresence) throws ParseException {
		Request request = prepareNotifyRequest(dialog);
		request.setContent(simplePresence.toXML(), headerFactory.createContentTypeHeader("application", "pidf+xml"));
		
		return request;
	}
	
	public void contactSubscribed(String targetSipAddress) {
		try {
			Roster     roster     = getTransport().getRosterManager().getRoster(getJID().getNode());
			JID        contactJID = getTransport().convertIDToJID(targetSipAddress);
            RosterItem item       = roster.getRosterItem(contactJID);
			
			Log.debug("SimpleSession(" + jid.getNode() + ").contactSubscribed:  Preparing presence packet...");
			Presence presence = new Presence();
			presence.setFrom(contactJID);
			presence.setTo(getJID());
			presence.setType(Presence.Type.subscribed);
			getTransport().sendPacket(presence);
			Log.debug("SimpleSession(" + jid.getNode() + ").contactSubscribed:  Presence packet sent ==> \n" + presence.toXML());
			
			
//			Log.debug("SimpleSession(" + jid.getNode() + ").contactSubscribed:  Synchronizing SIP user roster...");
//			String rosteruserid = ((SimpleTransport) transport).convertJIDToID(item.getJid());
//			if (myRoster.getEntry(rosteruserid) == null) {
//				SimpleRosterItem simpleRosterItem = new SimpleRosterItem(rosteruserid, item.getNickname(), 1L);
//				myRoster.addEntry(rosteruserid, simpleRosterItem);
//			}
//			Log.debug("SimpleSession(" + jid.getNode() + ").contactSubscribed:  Finished synchronizing SIP user roster.");
			
//			syncContactGroups(contact, item.getGroups());
			
//			unlockRoster(contactJID.toString());
        }
        catch (Exception e) {
			Log.debug("SimpleSession(" + jid.getNode() + ").contactSubscribed:  Exception occured when adding pending contact " + targetSipAddress, e);
			
			JID contactJID = getTransport().convertIDToJID(targetSipAddress);
//			unlockRoster(contactJID.toString());
        }
	}
	
	public void contactUnsubscribed(String targetSipAddress) {
		try {
			Roster     roster     = getTransport().getRosterManager().getRoster(getJID().getNode());
			JID        contactJID = getTransport().convertIDToJID(targetSipAddress);
			RosterItem item       = roster.getRosterItem(contactJID);
			
			Log.debug("SimpleSession(" + getJID().getNode() + ").contactUnsubscribed:  Preparing presence packet...");
			Presence presence = new Presence();
			presence.setFrom(contactJID);
			presence.setTo(getJID());
			presence.setType(Presence.Type.unsubscribed);
			getTransport().sendPacket(presence);
			Log.debug("SimpleSession(" + getJID().getNode() + ").contactUnsubscribed:  Presence packet sent ==> \n" + presence.toXML());
			
//			Log.debug("SimpleSession(" + jid.getNode() + ").contactUnsubscribed:  Synchronizing SIP user roster...");
//			String rosteruserid = ((SimpleTransport) transport).convertJIDToID(item.getJid());
//			myRoster.removeEntry(rosteruserid);
//			Log.debug("SimpleSession(" + jid.getNode() + ").contactUnsubscribed:  Finished synchronizing SIP user roster.");
			
//			syncContactGroups(contact, item.getGroups());
			
//			unlockRoster(contactJID.toString());
        }
        catch (Exception e) {
			Log.debug("SimpleSession(" + getJID().getNode() + ").contactUnsubscribed:  Exception occured when adding pending contact " + targetSipAddress, e);
			
			JID contactJID = getTransport().convertIDToJID(targetSipAddress);
//			unlockRoster(contactJID.toString());
        }
	}
	
	public ServerTransaction sendResponse(int status, Request request, ServerTransaction serverTransaction) {
		try {
			Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  Starting response sending process.");
			
			if (serverTransaction == null)
				serverTransaction = udpSipProvider.getNewServerTransaction(request);
			
			Response response = messageFactory.createResponse(status, request);
			
			// Set "Exprires" header
			if (request.getHeader(ExpiresHeader.NAME) != null)
				response.setHeader(request.getHeader(ExpiresHeader.NAME));
			
			// Add "Contact" header
			Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  Preparing \"Contact\" header...");
			try {
				SipURI contactURI = addressFactory.createSipURI(null, InetAddress.getLocalHost().getHostAddress());
				contactURI.setPort(sipPort);
			
				Address contactAddress      = addressFactory.createAddress(contactURI);
				
//				contactAddress.setDisplayName(mySipUsername);
				
				ContactHeader contactHeader = headerFactory.createContactHeader(contactAddress);
				response.addHeader(contactHeader);
			}
			catch (Exception e) {
				Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  Exception occured when adding ContactHeader.", e);
//				return false;	// We can continue with this though.
			}
			
			Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  Sending response:  " + response.toString());
			
			serverTransaction.sendResponse(response);
//			udpSipProvider.sendResponse(response);
			
			Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  Response sent!");
			
			return serverTransaction;
		}
		catch (Exception ex) {
			Log.debug("SimpleSession(" + jid.getNode() + ").sendResponse:  ", ex);
		}
		
		return null;
	}

	public void sendNotify(Dialog dialog) throws ParseException, SipException, InvalidArgumentException {
		Request notifyRequest = prepareNotifyRequest(dialog);
		
		try {
			User     me         = XMPPServer.getInstance().getUserManager().getUser(getJID().getNode());
			Presence myPresence = XMPPServer.getInstance().getPresenceManager().getPresence(me);
			
			String         presenceContent = "";
			SimplePresence simplePresence  = new SimplePresence();
			simplePresence.setEntity("pres:" + registration.getUsername() + "@" + sipHost);
			simplePresence.setDmNote(myPresence.getStatus());
			
			if (myPresence.getStatus() != null && myPresence.getStatus().equalsIgnoreCase("Offline"))
				simplePresence.setTupleStatus(SimplePresence.TupleStatus.CLOSED);
			else {
				simplePresence.setTupleStatus(SimplePresence.TupleStatus.OPEN);
				
				if (myPresence.getShow() != null) {
					switch (myPresence.getShow()) {
						case away:
							simplePresence.setRpid(SimplePresence.Rpid.AWAY);
							break;
						case dnd:
							simplePresence.setRpid(SimplePresence.Rpid.BUSY);
							break;
						case xa:
							simplePresence.setRpid(SimplePresence.Rpid.AWAY);
							break;
						default:
							break;
					}
				}
			}
			
			presenceContent = simplePresence.toXML();
			
			ContentTypeHeader contentTypeHeader = headerFactory.createContentTypeHeader("application", "pidf+xml");
			notifyRequest.setContent(presenceContent, contentTypeHeader);
		}
		catch (Exception e) {
			Log.debug("Unable to include presence details in the packet.", e);
		}
		
		sendRequest(notifyRequest, ListeningPoint.UDP, dialog);
	}
	
	private String getTag() {
		StringBuffer tag = new StringBuffer(Integer.toHexString(this.hashCode()));
		while (tag.length() < 8) {
			tag.insert(0, "0");
		}
		
		return new String(tag);
	}
	
	void printDialog(Dialog dialog) {
		if (dialog != null) {
			StringBuffer log = new StringBuffer(1024);
			log.append("Printing dialog:  \n");
			log.append("Call id      = ");
			log.append(dialog.getCallId().getCallId());
			log.append("\n");
			log.append("Dialog id    = ");
			log.append(dialog.getDialogId());
			log.append("\n");
			log.append("Local party  = ");
			log.append(dialog.getLocalParty());
			log.append("\n");
			log.append("Remote party = ");
			log.append(dialog.getRemoteParty());
			log.append("\n");
			log.append("Remote targ  = ");
			log.append(dialog.getRemoteTarget());
			log.append("\n");
			log.append("Local seq    = ");
			log.append(dialog.getLocalSeqNumber());
			log.append("\n");
			log.append("Remote seq   = ");
			log.append(dialog.getRemoteSeqNumber());
			log.append("\n");
			log.append("Local tag    = ");
			log.append(dialog.getLocalTag());
			log.append("\n");
			log.append("Remote tag   = ");
			log.append(dialog.getRemoteTag());
			log.append("\n");
			log.append("Dialog state = ");
			log.append(dialog.getState());
			Log.debug(new String(log));
		}
	}
}