/// <summary>
        /// Reset the selected caption to parse it with the new settings.
        /// </summary>
        /// <param name="settings">the updated caption settings</param>
        public override void OnApplyCaptionSettings(CustomCaptionSettings settings)
        {
            if (settings == null)
            {
                var selectedCaptions = this.MediaPlayer.SelectedCaption;

                this.MediaPlayer.SelectedCaption = null;

                this.MediaPlayer.SelectedCaption = selectedCaptions;
            }
        }
        /// <summary>
        /// Gets the Windows Phone 8 font family from the user settings Font Family
        /// </summary>
        /// <param name="userSettings">the user settings</param>
        /// <returns>the font family</returns>
        /// <remarks>See 
        /// <a href="http://msdn.microsoft.com/en-us/library/windowsphone/develop/cc189010(v=vs.105).aspx#silverlight_fonts">Text and fonts for Windows Phone</a>
        /// for more details on the fonts available for Windows Phone 8.
        /// </remarks>
        public static FF.FontFamily GetFontFamily(CustomCaptionSettings userSettings)
        {
            if (fontMap == null)
            {
                fontMap = new Dictionary<Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily, FF.FontFamily>();
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.Default] = null;
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.MonospaceSerif] = new FF.FontFamily("Courier New");
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.ProportionalSerif] = new FF.FontFamily("Times New Roman");
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.MonospaceSansSerif] = new FF.FontFamily("Calibri");
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.ProportionalSansSerif] = new FF.FontFamily("Tahoma");
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.Casual] = new FF.FontFamily("Comic Sans MS");

                // Since Windows Phone 8 does not ship with a cursive font, we use this.
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.Cursive] = new FF.FontFamily("Calibri Italic Light");

                // _Smallcaps is a unique keyword that will trigger the usage of Typography.SetCapitals(textblock, FontCapitals.SmallCaps)
                // and the default font.
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.Smallcaps] = new FF.FontFamily("_Smallcaps");
            }

            var fontName = fontMap[userSettings.FontFamily];

            return fontName;
        }
        /// <summary>
        /// Recursively apply the settings to the UI elements
        /// </summary>
        /// <param name="element">a UI Element</param>
        /// <param name="settings">the custom caption settings</param>
        /// <param name="parent">the parent element</param>
        /// <param name="isRoot">true if this is the root node</param>
        public void ApplySettings(UIElement element, CustomCaptionSettings settings, UIElement parent, bool isRoot = false)
        {
            if (settings == null)
            {
                return;
            }

            var panel       = element as StackPanel;
            var border      = element as Border;
            var textBlock   = element as TextBlock;
            var grid        = element as Grid;

            if (panel != null)
            {
                this.ApplyPanel(settings, isRoot, panel);
            }
            else if (border != null)
            {
                this.ApplyBorder(settings, border);
            }
            else if (textBlock != null)
            {
                this.ApplyText(settings, textBlock, parent);
            }
            else if (grid != null)
            {
                foreach (var item in grid.Children)
                {
                    this.ApplySettings(item, settings, grid, true);
                }
            }
            else
            {
                System.Diagnostics.Debugger.Break();
            }
        }
        /// <summary>
        /// update the capitals for all of the TextBlock elements in a panel.
        /// </summary>
        /// <param name="customCaptionSettings">the custom caption settings
        /// </param>
        /// <param name="panel">the panel</param>
        /// <exception cref="ArgumentNullException">if customCaptionSettings 
        /// or panel are null.</exception>
        private static void UpdateCaptials(
            CustomCaptionSettings customCaptionSettings,
            Panel panel)
        {
            if (customCaptionSettings == null)
            {
                throw new ArgumentNullException(
                    "customCaptionSettings",
                    "customCaptionSettings cannot be null.");
            }

            if (panel == null)
            {
                return;
            }

            var children = panel.Children.OfType<TextBlock>();

            foreach (var item in children)
            {
                if (customCaptionSettings.FontFamily == Model.FontFamily.Smallcaps)
                {
                    Typography.SetCapitals(item, FontCapitals.SmallCaps);
                }
                else
                {
                    Typography.SetCapitals(item, FontCapitals.Normal);
                }
            }
        }
        /// <summary>
        /// update the capitals for all of the TextBlock elements in a panel.
        /// </summary>
        /// <param name="customCaptionSettings">the custom caption settings
        /// </param>
        /// <param name="panel">the panel</param>
        /// <exception cref="ArgumentNullException">if customCaptionSettings 
        /// or panel are null.</exception>
        private static void UpdateCaptials(
            CustomCaptionSettings customCaptionSettings, 
            Panel panel)
        {
            if (customCaptionSettings == null)
            {
                throw new ArgumentNullException(
                    "customCaptionSettings", 
                    "customCaptionSettings cannot be null.");
            }

            if (panel == null)
            {
                throw new ArgumentNullException(
                    "panel", 
                    "panel cannot be null.");
            }

            var children = panel.Children.OfType<TextBlock>();

            foreach (var item in children)
            {
                if (customCaptionSettings.FontFamily == Model.FontFamily.Smallcaps)
                {
                    Typography.SetCapitals(item, FontCapitals.SmallCaps);
                }
                else
                {
                    Typography.SetCapitals(item, FontCapitals.Normal);
                }

                ////System.Diagnostics.Debug.WriteLine("Captials for {0} are {1}", item.Name, Typography.GetCapitals(item));
            }
        }
        /// <summary>
        /// Gets the font family from the user settings Font
        /// </summary>
        /// <param name="userSettings">the user settings</param>
        /// <returns>the font family</returns>
        private static FF.FontFamily GetFontFamily(CustomCaptionSettings userSettings)
        {
            if (fontMap == null)
            {
                fontMap = new Dictionary<Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily, FF.FontFamily>();

                // _Smallcaps is a unique keyword that will trigger the usage of Typography.SetCapitals(textblock, FontCapitals.SmallCaps)
                // and the default font.
                fontMap[Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily.Smallcaps] = new FF.FontFamily("_Smallcaps");
            }

            FF.FontFamily fontFamily;

            if (fontMap.TryGetValue(userSettings.FontFamily, out fontFamily))
            {
                return fontFamily;
            }

            var name = GetFontFamilyName(userSettings.FontFamily);

            if (name == null)
            {
                return null;
            }

            fontFamily = new FF.FontFamily(name);

            fontMap[userSettings.FontFamily] = fontFamily;

            return fontFamily;
        }
        /// <summary>
        /// Apply the font style
        /// </summary>
        /// <param name="captionElement">the caption element</param>
        /// <param name="userSettings">the user settings</param>
        private static void ApplyFontStyle(TimedTextElement captionElement, CustomCaptionSettings userSettings)
        {
            var outlineWidth = 1.0;

            if (userSettings.FontSize.HasValue)
            {
                outlineWidth = System.Convert.ToDouble(userSettings.FontSize.Value) / 100.0;
            }

            var outlineColor = Media.Colors.Black;

            if (userSettings.FontColor != null)
            {
                outlineColor = Media.Color.FromArgb(255, 0, 0, 0);
            }

            switch (userSettings.FontStyle)
            {
                case FontStyle.Default:
                    captionElement.Style.TextStyle = TextStyle.Default;

                    // Todo: look at code for calculation of OutlineWidth and OutlineBlur
                    captionElement.Style.OutlineWidth = new Length { Value = outlineWidth, Unit = LengthUnit.Pixel };
                    captionElement.Style.OutlineColor = outlineColor;
                    break;

                case FontStyle.DepressedEdge:
                    captionElement.Style.TextStyle = TextStyle.DepressedEdge;
                    captionElement.Style.OutlineColor = outlineColor;
                    captionElement.Style.OutlineWidth = new Length { Value = outlineWidth, Unit = LengthUnit.Pixel };
                    break;

                case FontStyle.DropShadow:
                    captionElement.Style.TextStyle = TextStyle.DropShadow;

                    // Todo: look at code for calculation of OutlineWidth and OutlineBlur
                    captionElement.Style.OutlineColor = outlineColor;
                    captionElement.Style.OutlineWidth = new Length { Value = outlineWidth, Unit = LengthUnit.Pixel };
                    break;

                case FontStyle.None:
                    captionElement.Style.TextStyle = TextStyle.None;
                    break;

                case FontStyle.Outline:
                    captionElement.Style.TextStyle = TextStyle.Outline;
                    captionElement.Style.OutlineWidth = new Length { Value = outlineWidth, Unit = LengthUnit.Pixel };
                    captionElement.Style.OutlineColor = outlineColor;
                    break;

                case FontStyle.RaisedEdge:
                    captionElement.Style.TextStyle = TextStyle.RaisedEdge;
                    captionElement.Style.OutlineWidth = new Length { Value = outlineWidth, Unit = LengthUnit.Pixel };
                    captionElement.Style.OutlineColor = outlineColor;
                    break;
            }
        }
        /// <summary>
        /// Recursively update the caption elements
        /// </summary>
        /// <param name="captionElement">the caption element</param>
        /// <param name="userSettings">the user settings</param>
        /// <param name="level">the 0-based level of the caption element</param>
        private static void UpdateElement(
            TimedTextElement captionElement,
            CustomCaptionSettings userSettings,
            uint level)
        {
            if (level == 0)
            {
                if (userSettings.WindowColor != null)
                {
                    captionElement.Style.BackgroundColor = userSettings.WindowColor.ToColor();
                }
            }
            else if (level == 1)
            {
                if (userSettings.BackgroundColor != null)
                {
                    captionElement.Style.BackgroundColor = userSettings.BackgroundColor.ToColor();
                }
            }

            if (userSettings.FontColor != null)
            {
                captionElement.Style.Color = userSettings.FontColor.ToColor();
            }

            if (userSettings.FontSize.HasValue)
            {
                captionElement.Style.FontSize = new Length
                {
                    Unit = captionElement.Style.FontSize.Unit,
                    Value = System.Convert.ToDouble(userSettings.FontSize.Value) * captionElement.Style.FontSize.Value / 100.0
                };
            }

            var fontFamily = GetFontFamily(userSettings);

            if (fontFamily != null)
            {
                captionElement.Style.FontFamily = fontFamily;

#if WINDOWS_PHONE
                if (userSettings.FontFamily == FontFamily.Cursive)
                {
                    captionElement.Style.FontStyle = System.Windows.FontStyles.Italic;
                }
#endif
            }

            ApplyFontStyle(captionElement, userSettings);

            var children = captionElement.Children as MediaMarkerCollection<TimedTextElement>;

            if (children != null)
            {
                foreach (var child in children)
                {
                    UpdateElement(child, userSettings, level + 1);
                }
            }
        }
        /// <summary>
        /// Gets the Windows or Windows Phone font family from the user 
        /// settings Font
        /// </summary>
        /// <param name="settings">the custom caption settings</param>
        /// <returns>the font family</returns>
        private Media.FontFamily GetFontFamily(CustomCaptionSettings settings)
        {
            if (this.fontMap == null)
            {
                this.fontMap = new Dictionary<Microsoft.PlayerFramework.CaptionSettings.Model.FontFamily, Media.FontFamily>();
            }

            Media.FontFamily fontFamily;

            if (this.fontMap.TryGetValue(settings.FontFamily, out fontFamily))
            {
                return fontFamily;
            }

            string fontName = CC608CaptionSettingsPlugin.GetFontFamilyName(settings.FontFamily);

            if (fontName == null)
            {
                return null;
            }

            fontFamily = new Media.FontFamily(fontName);

            this.fontMap[settings.FontFamily] = fontFamily;

            return fontFamily;
        }
        /// <summary>
        /// Apply a depressed edge
        /// </summary>
        /// <param name="textBlock">the text block</param>
        /// <param name="parent">the parent</param>
        /// <param name="settings">the settings</param>
        private void ApplyDepressedEdge(TextBlock textBlock, UIElement parent, CustomCaptionSettings settings)
        {
            var offset = 1.5;

            if (settings.FontSize.HasValue)
            {
                offset = offset * settings.FontSize.Value / 100;
            }

            var newTextBlocks = new TextBlock[]
            {
                new TextBlock
                {
                    Name = "TopLeft",
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = -offset,
                        Y = -offset
                    },
                },
                new TextBlock
                {
                    Name = "Top",
                    RenderTransform = new Media.TranslateTransform
                    {
                        Y = -offset
                    },
                },
                new TextBlock
                {
                    Name = "Left",
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = -offset,
                    },
                },
            };

            CopyAttributes(textBlock, newTextBlocks, parent, 0.25, 1);
        }
        /// <summary>
        /// Apply an outline
        /// </summary>
        /// <param name="textBlock">the text block</param>
        /// <param name="parent">the parent</param>
        /// <param name="settings">the settings</param>
        private void ApplyOutline(TextBlock textBlock, UIElement parent, CustomCaptionSettings settings)
        {
            var offset = 1.0;

            if (settings.FontSize.HasValue)
            {
                offset = settings.FontSize.Value / 100;
            }
            
            var newTextBlocks = new TextBlock[]
            {
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = -offset,
                        Y = -offset
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        Y = -offset
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = offset,
                        Y = -offset
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = -offset,
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = offset,
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = -offset,
                        Y = offset
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        Y = offset
                    },
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = offset,
                        Y = offset
                    }
                },
            };

            CopyAttributes(textBlock, newTextBlocks, parent, textBlock.Opacity, -1);
        }
        /// <summary>
        /// Apply the font style
        /// </summary>
        /// <param name="settings">the caption settings</param>
        /// <param name="textBlock">the text block</param>
        /// <param name="parent">the text block's parent</param>
        private void ApplyFontStyle(CustomCaptionSettings settings, TextBlock textBlock, UIElement parent)
        {
            switch (settings.FontStyle)
            {
                case Model.FontStyle.Default:
                    // default style is Outline
                    this.ApplyOutline(textBlock, parent, settings);
                    break;

                case Model.FontStyle.DepressedEdge:
                    this.ApplyDepressedEdge(textBlock, parent, settings);
                    break;

                case Model.FontStyle.DropShadow:
                    this.ApplyDropShadow(textBlock, parent, settings);
                    break;

                case Model.FontStyle.Outline:
                    this.ApplyOutline(textBlock, parent, settings);
                    break;

                case Model.FontStyle.RaisedEdge:
                    this.ApplyRaisedEdge(textBlock, parent, settings);
                    break;

                case Model.FontStyle.None:
                    break;
            }
        }
        /// <summary>
        /// Apply setting to a TextBlock
        /// </summary>
        /// <param name="settings">the custom caption settings</param>
        /// <param name="textBlock">the text block</param>
        /// <param name="parent">the parent</param>
        private void ApplyText(CustomCaptionSettings settings, TextBlock textBlock, UIElement parent)
        {
            if (string.IsNullOrWhiteSpace(textBlock.Text))
            {
                return;
            }

            this.ApplyFontStyle(settings, textBlock, parent);
        }
        /// <summary>
        /// Apply settings to a panel
        /// </summary>
        /// <param name="settings">the custom caption settings</param>
        /// <param name="isRoot">is this the root panel</param>
        /// <param name="panel">the panel</param>
        private void ApplyPanel(CustomCaptionSettings settings, bool isRoot, StackPanel panel)
        {
            var children = System.Convert.ToDouble(panel.Children.Count);

            var startIndex = -1;

            var endIndex = -1;

            var index = 0;

            foreach (var item in panel.Children)
            {
                bool empty = IsRowEmpty(item);

                if (!empty && startIndex == -1)
                {
                    startIndex = index;
                }

                if (!empty)
                {
                    endIndex = index + 1;
                }

                this.ApplySettings(item, settings, panel);

                index++;
            }

            if (isRoot && settings.WindowColor != null)
            {
                if (startIndex >= 0 && endIndex >= 0)
                {
                    var startPercentage = Math.Max(0.0, (System.Convert.ToDouble(startIndex) / children) - 0.01);
                    var endPercentage = Math.Min(1.0, (System.Convert.ToDouble(endIndex) / children) + 0.01);

                    var gradientStops = new Media.GradientStopCollection();
                    var color = settings.WindowColor.ToColor();

                    gradientStops.Add(new Media.GradientStop
                        {
                            Color = Colors.Transparent,
                            Offset = startPercentage
                        });
                    gradientStops.Add(new Media.GradientStop
                    {
                        Color = color,
                        Offset = startPercentage
                    });
                    gradientStops.Add(new Media.GradientStop
                    {
                        Color = color,
                        Offset = endPercentage
                    });
                    gradientStops.Add(new Media.GradientStop
                    {
                        Color = Colors.Transparent,
                        Offset = endPercentage
                    });

                    panel.Background = new Media.LinearGradientBrush(gradientStops, 90);
                }
            }
        }
 /// <summary>
 /// Apply settings to a border
 /// </summary>
 /// <param name="settings">the custom caption settings</param>
 /// <param name="border">a border</param>
 private void ApplyBorder(CustomCaptionSettings settings, Border border)
 {
     this.ApplySettings(border.Child, settings, border);
 }
        /// <summary>
        /// Apply a drop shadow
        /// </summary>
        /// <param name="textBlock">the text block</param>
        /// <param name="parent">the parent</param>
        /// <param name="settings">the settings</param>
        private void ApplyDropShadow(TextBlock textBlock, UIElement parent, CustomCaptionSettings settings)
        {
            var offset = this.DropShadowOffset;

            if (settings.FontSize.HasValue)
            {
                offset = offset * settings.FontSize.Value / 100;
            }

            var newTextBlocks = new TextBlock[]
            {
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = offset
                    }
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        Y = offset
                    }
                },
                new TextBlock
                {
                    RenderTransform = new Media.TranslateTransform
                    {
                        X = offset,
                        Y = offset
                    }
                },
            };

            CopyAttributes(textBlock, newTextBlocks, parent, 0.5, -1);
        }