    ﻿using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Xml;

    namespace Nikse.SubtitleEdit.Core.SubtitleFormats
    {
        public class AdvancedSubStationAlpha : SubtitleFormat
        {
            public string Errors { get; private set; }

            public static string DefaultStyle
            {
                get
                {
                    string borderStyle = "1"; // 1=normal, 3=opaque box
                    if (Configuration.Settings.SubtitleSettings.SsaOpaqueBox)
                        borderStyle = "3";

                    string boldStyle = "0"; // 0=regular
                    if (Configuration.Settings.SubtitleSettings.SsaFontBold)
                        boldStyle = "-1";

                    var ssa = Configuration.Settings.SubtitleSettings;
                    return "Style: Default," + ssa.SsaFontName + "," +
                           (int)ssa.SsaFontSize + "," +
                           GetSsaColorString(Color.FromArgb(ssa.SsaFontColorArgb)) + "," +
                           "&H0300FFFF,&H00000000,&H02000000," + boldStyle + ",0,0,0,100,100,0,0," + borderStyle + "," + ssa.SsaOutline.ToString(CultureInfo.InvariantCulture) + "," +
                           Configuration.Settings.SubtitleSettings.SsaShadow.ToString(CultureInfo.InvariantCulture) + ",2," + ssa.SsaMarginLeft + "," + ssa.SsaMarginRight + "," + ssa.SsaMarginTopBottom + ",1";
                }
            }

            public static string DefaultHeader
            {
                get
                {
                    SubtitleFormat format = new AdvancedSubStationAlpha();
                    var sub = new Subtitle();
                    string text = format.ToText(sub, string.Empty);
                    var lines = text.SplitToLines();
                    format.LoadSubtitle(sub, lines, string.Empty);
                    return sub.Header;
                }
            }

            public override string Extension => ".ass";

            public const string NameOfFormat = "Advanced Sub Station Alpha";

            public override string Name => NameOfFormat;

            public override bool IsMine(List<string> lines, string fileName)
            {
                var subtitle = new Subtitle();

                var sb = new StringBuilder();
                lines.ForEach(line => sb.AppendLine(line));
                string all = sb.ToString();
                if (!string.IsNullOrEmpty(fileName) && fileName.EndsWith(".ass", StringComparison.OrdinalIgnoreCase) && !all.Contains("[V4 Styles]"))
                {
                }
                else if (!all.Contains("dialog:", StringComparison.OrdinalIgnoreCase) && !all.Contains("dialogue:", StringComparison.OrdinalIgnoreCase))
                {
                    return false;
                }
                else if (!all.Contains("[V4+ Styles]") && new SubStationAlpha().IsMine(lines, fileName))
                {
                    return false;
                }

                LoadSubtitle(subtitle, lines, fileName);
                Errors = null;
                if (subtitle.Paragraphs.Count > _errorCount)
                {
                    if (!string.IsNullOrEmpty(subtitle.Header))
                        subtitle.Header = subtitle.Header.Replace("[V4 Styles]", "[V4+ Styles]");
                    return true;
                }
                return false;
            }

            public const string HeaderNoStyles = @"[Script Info]
    ; This is an Advanced Sub Station Alpha v4+ script.
    Title: {0}
    ScriptType: v4.00+
    Collisions: Normal
    PlayDepth: 0

    [V4+ Styles]
    Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
    {1}

    [Events]
    Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text";

            public override string ToText(Subtitle subtitle, string title)
            {
                bool fromTtml = false;
                string header = @"[Script Info]
    ; This is an Advanced Sub Station Alpha v4+ script.
    Title: {0}
    ScriptType: v4.00+
    Collisions: Normal
    PlayDepth: 0

    [V4+ Styles]
    Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
    " + DefaultStyle + @"

    [Events]
    Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text";

                const string timeCodeFormat = "{0}:{1:00}:{2:00}.{3:00}"; // h:mm:ss.cc
                const string paragraphWriteFormat = "Dialogue: {9},{0},{1},{3},{4},{5},{6},{7},{8},{2}";
                const string commentWriteFormat = "Comment: {9},{0},{1},{3},{4},{5},{6},{7},{8},{2}";

                var sb = new StringBuilder();
                bool isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
                var styles = new List<string>();
                if (isValidAssHeader)
                {
                    sb.AppendLine(subtitle.Header.Trim());
                    sb.AppendLine("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text");
                    styles = GetStylesFromHeader(subtitle.Header);
                }
                else if (!string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4 Styles]"))
                {
                    LoadStylesFromSubstationAlpha(subtitle, title, header, HeaderNoStyles, sb);
                }
                else if (subtitle.Header != null && subtitle.Header.Contains("http://www.w3.org/ns/ttml"))
                {
                    LoadStylesFromTimedText10(subtitle, title, header, HeaderNoStyles, sb);
                    fromTtml = true;
                    isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
                    if (isValidAssHeader)
                    {
                        styles = GetStylesFromHeader(subtitle.Header);
                    }
                }
                else if (subtitle.Header != null && subtitle.Header.Contains("http://www.w3.org/2006/10/ttaf1"))
                {
                    LoadStylesFromTimedTextTimedDraft2006Oct(subtitle, title, header, HeaderNoStyles, sb);
                    fromTtml = true;
                    isValidAssHeader = !string.IsNullOrEmpty(subtitle.Header) && subtitle.Header.Contains("[V4+ Styles]");
                    if (isValidAssHeader)
                    {
                        styles = GetStylesFromHeader(subtitle.Header);
                    }
                }
                else
                {
                    sb.AppendLine(string.Format(header, title));
                }
                foreach (Paragraph p in subtitle.Paragraphs)
                {
                    string start = string.Format(timeCodeFormat, p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, p.StartTime.Milliseconds / 10);
                    string end = string.Format(timeCodeFormat, p.EndTime.Hours, p.EndTime.Minutes, p.EndTime.Seconds, p.EndTime.Milliseconds / 10);
                    string style = "Default";
                    if (!string.IsNullOrEmpty(p.Extra) && isValidAssHeader && styles.Contains(p.Extra))
                        style = p.Extra;
                    if (fromTtml && !string.IsNullOrEmpty(p.Style) && isValidAssHeader && styles.Contains(p.Style))
                        style = p.Style;

                    string actor = "";
                    if (!string.IsNullOrEmpty(p.Actor))
                        actor = p.Actor;

                    string marginL = "0";
                    if (!string.IsNullOrEmpty(p.MarginL) && Utilities.IsInteger(p.MarginL))
                        marginL = p.MarginL;
                    string marginR = "0";
                    if (!string.IsNullOrEmpty(p.MarginR) && Utilities.IsInteger(p.MarginR))
                        marginR = p.MarginR;
                    string marginV = "0";
                    if (!string.IsNullOrEmpty(p.MarginV) && Utilities.IsInteger(p.MarginV))
                        marginV = p.MarginV;

                    string effect = "";
                    if (!string.IsNullOrEmpty(p.Effect))
                        effect = p.Effect;

                    if (p.IsComment)
                        sb.AppendLine(string.Format(commentWriteFormat, start, end, FormatText(p), style, actor, marginL, marginR, marginV, effect, p.Layer));
                    else
                        sb.AppendLine(string.Format(paragraphWriteFormat, start, end, FormatText(p), style, actor, marginL, marginR, marginV, effect, p.Layer));
                }

                if (!string.IsNullOrEmpty(subtitle.Footer) && (subtitle.Footer.Contains("[Fonts]" + Environment.NewLine) || subtitle.Footer.Contains("[Graphics]" + Environment.NewLine)))
                {
                    sb.AppendLine();
                    sb.AppendLine(subtitle.Footer);
                }
                return sb.ToString().Trim();
            }

            private static void LoadStylesFromSubstationAlpha(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
            {
                try
                {
                    bool styleFound = false;
                    var ttStyles = new StringBuilder();
                    // Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
                    const string styleFormat = "Style: {0},{1},{2},{3},{4},{5},{6},{7},{8},{9},0,100,100,0,0,{10},{11},{12},{13},{14},{15},{16},1";
                    foreach (string styleName in GetStylesFromHeader(subtitle.Header))
                    {
                        try
                        {
                            var ssaStyle = GetSsaStyle(styleName, subtitle.Header);
                            if (ssaStyle != null)
                            {
                                string bold = "0";
                                if (ssaStyle.Bold)
                                    bold = "-1";
                                string italic = "0";
                                if (ssaStyle.Italic)
                                    italic = "-1";
                                string underline = "0";
                                if (ssaStyle.Underline)
                                    underline = "-1";

                                string newAlignment = "2";
                                switch (ssaStyle.Alignment)
                                {
                                    case "1":
                                        newAlignment = "1";
                                        break;
                                    case "3":
                                        newAlignment = "3";
                                        break;
                                    case "9":
                                        newAlignment = "4";
                                        break;
                                    case "10":
                                        newAlignment = "5";
                                        break;
                                    case "11":
                                        newAlignment = "6";
                                        break;
                                    case "5":
                                        newAlignment = "7";
                                        break;
                                    case "6":
                                        newAlignment = "8";
                                        break;
                                    case "7":
                                        newAlignment = "9";
                                        break;
                                }

                                ttStyles.AppendLine(string.Format(styleFormat, ssaStyle.Name, ssaStyle.FontName, ssaStyle.FontSize, GetSsaColorString(ssaStyle.Primary), GetSsaColorString(ssaStyle.Secondary),
                                    GetSsaColorString(ssaStyle.Outline), GetSsaColorString(ssaStyle.Background), bold, italic, underline, ssaStyle.BorderStyle, ssaStyle.OutlineWidth, ssaStyle.ShadowWidth,
                                    newAlignment, ssaStyle.MarginLeft, ssaStyle.MarginRight, ssaStyle.MarginVertical));
                                styleFound = true;
                            }
                        }
                        catch
                        {
                            // ignored
                        }
                    }

                    if (styleFound)
                    {
                        sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
                        subtitle.Header = sb.ToString();
                    }
                    else
                    {
                        sb.AppendLine(string.Format(header, title));
                    }
                }
                catch
                {
                    sb.AppendLine(string.Format(header, title));
                }
            }

            public static void LoadStylesFromTimedText10(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
            {
                foreach (Paragraph p in subtitle.Paragraphs)
                {
                    p.Effect = null;
                }

                try
                {
                    var lines = subtitle.Header.SplitToLines();
                    var tt = new TimedText10();
                    var sub = new Subtitle();
                    tt.LoadSubtitle(sub, lines, string.Empty);

                    var xml = new XmlDocument();
                    xml.LoadXml(subtitle.Header);
                    var nsmgr = new XmlNamespaceManager(xml.NameTable);
                    nsmgr.AddNamespace("ttml", "http://www.w3.org/ns/ttml");
                    XmlNode head = xml.DocumentElement.SelectSingleNode("ttml:head", nsmgr);
                    int styleCount = 0;
                    var ttStyles = new StringBuilder();
                    var styleNames = new List<string>();
                    foreach (XmlNode node in head.SelectNodes("//ttml:style", nsmgr))
                    {
                        string name = null;
                        if (node.Attributes["xml:id"] != null)
                            name = node.Attributes["xml:id"].Value;
                        else if (node.Attributes["id"] != null)
                            name = node.Attributes["id"].Value;
                        if (name != null)
                        {
                            styleCount++;

                            string fontFamily = "Arial";
                            if (node.Attributes["tts:fontFamily"] != null)
                                fontFamily = node.Attributes["tts:fontFamily"].Value;

                            string fontWeight = "normal";
                            if (node.Attributes["tts:fontWeight"] != null)
                                fontWeight = node.Attributes["tts:fontWeight"].Value;

                            string fontStyle = "normal";
                            if (node.Attributes["tts:fontStyle"] != null)
                                fontStyle = node.Attributes["tts:fontStyle"].Value;

                            string color = "#ffffff";
                            if (node.Attributes["tts:color"] != null)
                                color = node.Attributes["tts:color"].Value.Trim();
                            Color c = Color.White;
                            try
                            {
                                if (color.StartsWith("rgb(", StringComparison.Ordinal))
                                {
                                    string[] arr = color.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                    c = Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
                                }
                                else
                                {
                                    c = ColorTranslator.FromHtml(color);
                                }
                            }
                            catch
                            {
                                // ignored
                            }

                            string fontSize = "20";
                            if (node.Attributes["tts:fontSize"] != null)
                                fontSize = node.Attributes["tts:fontSize"].Value.Replace("px", string.Empty).Replace("em", string.Empty);
                            int fSize;
                            if (!int.TryParse(fontSize, out fSize))
                                fSize = 20;

                            string italic = "0";
                            if (fontStyle == "italic")
                                italic = "-1";

                            string bold = "0";
                            if (fontWeight == "bold")
                                bold = "-1";

                            const string styleFormat = "Style: {0},{1},{2},{3},&H0300FFFF,&H00000000,&H02000000,{4},{5},0,0,100,100,0,0,1,2,2,2,10,10,10,1";
                            ttStyles.AppendLine(string.Format(styleFormat, name, fontFamily, fSize, GetSsaColorString(c), bold, italic));
                            styleNames.Add(name);
                        }
                    }

                    if (styleCount > 0)
                    {
                        if (!styleNames.Contains("Default") && !styleNames.Contains("default") && subtitle.Paragraphs.Any(pa => string.IsNullOrEmpty(pa.Extra)))
                        {
                            ttStyles = new StringBuilder(DefaultStyle + Environment.NewLine + ttStyles);
                            foreach (var paragraph in subtitle.Paragraphs)
                            {
                                if (string.IsNullOrEmpty(paragraph.Extra))
                                    paragraph.Extra = "Default";
                            }
                        }
                        sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
                        subtitle.Header = sb.ToString();
                    }
                    else
                    {
                        sb.AppendLine(string.Format(header, title));
                    }

                    // Set correct style on paragraphs
                    foreach (Paragraph p in subtitle.Paragraphs)
                    {
                        if (p.Extra != null && p.Extra.Contains('/'))
                        {
                            p.Extra = p.Extra.Split('/')[0].Trim();
                        }
                    }
                }
                catch
                {
                    sb.AppendLine(string.Format(header, title));
                }
            }

            public static void LoadStylesFromTimedTextTimedDraft2006Oct(Subtitle subtitle, string title, string header, string headerNoStyles, StringBuilder sb)
            {
                foreach (Paragraph p in subtitle.Paragraphs)
                {
                    p.Effect = null;
                }

                try
                {
                    var lines = subtitle.Header.SplitToLines();
                    var sb2 = new StringBuilder();
                    lines.ForEach(line => sb2.AppendLine(line));
                    var xml = new XmlDocument { XmlResolver = null };
                    xml.LoadXml(sb2.ToString().Replace(" & ", " &amp; ").Replace("Q&A", "Q&amp;A").RemoveControlCharactersButWhiteSpace().Trim());
                    subtitle.Header = xml.OuterXml;
                    var nsmgr = new XmlNamespaceManager(xml.NameTable);
                    nsmgr.AddNamespace("ttaf1", xml.DocumentElement.NamespaceURI);
                    int styleCount = 0;
                    var ttStyles = new StringBuilder();
                    var styleNames = new List<string>();
                    foreach (XmlNode node in xml.DocumentElement.SelectNodes("//ttaf1:style", nsmgr))
                    {
                        string name = null;
                        if (node.Attributes["xml:id"] != null)
                            name = node.Attributes["xml:id"].Value;
                        else if (node.Attributes["id"] != null)
                            name = node.Attributes["id"].Value;
                        if (name != null)
                        {
                            styleCount++;

                            string fontFamily = "Arial";
                            if (node.Attributes["tts:fontFamily"] != null)
                                fontFamily = node.Attributes["tts:fontFamily"].Value;

                            string fontWeight = "normal";
                            if (node.Attributes["tts:fontWeight"] != null)
                                fontWeight = node.Attributes["tts:fontWeight"].Value;

                            string fontStyle = "normal";
                            if (node.Attributes["tts:fontStyle"] != null)
                                fontStyle = node.Attributes["tts:fontStyle"].Value;

                            string color = "#ffffff";
                            if (node.Attributes["tts:color"] != null)
                                color = node.Attributes["tts:color"].Value.Trim();
                            Color c = Color.White;
                            try
                            {
                                if (color.StartsWith("rgb(", StringComparison.Ordinal))
                                {
                                    string[] arr = color.Remove(0, 4).TrimEnd(')').Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                                    c = Color.FromArgb(int.Parse(arr[0]), int.Parse(arr[1]), int.Parse(arr[2]));
                                }
                                else
                                {
                                    c = ColorTranslator.FromHtml(color);
                                }
                            }
                            catch
                            {
                                // ignored
                            }

                            string fontSize = "20";
                            if (node.Attributes["tts:fontSize"] != null)
                                fontSize = node.Attributes["tts:fontSize"].Value.Replace("px", string.Empty).Replace("em", string.Empty);
                            int fSize;
                            if (!int.TryParse(fontSize, out fSize))
                                fSize = 20;

                            string italic = "0";
                            if (fontStyle == "italic")
                                italic = "-1";

                            string bold = "0";
                            if (fontWeight == "bold")
                                bold = "-1";

                            const string styleFormat = "Style: {0},{1},{2},{3},&H0300FFFF,&H00000000,&H02000000,{4},{5},0,0,100,100,0,0,1,2,2,2,10,10,10,1";
                            ttStyles.AppendLine(string.Format(styleFormat, name, fontFamily, fSize, GetSsaColorString(c), bold, italic));
                            styleNames.Add(name);
                        }
                    }

                    if (styleCount > 0)
                    {
                        if (!styleNames.Contains("Default") && !styleNames.Contains("default") && subtitle.Paragraphs.Any(pa => string.IsNullOrEmpty(pa.Extra)))
                        {
                            ttStyles = new StringBuilder(DefaultStyle + Environment.NewLine + ttStyles);
                            foreach (var paragraph in subtitle.Paragraphs)
                            {
                                if (string.IsNullOrEmpty(paragraph.Extra))
                                    paragraph.Extra = "Default";
                            }
                        }
                        sb.AppendLine(string.Format(headerNoStyles, title, ttStyles));
                        subtitle.Header = sb.ToString();
                    }
                    else
                    {
                        sb.AppendLine(string.Format(header, title));
                    }

                    // Set correct style on paragraphs
                    foreach (Paragraph p in subtitle.Paragraphs)
                    {
                        if (p.Extra != null && p.Extra.Contains('/'))
                        {
                            p.Extra = p.Extra.Split('/')[0].Trim();
                        }
                    }
                }
                catch
                {
                    sb.AppendLine(string.Format(header, title));
                }
            }

            public static List<string> GetStylesFromHeader(string headerLines)
            {
                var list = new List<string>();

                if (headerLines == null)
                    headerLines = DefaultStyle;

                if (headerLines.Contains("http://www.w3.org/ns/ttml"))
                {
                    var subtitle = new Subtitle { Header = headerLines };
                    LoadStylesFromTimedText10(subtitle, string.Empty, headerLines, HeaderNoStyles, new StringBuilder());
                    headerLines = subtitle.Header;
                }

                foreach (string line in headerLines.SplitToLines())
                {
                    if (line.StartsWith("style:", StringComparison.OrdinalIgnoreCase))
                    {
                        int end = line.IndexOf(',');
                        if (end > 0)
                            list.Add(line.Substring(6, end - 6).Trim());
                    }
                }
                return list;
            }

            public static string FormatText(Paragraph p)
            {
                string text = p.Text.Replace(Environment.NewLine, "\\N");

                if (!text.Contains('<'))
                    return text;

                text = text.Replace("<i>", @"{\i1}");
                text = text.Replace("</i>", @"{\i0}");
                text = text.Replace("</i>", @"{\i}");
                text = text.Replace("<u>", @"{\u1}");
                text = text.Replace("</u>", @"{\u0}");
                text = text.Replace("</u>", @"{\u}");
                text = text.Replace("<b>", @"{\b1}");
                text = text.Replace("</b>", @"{\b0}");
                text = text.Replace("</b>", @"{\b}");
                int count = 0;
                while (text.Contains("<font ") && count < 10)
                {
                    int start = text.IndexOf("<font ", StringComparison.Ordinal);
                    int end = text.IndexOf('>', start);
                    if (end > 0)
                    {
                        string fontTag = text.Substring(start + 5, end - (start + 4));
                        text = text.Remove(start, end - start + 1);
                        int indexOfEndFont = text.IndexOf("</font>", start, StringComparison.Ordinal);
                        if (indexOfEndFont > 0)
                        {
                            text = text.Remove(indexOfEndFont, 7);
                            if (indexOfEndFont < text.Length)
                            {
                                if (fontTag.Contains(" size="))
                                {
                                    text = text.Insert(indexOfEndFont, "{\\fs}");
                                }
                                if (fontTag.Contains(" face="))
                                {
                                    text = text.Insert(indexOfEndFont, "{\\fn}");
                                }
                                if (fontTag.Contains(" color="))
                                {
                                    text = text.Insert(indexOfEndFont, "{\\c}");
                                }
                            }
                        }

                        fontTag = FormatTag(ref text, start, fontTag, "face=\"", "fn", "}");
                        fontTag = FormatTag(ref text, start, fontTag, "face='", "fn", "}");
                        fontTag = FormatTag(ref text, start, fontTag, "face=", "fn", "}");

                        fontTag = FormatTag(ref text, start, fontTag, "size=\"", "fs", "}");
                        fontTag = FormatTag(ref text, start, fontTag, "size='", "fs", "}");
                        fontTag = FormatTag(ref text, start, fontTag, "size=", "fs", "}");

                        fontTag = FormatTag(ref text, start, fontTag, "color=\"", "c&H", "&}");
                        fontTag = FormatTag(ref text, start, fontTag, "color='", "c&H", "&}");
                        FormatTag(ref text, start, fontTag, "color=", "c&H", "&}");
                    }
                    count++;
                }
                text = text.Replace("{\\c}", "@___@@").Replace("}{", string.Empty).Replace("@___@@", "{\\c}").Replace("{\\c}{\\c&", "{\\c&");
                while (text.EndsWith("{\\c}", StringComparison.Ordinal))
                {
                    text = text.Remove(text.Length - 4);
                }
                while (text.Contains("\\fs\\fs", StringComparison.Ordinal))
                {
                    text = text.Replace("\\fs\\fs", "\\fs");
                }
                while (text.Contains("\\fn\\fn", StringComparison.Ordinal))
                {
                    text = text.Replace("\\fn\\fn", "\\fn");
                }
                while (text.Contains("\\c\\c&H", StringComparison.Ordinal))
                {
                    text = text.Replace("\\c\\c&H", "\\c&H");
                }
                return text;
            }

            private static string FormatTag(ref string text, int start, string fontTag, string tag, string ssaTagName, string endSsaTag)
            {
                if (fontTag.Contains(tag))
                {
                    int fontStart = fontTag.IndexOf(tag, StringComparison.Ordinal);

                    int fontEnd = fontTag.IndexOfAny(new[] { '"', '\'' }, fontStart + tag.Length);
                    if (fontEnd < 0)
                        fontEnd = fontTag.IndexOfAny(new[] { ' ', '>' }, fontStart + tag.Length);

                    if (fontEnd > 0)
                    {
                        string subTag = fontTag.Substring(fontStart + tag.Length, fontEnd - (fontStart + tag.Length));
                        if (tag.Contains("color"))
                        {
                            Color c;
                            try
                            {
                                c = ColorTranslator.FromHtml(subTag);
                            }
                            catch
                            {
                                c = Color.White;
                            }
                            subTag = (c.B.ToString("X2") + c.G.ToString("X2") + c.R.ToString("X2")).ToLowerInvariant(); // use bbggrr
                        }
                        fontTag = fontTag.Remove(fontStart, fontEnd - fontStart + 1);
                        if (start < text.Length)
                            text = text.Insert(start, @"{\" + ssaTagName + subTag + endSsaTag);
                    }
                }
                return fontTag;
            }

            public static string GetFormattedText(string text)
            {
                text = text.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine);

                var tooComplex = ContainsUnsupportedTags(text);

                if (!tooComplex)
                {
                    for (int i = 0; i < 10; i++) // just look ten times...
                    {
                        bool italic;
                        if (text.Contains(@"{\fn"))
                        {
                            int start = text.IndexOf(@"{\fn", StringComparison.Ordinal);
                            int end = text.IndexOf('}', start);
                            if (end > 0 && !text.Substring(start).StartsWith("{\\fn}", StringComparison.Ordinal))
                            {
                                string fontName = text.Substring(start + 4, end - (start + 4));
                                string extraTags = string.Empty;
                                string unknownTags;
                                CheckAndAddSubTags(ref fontName, ref extraTags, out unknownTags, out italic);
                                text = text.Remove(start, end - start + 1);
                                if (italic)
                                    text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">" + unknownTags + "<i>");
                                else
                                    text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">" + unknownTags);

                                int indexOfEndTag = text.IndexOf("{\\fn}", start, StringComparison.Ordinal);
                                if (indexOfEndTag > 0)
                                {
                                    text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
                                }
                                else
                                {
                                    int indexOfNextTag1 = text.IndexOf("{\\fn", start, StringComparison.Ordinal);
                                    int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
                                    if (indexOfNextTag1 > 0)
                                    {
                                        text = text.Insert(indexOfNextTag1, "</font>");
                                    }
                                    else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
                                    {
                                        text = text.Insert(indexOfNextTag2, "</font>");
                                    }
                                    else
                                    {
                                        text += "</font>";
                                    }
                                }
                            }
                        }

                        if (text.Contains(@"{\fs"))
                        {
                            int start = text.IndexOf(@"{\fs", StringComparison.Ordinal);
                            int end = text.IndexOf('}', start);
                            if (end > 0 && !text.Substring(start).StartsWith("{\\fs}", StringComparison.Ordinal))
                            {
                                string fontSize = text.Substring(start + 4, end - (start + 4));
                                string extraTags = string.Empty;
                                string unknownTags;
                                CheckAndAddSubTags(ref fontSize, ref extraTags, out unknownTags, out italic);
                                if (Utilities.IsInteger(fontSize))
                                {
                                    text = text.Remove(start, end - start + 1);
                                    if (italic)
                                        text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">" + unknownTags + "<i>");
                                    else
                                        text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">" + unknownTags);

                                    int indexOfEndTag = text.IndexOf("{\\fs}", start, StringComparison.Ordinal);
                                    if (indexOfEndTag > 0)
                                    {
                                        text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
                                    }
                                    else
                                    {
                                        int indexOfNextTag1 = text.IndexOf("{\\fs", start, StringComparison.Ordinal);
                                        int indexOfNextTag2 = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
                                        if (indexOfNextTag1 > 0)
                                        {
                                            text = text.Insert(indexOfNextTag1, "</font>");
                                        }
                                        else if (indexOfNextTag2 > 0 && text.IndexOf("{\\", start, StringComparison.Ordinal) >= indexOfNextTag2)
                                        {
                                            text = text.Insert(indexOfNextTag2, "</font>");
                                        }
                                        else
                                        {
                                            text += "</font>";
                                        }
                                    }
                                }
                            }
                        }

                        if (text.Contains(@"{\c"))
                        {
                            int start = text.IndexOf(@"{\c", StringComparison.Ordinal);
                            int end = text.IndexOf('}', start);
                            if (end > 0 && !text.Substring(start).StartsWith("{\\c}", StringComparison.Ordinal) && !text.Substring(start).StartsWith("{\\clip", StringComparison.Ordinal))
                            {
                                string color = text.Substring(start + 4, end - (start + 4));
                                string extraTags = string.Empty;
                                string unknownTags;
                                CheckAndAddSubTags(ref color, ref extraTags, out unknownTags, out italic);

                                color = color.RemoveChar('&').TrimStart('H');
                                color = color.PadLeft(6, '0');

                                // switch to rrggbb from bbggrr
                                color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
                                color = color.ToLower();

                                text = text.Remove(start, end - start + 1);
                                if (italic)
                                    text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">" + unknownTags + "<i>");
                                else
                                    text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">" + unknownTags);
                                int indexOfEndTag = text.IndexOf("{\\c}", start, StringComparison.Ordinal);
                                int indexOfNextColorTag = text.IndexOf("{\\c&", start, StringComparison.Ordinal);
                                if (indexOfNextColorTag > 0 && (indexOfNextColorTag < indexOfEndTag || indexOfEndTag == -1))
                                    text = text.Insert(indexOfNextColorTag, "</font>");
                                else if (indexOfEndTag > 0)
                                    text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
                                else
                                    text += "</font>";
                            }
                        }

                        if (text.Contains(@"{\1c")) // "1" specifices primary color
                        {
                            int start = text.IndexOf(@"{\1c", StringComparison.Ordinal);
                            int end = text.IndexOf('}', start);
                            if (end > 0 && !text.Substring(start).StartsWith("{\\1c}", StringComparison.Ordinal))
                            {
                                string color = text.Substring(start + 5, end - (start + 5));
                                string extraTags = string.Empty;
                                string unknownTags;
                                CheckAndAddSubTags(ref color, ref extraTags, out unknownTags, out italic);

                                color = color.RemoveChar('&').TrimStart('H');
                                color = color.PadLeft(6, '0');

                                // switch to rrggbb from bbggrr
                                color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
                                color = color.ToLower();

                                text = text.Remove(start, end - start + 1);
                                if (italic)
                                    text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">" + unknownTags + "<i>");
                                else
                                    text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">" + unknownTags);
                                text += "</font>";
                            }
                        }
                    }
                }

                text = text.Replace(@"{\i1}", "<i>");
                text = text.Replace(@"{\i0}", "</i>");
                text = text.Replace(@"{\i}", "</i>");
                if (Utilities.CountTagInText(text, "<i>") > Utilities.CountTagInText(text, "</i>"))
                    text += "</i>";

                text = text.Replace(@"{\u1}", "<u>");
                text = text.Replace(@"{\u0}", "</u>");
                text = text.Replace(@"{\u}", "</u>");
                if (Utilities.CountTagInText(text, "<u>") > Utilities.CountTagInText(text, "</u>"))
                    text += "</u>";

                text = text.Replace(@"{\b1}", "<b>");
                text = text.Replace(@"{\b0}", "</b>");
                text = text.Replace(@"{\b}", "</b>");
                if (Utilities.CountTagInText(text, "<b>") > Utilities.CountTagInText(text, "</b>"))
                    text += "</b>";

                return text;
            }

            private static bool ContainsUnsupportedTags(string text)
            {
                if (string.IsNullOrEmpty(text) || !text.Contains("{\\", StringComparison.OrdinalIgnoreCase))
                    return false;

                var unsupportedTags = new List<string>
                {
                    "\\alpha",
                    "\\be0",
                    "\\be1",
                    "\\bord",
                    "\\blur",
                    "\\clip",
                    "\\fad",
                    "\\fa",
                    "\\fade",
                    "\\fscx",
                    "\\fscy",
                    "\\fr",
                    "\\iclip",
                    "\\k",
                    "\\K",
                    "\\kf",
                    "\\ko",
                    "\\move",
                    "\\org",
                    "\\p",
                    "\\pos",
                    "\\s0",
                    "\\s1",
                    "\\t(",
                    "\\xbord",
                    "\\ybord",
                    "\\xshad",
                    "\\yshad"
                };

                foreach (var unsupportedTag in unsupportedTags)
                {
                    if (text.Contains(unsupportedTag, StringComparison.Ordinal))
                        return true;
                }

                return false;
            }

            private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out string unknownTags, out bool italic)
            {
                italic = false;
                unknownTags = string.Empty;
                int indexOfSPlit = tagName.IndexOf('\\');
                if (indexOfSPlit > 0)
                {
                    string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
                    tagName = tagName.Remove(indexOfSPlit);

                    for (int i = 0; i < 10; i++)
                    {
                        if (rest.StartsWith("fs", StringComparison.Ordinal) && rest.Length > 2)
                        {
                            indexOfSPlit = rest.IndexOf('\\');
                            string fontSize = rest;
                            if (indexOfSPlit > 0)
                            {
                                fontSize = rest.Substring(0, indexOfSPlit);
                                rest = rest.Substring(indexOfSPlit).TrimStart('\\');
                            }
                            else
                            {
                                rest = string.Empty;
                            }
                            extraTags += " size=\"" + fontSize.Substring(2) + "\"";
                        }
                        else if (rest.StartsWith("fn", StringComparison.Ordinal) && rest.Length > 2)
                        {
                            indexOfSPlit = rest.IndexOf('\\');
                            string fontName = rest;
                            if (indexOfSPlit > 0)
                            {
                                fontName = rest.Substring(0, indexOfSPlit);
                                rest = rest.Substring(indexOfSPlit).TrimStart('\\');
                            }
                            else
                            {
                                rest = string.Empty;
                            }
                            extraTags += " face=\"" + fontName.Substring(2) + "\"";
                        }
                        else if (rest.StartsWith('c') && rest.Length > 2)
                        {
                            indexOfSPlit = rest.IndexOf('\\');
                            string fontColor = rest;
                            if (indexOfSPlit > 0)
                            {
                                fontColor = rest.Substring(0, indexOfSPlit);
                                rest = rest.Substring(indexOfSPlit).TrimStart('\\');
                            }
                            else
                            {
                                rest = string.Empty;
                            }

                            string color = fontColor.Substring(2);
                            color = color.RemoveChar('&').TrimStart('H');
                            color = color.PadLeft(6, '0');
                            // switch to rrggbb from bbggrr
                            color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
                            color = color.ToLower();

                            extraTags += " color=\"" + color + "\"";
                        }
                        else if (rest.StartsWith("i1", StringComparison.Ordinal) && rest.Length > 1)
                        {
                            indexOfSPlit = rest.IndexOf('\\');
                            italic = true;
                            if (indexOfSPlit > 0)
                            {
                                rest = rest.Substring(indexOfSPlit).TrimStart('\\');
                            }
                            else
                            {
                                rest = string.Empty;
                            }
                        }
                        else if (rest.Length > 0 && rest.Contains("\\"))
                        {
                            indexOfSPlit = rest.IndexOf('\\');
                            var unknowntag = rest.Substring(0, indexOfSPlit);
                            unknownTags += "\\" + unknowntag;
                            rest = rest.Substring(indexOfSPlit).TrimStart('\\');
                        }
                        else if (!string.IsNullOrEmpty(rest))
                        {
                            unknownTags += "\\" + rest;
                            rest = string.Empty;
                        }
                    }
                }
                if (!string.IsNullOrEmpty(unknownTags))
                    unknownTags = "{" + unknownTags + "}";
            }

            public override void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
            {
                _errorCount = 0;
                Errors = null;
                bool eventsStarted = false;
                bool fontsStarted = false;
                bool graphicsStarted = false;
                subtitle.Paragraphs.Clear();

                // Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text
                int indexLayer = 0;
                int indexStart = 1;
                int indexEnd = 2;
                int indexStyle = 3;
                int indexActor = 4;
                int indexName = -1; // convert "Name" to "Actor" (if no "Actor") - "Name" is from SSA ?
                int indexMarginL = 5;
                int indexMarginR = 6;
                int indexMarginV = 7;
                int indexEffect = 8;
                int indexText = 9;
                var errors = new StringBuilder();
                int lineNumber = 0;

                var header = new StringBuilder();
                var footer = new StringBuilder();
                foreach (string line in lines)
                {
                    lineNumber++;
                    if (!eventsStarted && !fontsStarted && !graphicsStarted &&
                        !line.Trim().Equals("[fonts]", StringComparison.InvariantCultureIgnoreCase) &&
                        !line.Trim().Equals("[graphics]", StringComparison.InvariantCultureIgnoreCase))
                        header.AppendLine(line);

                    if (string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith(';'))
                    {
                        // skip empty and comment lines
                    }
                    else if (line.TrimStart().StartsWith("dialog:", StringComparison.OrdinalIgnoreCase) || line.TrimStart().StartsWith("dialogue:", StringComparison.OrdinalIgnoreCase)) // fix faulty font tags...
                    {
                        eventsStarted = true;
                        fontsStarted = false;
                        graphicsStarted = false;
                    }

                    if (line.Trim().Equals("[events]", StringComparison.OrdinalIgnoreCase))
                    {
                        if (header.ToString().IndexOf(Environment.NewLine + "[events]", StringComparison.OrdinalIgnoreCase) < 0)
                        {
                            var h = header.ToString().TrimEnd();
                            header.Clear();
                            header.AppendLine(h);
                            header.AppendLine();
                            header.AppendLine("[Events]");
                        }
                        eventsStarted = true;
                        fontsStarted = false;
                        graphicsStarted = false;
                    }
                    else if (line.Trim().Equals("[fonts]", StringComparison.OrdinalIgnoreCase))
                    {
                        eventsStarted = false;
                        fontsStarted = true;
                        graphicsStarted = false;
                        footer.AppendLine();
                        footer.AppendLine("[Fonts]");
                    }
                    else if (line.Trim().Equals("[graphics]", StringComparison.OrdinalIgnoreCase))
                    {
                        eventsStarted = false;
                        fontsStarted = false;
                        graphicsStarted = true;
                        footer.AppendLine();
                        footer.AppendLine("[Graphics]");
                    }
                    else if (fontsStarted)
                    {
                        footer.AppendLine(line);
                    }
                    else if (graphicsStarted)
                    {
                        footer.AppendLine(line);
                    }
                    else if (eventsStarted)
                    {
                        string s = line.Trim().ToLower();
                        if (line.Length > 10 && s.StartsWith("format:", StringComparison.Ordinal))
                        {
                            indexLayer = -1;
                            indexStart = -1;
                            indexEnd = -1;
                            indexStyle = -1;
                            indexActor = -1;
                            indexName = -1;
                            indexMarginL = -1;
                            indexMarginR = -1;
                            indexMarginV = -1;
                            indexEffect = -1;
                            indexText = -1;

                            var format = s.Substring(8).Split(',');
                            for (int i = 0; i < format.Length; i++)
                            {
                                var formatTrimmed = format[i].Trim();
                                if (formatTrimmed.Equals("start", StringComparison.Ordinal))
                                    indexStart = i;
                                else if (formatTrimmed.Equals("end", StringComparison.Ordinal))
                                    indexEnd = i;
                                else if (formatTrimmed.Equals("text", StringComparison.Ordinal))
                                    indexText = i;
                                else if (formatTrimmed.Equals("style", StringComparison.Ordinal))
                                    indexStyle = i;
                                else if (formatTrimmed.Equals("actor", StringComparison.Ordinal))
                                    indexActor = i;
                                else if (formatTrimmed.Equals("name", StringComparison.Ordinal))
                                    indexName = i;
                                else if (formatTrimmed.Equals("marginl", StringComparison.Ordinal))
                                    indexMarginL = i;
                                else if (formatTrimmed.Equals("marginr", StringComparison.Ordinal))
                                    indexMarginR = i;
                                else if (formatTrimmed.Equals("marginv", StringComparison.Ordinal))
                                    indexMarginV = i;
                                else if (formatTrimmed.Equals("effect", StringComparison.Ordinal))
                                    indexEffect = i;
                                else if (formatTrimmed.Equals("layer", StringComparison.Ordinal))
                                    indexLayer = i;
                            }
                        }
                        else if (!string.IsNullOrEmpty(s))
                        {
                            var text = string.Empty;
                            var start = string.Empty;
                            var end = string.Empty;
                            var style = string.Empty;
                            var actor = string.Empty;
                            var marginL = string.Empty;
                            var marginR = string.Empty;
                            var marginV = string.Empty;
                            var effect = string.Empty;
                            var layer = 0;

                            string[] splittedLine;
                            if (s.StartsWith("dialog:", StringComparison.Ordinal))
                                splittedLine = line.Remove(0, 7).Split(',');
                            else if (s.StartsWith("dialogue:", StringComparison.Ordinal))
                                splittedLine = line.Remove(0, 9).Split(',');
                            else
                                splittedLine = line.Split(',');

                            for (int i = 0; i < splittedLine.Length; i++)
                            {
                                if (i == indexStart)
                                    start = splittedLine[i].Trim();
                                else if (i == indexEnd)
                                    end = splittedLine[i].Trim();
                                else if (i == indexStyle)
                                    style = splittedLine[i].Trim();
                                else if (i == indexActor)
                                    actor = splittedLine[i].Trim();
                                else if (i == indexName && indexActor == -1)
                                    actor = splittedLine[i].Trim();
                                else if (i == indexMarginL)
                                    marginL = splittedLine[i].Trim();
                                else if (i == indexMarginR)
                                    marginR = splittedLine[i].Trim();
                                else if (i == indexMarginV)
                                    marginV = splittedLine[i].Trim();
                                else if (i == indexEffect)
                                    effect = splittedLine[i].Trim();
                                else if (i == indexLayer)
                                    int.TryParse(splittedLine[i].Replace("Comment:", string.Empty).Trim(), out layer);
                                else if (i == indexText)
                                    text = splittedLine[i];
                                else if (i > indexText)
                                    text += "," + splittedLine[i];
                            }

                            try
                            {
                                var p = new Paragraph
                                {
                                    StartTime = GetTimeCodeFromString(start),
                                    EndTime = GetTimeCodeFromString(end),
                                    Text = GetFormattedText(text)
                                };

                                if (!string.IsNullOrEmpty(style))
                                    p.Extra = style;
                                if (!string.IsNullOrEmpty(actor))
                                    p.Actor = actor;
                                if (!string.IsNullOrEmpty(marginL))
                                    p.MarginL = marginL;
                                if (!string.IsNullOrEmpty(marginR))
                                    p.MarginR = marginR;
                                if (!string.IsNullOrEmpty(marginV))
                                    p.MarginV = marginV;
                                if (!string.IsNullOrEmpty(effect))
                                    p.Effect = effect;
                                p.Layer = layer;
                                p.IsComment = s.StartsWith("comment:", StringComparison.Ordinal);
                                subtitle.Paragraphs.Add(p);
                            }
                            catch
                            {
                                _errorCount++;
                                if (errors.Length < 2000)
                                    errors.AppendLine(string.Format(Configuration.Settings.Language.Main.LineNumberXErrorReadingTimeCodeFromSourceLineY, lineNumber, line));
                            }
                        }
                    }
                }
                if (header.Length > 0)
                    subtitle.Header = header.ToString();
                if (footer.Length > 0)
                    subtitle.Footer = footer.ToString().Trim();
                subtitle.Renumber();
                Errors = errors.ToString();
            }

            private static TimeCode GetTimeCodeFromString(string time)
            {
                // h:mm:ss.cc
                string[] timeCode = time.Split(':', '.');
                return new TimeCode(int.Parse(timeCode[0]),
                    int.Parse(timeCode[1]),
                    int.Parse(timeCode[2]),
                    int.Parse(timeCode[3]) * 10);
            }

            public override void RemoveNativeFormatting(Subtitle subtitle, SubtitleFormat newFormat)
            {
                if (newFormat != null && newFormat.Name == SubStationAlpha.NameOfFormat)
                {
                    foreach (Paragraph p in subtitle.Paragraphs)
                    {
                        string s = p.Text;
                        if (s.Contains('{') && s.Contains('}'))
                        {
                            s = s.Replace(@"\u0", string.Empty);
                            s = s.Replace(@"\u1", string.Empty);
                            s = s.Replace(@"\s0", string.Empty);
                            s = s.Replace(@"\s1", string.Empty);
                            s = s.Replace(@"\be0", string.Empty);
                            s = s.Replace(@"\be1", string.Empty);

                            s = RemoveTag(s, "shad");
                            s = RemoveTag(s, "fsc");
                            s = RemoveTag(s, "fsp");
                            s = RemoveTag(s, "fr");

                            s = RemoveTag(s, "t(");
                            s = RemoveTag(s, "move(");
                            s = RemoveTag(s, "Position(");
                            s = RemoveTag(s, "org(");
                            s = RemoveTag(s, "fade(");
                            s = RemoveTag(s, "fad(");
                            s = RemoveTag(s, "clip(");
                            s = RemoveTag(s, "iclip(");
                            s = RemoveTag(s, "pbo(");
                            s = RemoveTag(s, "bord");
                            s = RemoveTag(s, "pos");

                            // TODO: Alignment tags

                            s = s.Replace("{}", string.Empty);

                            p.Text = s;
                        }
                    }
                }
                else
                {
                    foreach (Paragraph p in subtitle.Paragraphs)
                    {
                        int indexOfBegin = p.Text.IndexOf('{');
                        string pre = string.Empty;
                        while (indexOfBegin >= 0 && p.Text.IndexOf('}') > indexOfBegin)
                        {
                            string s = p.Text.Substring(indexOfBegin);
                            if (s.StartsWith("{\\an1}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an2}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an3}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an4}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an5}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an6}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an7}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an8}", StringComparison.Ordinal) ||
                                s.StartsWith("{\\an9}", StringComparison.Ordinal))
                            {
                                pre = s.Substring(0, 6);
                            }
                            else if (s.StartsWith("{\\an1\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an2\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an3\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an4\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an5\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an6\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an7\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an8\\", StringComparison.Ordinal) ||
                                     s.StartsWith("{\\an9\\", StringComparison.Ordinal))
                            {
                                pre = s.Substring(0, 5) + "}";
                            }
                            int indexOfEnd = p.Text.IndexOf('}');
                            p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);

                            indexOfBegin = p.Text.IndexOf('{');
                        }
                        p.Text = pre + p.Text;
                    }
                }
            }

            private static string RemoveTag(string s, string tag)
            {
                int indexOfTag = s.IndexOf(@"\" + tag, StringComparison.Ordinal);
                if (indexOfTag > 0)
                {
                    var endIndex1 = s.IndexOf('\\', indexOfTag + 1);
                    var endIndex2 = s.IndexOf('}', indexOfTag + 1);
                    endIndex1 = Math.Min(endIndex1, endIndex2);
                    if (endIndex1 > 0)
                        s = s.Remove(indexOfTag, endIndex1 - indexOfTag);
                }
                return s;
            }

            /// <summary>
            /// BGR color like this: &amp;HBBGGRR&amp; (where BB, GG, and RR are hex values in uppercase)
            /// </summary>
            /// <param name="f">Input string</param>
            /// <param name="defaultColor">Default color</param>
            /// <returns>Input string as color, or default color if problems</returns>
            public static Color GetSsaColor(string f, Color defaultColor)
            {
                //Red = &H0000FF&
                //Green = &H00FF00&
                //Blue = &HFF0000&
                //White = &HFFFFFF&
                //Black = &H000000&
                string s = f.Trim().Trim('&');

                if (s.StartsWith('h') && s.Length < 7)
                {
                    while (s.Length < 7)
                        s = s.Insert(1, "0");
                }

                if (s.StartsWith('h') && s.Length == 7)
                {
                    s = s.Substring(1);
                    string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
                    try
                    {
                        return ColorTranslator.FromHtml(hexColor);
                    }
                    catch
                    {
                        return defaultColor;
                    }
                }
                if (s.StartsWith('h') && s.Length == 9)
                {
                    int alpha;
                    if (int.TryParse(s.Substring(1, 2), NumberStyles.HexNumber, null, out alpha))
                    {
                        alpha = 255 - alpha; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
                    }
                    else
                    {
                        alpha = 255; // full color
                    }
                    s = s.Substring(3);
                    string hexColor = "#" + s.Substring(4, 2) + s.Substring(2, 2) + s.Substring(0, 2);
                    try
                    {
                        var c = ColorTranslator.FromHtml(hexColor);
                        return Color.FromArgb(alpha, c);
                    }
                    catch
                    {
                        return defaultColor;
                    }
                }
                int number;
                if (int.TryParse(f, out number))
                {
                    Color temp = Color.FromArgb(number);
                    return Color.FromArgb(255, temp.B, temp.G, temp.R);
                }
                return defaultColor;
            }

            public static string GetSsaColorString(Color c)
            {
                return $"&H{255 - c.A:X2}{c.B:X2}{c.G:X2}{c.R:X2}"; // ASS stores alpha in reverse (0=full itentity and 255=fully transparent)
            }

            public static string CheckForErrors(string header)
            {
                if (string.IsNullOrEmpty(header))
                    return string.Empty;

                var sb = new StringBuilder();

                int styleCount = -1;

                int nameIndex = -1;
                int fontNameIndex = -1;
                int fontsizeIndex = -1;
                int primaryColourIndex = -1;
                int secondaryColourIndex = -1;
                int outlineColourIndex = -1;
                int backColourIndex = -1;
                int boldIndex = -1;
                int italicIndex = -1;
                int underlineIndex = -1;
                int outlineIndex = -1;
                int shadowIndex = -1;
                int alignmentIndex = -1;
                int marginLIndex = -1;
                int marginRIndex = -1;
                int marginVIndex = -1;
                int borderStyleIndex = -1;

                foreach (string line in header.SplitToLines())
                {
                    string s = line.Trim().ToLower();
                    if (s.StartsWith("format:", StringComparison.Ordinal))
                    {
                        if (line.Length > 10)
                        {
                            var format = line.Substring(8).ToLower().Split(',');
                            styleCount = format.Length;
                            for (int i = 0; i < format.Length; i++)
                            {
                                string f = format[i].Trim();
                                if (f == "name")
                                    nameIndex = i;
                                else if (f == "fontname")
                                    fontNameIndex = i;
                                else if (f == "fontsize")
                                    fontsizeIndex = i;
                                else if (f == "primarycolour")
                                    primaryColourIndex = i;
                                else if (f == "secondarycolour")
                                    secondaryColourIndex = i;
                                else if (f == "outlinecolour")
                                    outlineColourIndex = i;
                                else if (f == "backcolour")
                                    backColourIndex = i;
                                else if (f == "bold")
                                    boldIndex = i;
                                else if (f == "italic")
                                    italicIndex = i;
                                else if (f == "underline")
                                    underlineIndex = i;
                                else if (f == "outline")
                                    outlineIndex = i;
                                else if (f == "shadow")
                                    shadowIndex = i;
                                else if (f == "alignment")
                                    alignmentIndex = i;
                                else if (f == "marginl")
                                    marginLIndex = i;
                                else if (f == "marginr")
                                    marginRIndex = i;
                                else if (f == "marginv")
                                    marginVIndex = i;
                                else if (f == "borderstyle")
                                    borderStyleIndex = i;
                            }
                        }
                    }
                    else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
                    {
                        if (line.Length > 10)
                        {
                            string rawLine = line;
                            var format = line.Substring(6).Split(',');

                            if (format.Length != styleCount)
                            {
                                sb.AppendLine("Number of expected Style elements do not match number of Format elements: " + rawLine);
                                sb.AppendLine();
                            }
                            else
                            {
                                Color dummyColor = Color.FromArgb(9, 14, 16, 26);
                                for (int i = 0; i < format.Length; i++)
                                {
                                    string f = format[i].Trim().ToLower();
                                    if (i == nameIndex)
                                    {
                                        if (f.Length == 0)
                                        {
                                            sb.AppendLine("'Name' is empty: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == fontNameIndex)
                                    {
                                        if (f.Length == 0)
                                        {
                                            sb.AppendLine("'Fontname' is empty: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == fontsizeIndex)
                                    {
                                        int number;
                                        if (!int.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'Fontsize' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == primaryColourIndex)
                                    {
                                        if (GetSsaColor(f, dummyColor) == dummyColor || f == "&h")
                                        {
                                            sb.AppendLine("'PrimaryColour' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == secondaryColourIndex)
                                    {
                                        if (GetSsaColor(f, dummyColor) == dummyColor)
                                        {
                                            sb.AppendLine("'SecondaryColour' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == outlineColourIndex)
                                    {
                                        if (GetSsaColor(f, dummyColor) == dummyColor)
                                        {
                                            sb.AppendLine("'OutlineColour' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == backColourIndex)
                                    {
                                        if (GetSsaColor(f, dummyColor) == dummyColor)
                                        {
                                            sb.AppendLine("'BackColour' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == boldIndex)
                                    {
                                        if (Utilities.AllLetters.Contains(f))
                                        {
                                            sb.AppendLine("'Bold' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == italicIndex)
                                    {
                                        if (Utilities.AllLetters.Contains(f))
                                        {
                                            sb.AppendLine("'Italic' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == underlineIndex)
                                    {
                                        if (Utilities.AllLetters.Contains(f))
                                        {
                                            sb.AppendLine("'Underline' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == outlineIndex)
                                    {
                                        float number;
                                        if (!float.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'Outline' (width) incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == shadowIndex)
                                    {
                                        float number;
                                        if (!float.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'Shadow' (width) incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == alignmentIndex)
                                    {
                                        if (!"101123456789 ".Contains(f))
                                        {
                                            sb.AppendLine("'Alignment' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == marginLIndex)
                                    {
                                        int number;
                                        if (!int.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'MarginL' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == marginRIndex)
                                    {
                                        int number;
                                        if (!int.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'MarginR' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == marginVIndex)
                                    {
                                        int number;
                                        if (!int.TryParse(f, out number) || f.StartsWith('-'))
                                        {
                                            sb.AppendLine("'MarginV' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                    else if (i == borderStyleIndex)
                                    {
                                        if (f.Length != 0 && !"123".Contains(f))
                                        {
                                            sb.AppendLine("'BorderStyle' incorrect: " + rawLine);
                                            sb.AppendLine();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                return sb.ToString();
            }

            /// <summary>
            /// Add new style to ASS header
            /// </summary>
            /// <returns>Header with new style</returns>
            public static string AddSsaStyle(SsaStyle style, string header)
            {
                if (string.IsNullOrEmpty(header))
                    header = DefaultHeader;

                var sb = new StringBuilder();
                bool stylesStarted = false;
                bool styleAdded = false;
                string styleFormat = "Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding";
                foreach (string line in header.SplitToLines())
                {
                    if (line.Equals("[V4+ Styles]", StringComparison.OrdinalIgnoreCase) || line.Equals("[V4 Styles]", StringComparison.OrdinalIgnoreCase))
                        stylesStarted = true;
                    if (line.StartsWith("format:", StringComparison.OrdinalIgnoreCase))
                        styleFormat = line;
                    if (!line.StartsWith("Style: " + style.Name + ",", StringComparison.Ordinal)) // overwrite existing style
                        sb.AppendLine(line);
                    if (!styleAdded && stylesStarted && line.TrimStart().StartsWith("style:", StringComparison.OrdinalIgnoreCase))
                    {
                        sb.AppendLine(style.ToRawAss(styleFormat));
                        styleAdded = true;
                    }
                }
                return sb.ToString();
            }

            public static SsaStyle GetSsaStyle(string styleName, string header)
            {
                var style = new SsaStyle { Name = styleName };

                int nameIndex = -1;
                int fontNameIndex = -1;
                int fontsizeIndex = -1;
                int primaryColourIndex = -1;
                int secondaryColourIndex = -1;
                int tertiaryColourIndex = -1;
                int outlineColourIndex = -1;
                int backColourIndex = -1;
                int boldIndex = -1;
                int italicIndex = -1;
                int underlineIndex = -1;
                int outlineIndex = -1;
                int shadowIndex = -1;
                int alignmentIndex = -1;
                int marginLIndex = -1;
                int marginRIndex = -1;
                int marginVIndex = -1;
                int borderStyleIndex = -1;

                if (header == null)
                    header = DefaultHeader;

                foreach (string line in header.SplitToLines())
                {
                    string s = line.Trim().ToLower();
                    if (s.StartsWith("format:", StringComparison.Ordinal))
                    {
                        if (line.Length > 10)
                        {
                            var format = line.ToLower().Substring(8).Split(',');
                            for (int i = 0; i < format.Length; i++)
                            {
                                string f = format[i].Trim().ToLower();
                                if (f == "name")
                                    nameIndex = i;
                                else if (f == "fontname")
                                    fontNameIndex = i;
                                else if (f == "fontsize")
                                    fontsizeIndex = i;
                                else if (f == "primarycolour")
                                    primaryColourIndex = i;
                                else if (f == "secondarycolour")
                                    secondaryColourIndex = i;
                                else if (f == "tertiarycolour")
                                    tertiaryColourIndex = i;
                                else if (f == "outlinecolour")
                                    outlineColourIndex = i;
                                else if (f == "backcolour")
                                    backColourIndex = i;
                                else if (f == "bold")
                                    boldIndex = i;
                                else if (f == "italic")
                                    italicIndex = i;
                                else if (f == "underline")
                                    underlineIndex = i;
                                else if (f == "outline")
                                    outlineIndex = i;
                                else if (f == "shadow")
                                    shadowIndex = i;
                                else if (f == "alignment")
                                    alignmentIndex = i;
                                else if (f == "marginl")
                                    marginLIndex = i;
                                else if (f == "marginr")
                                    marginRIndex = i;
                                else if (f == "marginv")
                                    marginVIndex = i;
                                else if (f == "borderstyle")
                                    borderStyleIndex = i;
                            }
                        }
                    }
                    else if (s.RemoveChar(' ').StartsWith("style:", StringComparison.Ordinal))
                    {
                        if (line.Length > 10)
                        {
                            style.RawLine = line;
                            var format = line.Substring(6).Split(',');
                            for (int i = 0; i < format.Length; i++)
                            {
                                string f = format[i].Trim().ToLower();
                                if (i == nameIndex)
                                {
                                    style.Name = format[i].Trim();
                                }
                                else if (i == fontNameIndex)
                                {
                                    style.FontName = f;
                                }
                                else if (i == fontsizeIndex)
                                {
                                    int number;
                                    if (int.TryParse(f, out number))
                                        style.FontSize = number;
                                }
                                else if (i == primaryColourIndex)
                                {
                                    style.Primary = GetSsaColor(f, Color.White);
                                }
                                else if (i == secondaryColourIndex)
                                {
                                    style.Secondary = GetSsaColor(f, Color.Yellow);
                                }
                                else if (i == tertiaryColourIndex)
                                {
                                    style.Tertiary = GetSsaColor(f, Color.Yellow);
                                }
                                else if (i == outlineColourIndex)
                                {
                                    style.Outline = GetSsaColor(f, Color.Black);
                                }
                                else if (i == backColourIndex)
                                {
                                    style.Background = GetSsaColor(f, Color.Black);
                                }
                                else if (i == boldIndex)
                                {
                                    style.Bold = f == "-1" || f == "1";
                                }
                                else if (i == italicIndex)
                                {
                                    style.Italic = f == "-1" || f == "1";
                                }
                                else if (i == underlineIndex)
                                {
                                    style.Underline = f == "-1" || f == "1";
                                }
                                else if (i == outlineIndex)
                                {
                                    decimal number;
                                    if (decimal.TryParse(f, out number))
                                        style.OutlineWidth = number;
                                }
                                else if (i == shadowIndex)
                                {
                                    decimal number;
                                    if (decimal.TryParse(f, out number))
                                        style.ShadowWidth = number;
                                }
                                else if (i == alignmentIndex)
                                {
                                    style.Alignment = f;
                                }
                                else if (i == marginLIndex)
                                {
                                    int number;
                                    if (int.TryParse(f, out number))
                                        style.MarginLeft = number;
                                }
                                else if (i == marginRIndex)
                                {
                                    int number;
                                    if (int.TryParse(f, out number))
                                        style.MarginRight = number;
                                }
                                else if (i == marginVIndex)
                                {
                                    int number;
                                    if (int.TryParse(f, out number))
                                        style.MarginVertical = number;
                                }
                                else if (i == borderStyleIndex)
                                {
                                    style.BorderStyle = f;
                                }
                            }
                        }
                        if (styleName != null && style.Name != null && (styleName.Equals(style.Name, StringComparison.OrdinalIgnoreCase) ||
                                                                        styleName.Equals("*Default", StringComparison.OrdinalIgnoreCase) && style.Name.Equals("Default", StringComparison.OrdinalIgnoreCase)))
                        {
                            style.LoadedFromHeader = true;
                            return style;
                        }
                    }
                }
                return new SsaStyle { Name = styleName };
            }

            public override bool HasStyleSupport => true;
        }
    }

↑ V3022 Expression 'ssaStyle != null' is always true.

↑ V3125 The 'line' object was used after it was verified against null. Check lines: 1028, 1017.

↑ V3095 The 'line' object was used before it was verified against null. Check lines: 1014, 1017.