/// <summary>
        /// Send a alert notification to all iPhone devices subscribed to a given subscription.
        /// </summary>
        /// <param name="subId">Id of the subscription to send the message to.</param>
        /// <param name="message">The message text.</param>
        /// <param name="count">Number to show on the badge.</param>
        /// <param name="sound">Sound file for the iPhone notification.</param>
        /// <returns>Returns "success" if successful otherwise an error.</returns>        
        public string SendIphoneNotification(string subId, string message, string count, string sound)
        {
            // Make sure user is authenticated.
            if (!AuthManager.AuthenticateUser())
            {
                AuthManager.ConstructAuthResponse();
                return null;
            }

            // Make sure subscription name is created.
            bool subExists = this.subscriptionInfoMgr.IsSubscriptionRegistered(subId);
            if (!subExists)
            {
                throw new WebFaultException<string>(Utils.GetErrorString(PushMessageError.ErrorSubscriptionNameNotFound), System.Net.HttpStatusCode.BadRequest);
            }

            // Make sure that the count is an int.
            int countVal;
            bool success = int.TryParse(count, out countVal);
            if (!success)
            {
                throw new WebFaultException<string>(Utils.GetErrorString(PushMessageError.ErrorIllegalCount), System.Net.HttpStatusCode.BadRequest);
            }

            try
            {
                iPhoneMessage iPhoneMsg = new iPhoneMessage(subId, message, count, sound);
                this.msgQueue.Enque(iPhoneMsg);
            }
            catch (Exception e)
            {
                Trace.TraceError(string.Format(CultureInfo.InvariantCulture, "Internal Error: SendiOSAlert subscription: {0} title: {1}, Error: {2}", subId, message, e.Message));
                throw new WebFaultException<string>(e.Message, System.Net.HttpStatusCode.InternalServerError);
            }

            return "success";
        }
        /// <summary>
        /// Enque message for a particular device. For iPhone, we have to pack each message separately as the device ID is put in the message
        /// </summary>
        /// <param name="device"></param>
        /// <param name="msg"></param>
        private void EnqueiOSMessage(DeviceDataModel device, iPhoneMessage msg)
        {
            byte[] apnsNotificationBytes;
            int numberOfRetries = 0;
            if (device.DeviceType == "iOS")
            {
                if (!CheckDeviceId(device.DeviceId))
                {
                    return;
                }

                // compose the message
                apnsNotificationBytes = this.ComposeAPNSMessage(device.DeviceId, msg as iPhoneMessage);
                while (numberOfRetries < this.SendRetries)
                {
                    try
                    {
                        // and send the notification
                        this.SendiPhoneDeviceNotification(apnsNotificationBytes);
                        return;
                    }
                    catch (DeviceIdFormatException e)
                    {
                        // device ID was wrong. Send an error to listener
                        if (this.DeviceIdFormatError != null)
                        {
                            this.DeviceIdFormatError(this, new NotificationEventArgs(e));
                        }
                    }
                    catch (NotificationFormatException e)
                    {
                        // notification format was wrong. send error to listner
                        if (this.NotificationFormatError != null)
                        {
                            this.NotificationFormatError(this, new NotificationEventArgs(e));
                        }
                    }
                    catch (ObjectDisposedException e)
                    {
                        if (this.NotificationFailed != null)
                        {
                            this.NotificationFailed(this, new NotificationEventArgs(e));
                        }

                        this.DisconnectTCPClient();
                    }
                    catch (System.IO.IOException e)
                    {
                        if (this.NotificationFailed != null)
                        {
                            this.NotificationFailed(this, new NotificationEventArgs(e));
                        }

                        this.DisconnectTCPClient();
                    }

                    numberOfRetries++;
                }
            }
        }
        // uses .NET serializer to serialize the message. 
        // here is how it should look like {"aps": {"badge":3, "alert":"This is my alert", "sound":"default"}}
        private static string ToJson(iPhoneMessage message)
        {
            JavaScriptSerializer js = new JavaScriptSerializer();
            AppleNotification an = new AppleNotification();
            if (!string.IsNullOrEmpty(message.Alert))
            {
                an.Aps.Alert = message.Alert;
            }
            else
            {
                an.Aps.Alert = string.Empty;
            }

            if (!string.IsNullOrEmpty(message.Badge))
            {
                an.Aps.Badge = int.Parse(message.Badge, CultureInfo.InvariantCulture);
            }

            if (!string.IsNullOrEmpty(message.Sound))
            {
                an.Aps.Sound = message.Sound;
            }
            else
            {
                an.Aps.Sound = string.Empty;
            }

            string str = js.Serialize(an);
            return str;
        }
        /// <summary>
        /// Compose the APNS message per format . Look up Apple's docs. This packs the message in a binary format. 
        /// </summary>
        /// <param name="deviceID"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        private byte[] ComposeAPNSMessage(string deviceID, iPhoneMessage message)
        {
            // get byte size of deviceID
            byte[] deviceIDBytes = new byte[deviceID.Length / 2];

            // get deviceID bytes
            for (int i = 0; i < deviceIDBytes.Length; i++)
            {
                deviceIDBytes[i] = byte.Parse(deviceID.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, CultureInfo.InvariantCulture);
            }

            if (deviceIDBytes.Length != DeviceTokenBinarySize)
            {
                throw new DeviceIdFormatException(deviceID);
            }

            // size is always 32
            byte[] deviceIDSize = new byte[2] { 0, 32 };
            string messageInJson = ToJson(message).Replace("Aps", "aps").Replace("Alert", "alert").Replace("Badge", "badge").Replace("Sound", "sound"); 
            byte[] messageBytes = Encoding.UTF8.GetBytes(messageInJson);

            // if the message is longer, trim the alert
            if (messageBytes.Length > MaxPayloadSize)
            {
                int newSize = message.Alert.Length - (messageBytes.Length - MaxPayloadSize);
                if (newSize > 0)
                {
                    message.Alert = message.Alert.Substring(0, newSize);
                    messageBytes = Encoding.UTF8.GetBytes(message.ToString());
                }
            }

            // convert the message to binary
            byte[] messageBytesSize = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(Convert.ToInt16(messageBytes.Length)));

            // find the titak buffer size
            int bufferSize = sizeof(byte) + deviceIDSize.Length + deviceIDBytes.Length + messageBytesSize.Length + messageBytes.Length;
            byte[] apnsMessage = new byte[bufferSize];

            // pack the message
            apnsMessage[0] = 0x00;
            Buffer.BlockCopy(deviceIDSize, 0, apnsMessage, sizeof(byte), deviceIDSize.Length);
            Buffer.BlockCopy(deviceIDBytes, 0, apnsMessage, sizeof(byte) + deviceIDSize.Length, deviceIDBytes.Length);
            Buffer.BlockCopy(messageBytesSize, 0, apnsMessage, sizeof(byte) + deviceIDSize.Length + deviceIDBytes.Length, messageBytesSize.Length);
            Buffer.BlockCopy(messageBytes, 0, apnsMessage, sizeof(byte) + deviceIDSize.Length + deviceIDBytes.Length + messageBytesSize.Length, messageBytes.Length);

            return apnsMessage;
        }
 /// <summary>
 /// Initializes a new instance of the NotificationFormatException class.
 /// </summary>
 /// This is needed for ISerializable interface
 /// <param name="serializationInfo">SerializationInfo provides the class where the class is serialized.</param>
 /// <param name="streamingContext">Additional StreamingContext class.</param>
 protected NotificationFormatException(SerializationInfo serializationInfo, StreamingContext streamingContext) :
     base(serializationInfo, streamingContext)
 {
     if (serializationInfo != null)
     {
         PushMessageType messageType = (PushMessageType)serializationInfo.GetInt16("MessageType");
         string subscriptionName = serializationInfo.GetString("SubscriptionName");
         if ((PushMessageType)this.NotificationMessage.MessageType == PushMessageType.Raw)
         {
             string rawMessageText = serializationInfo.GetString("rawMessageText");
             RawMessage rawMessage = new RawMessage(subscriptionName, rawMessageText);
             this.NotificationMessage = rawMessage;
         }
         else if ((PushMessageType)this.NotificationMessage.MessageType == PushMessageType.Toast)
         {
             string tileMessageText = serializationInfo.GetString("toastMessageText");
             ToastMessage toastMessage = new ToastMessage(subscriptionName, tileMessageText);
             this.NotificationMessage = toastMessage;
         }
         else if ((PushMessageType)this.NotificationMessage.MessageType == PushMessageType.Tile)
         {
             string tileMessageTitle = serializationInfo.GetString("tileMessageTitle");
             string tileMessageCount = serializationInfo.GetString("tileMessageCount");
             string tileMessageUrl = serializationInfo.GetString("tileMessageUrl");
             TileMessage tileMessage = new TileMessage(subscriptionName, tileMessageTitle, tileMessageCount, tileMessageUrl);
             this.NotificationMessage = tileMessage;
         }
         else if ((PushMessageType)this.NotificationMessage.MessageType == PushMessageType.Iphone)
         {
             string messageAlert = serializationInfo.GetString("iPhoneMessageAlert");
             string messageBadge = serializationInfo.GetString("iPhoneMessageBadge");
             string messageSound = serializationInfo.GetString("iPhoneMessageSound");
             iPhoneMessage iphoneMessage = new iPhoneMessage(subscriptionName, messageAlert, messageBadge, messageSound);
             this.NotificationMessage = iphoneMessage;
         }
         else if ((PushMessageType)this.NotificationMessage.MessageType == PushMessageType.Common)
         {
             string messageTitle = serializationInfo.GetString("commmonMessageTitle");
             int messageCount = serializationInfo.GetInt32("commonMessageCount");
             string messageImage = serializationInfo.GetString("commonMessageImage");
             string messageSound = serializationInfo.GetString("commonMessageSound");
             CommonMessage commonMessage = new CommonMessage(subscriptionName, messageTitle, messageCount, messageImage, messageSound);
             this.NotificationMessage = commonMessage;
         }
     }
 }