/// <summary>
        /// Converts raw effect data to html outpout.
        /// </summary>
        /// <param name="effectStr">Effect information-</param>
        /// <param name="baseEffect">Base effect.</param>
        /// <param name="baseAttack">Base attack for the base effect.</param>
        /// <param name="baseEnemy">Base enemy for the base attack.</param>
        /// <returns>Converted data.</returns>
        public static string HandleEffect(string effectStr, Effect baseEffect, Attack baseAttack, Enemy baseEnemy)
        {
            /* What is wanted:
              * Damage: <span>(count*stack*amount)</span> over time (<span>amount</span> per hit).
              * Swiftness: (count*stack*amount) over time (amount per hit).
              * count*stack stability: for amount over time (stack per hit)
              * count*stack might: <span>count*stack</span>% more damage for amount over time (<span>stack</span>% more damage per hit)
              */
              // Note: Listing per hit for might/stability may not make sense if there is only stack.

              // Keep the original string for error logging purposes.
              var original = string.Copy(effectStr);
              // Copy values from the base effect to avoid changing it. If this ever gets refactored feel free to use them directly.
              SubEffectInformation information = new SubEffectInformation();
              information.hitCount = baseEffect.HitCount;
              information.hitLength = baseEffect.HitLength;
              information.hitFrequency = baseEffect.HitFrequency;
              information.variableHitCount = false;
              var split = effectStr.Split('|');
              effectStr = split[0];
              // Some effects get applied randomly. Don't add total damage/effect in those cases. Refactor this if possible.
              var effectChance = "";
              if (split.Length > 1)
            effectChance = split[1];
              if (effectChance.Length > 0)
            information.hitCount = 1;
              if (information.hitCount < 0)
              {
            information.variableHitCount = true;
            information.hitCount = 1;
              }
              // First effect type determines the icon.
              var firstType = "";
              var firstStacks = 0;
              var startIcon = "";
              var index = 0;
              while (true)
              {
            TagData text = TagData.FromString(effectStr, ':', ref index, new[] { ' ', '|', '(' }, new[] { ' ', '|', ')' });
            if (text == null)
              break;
            // Ignore other stuff like enemies.
            if (text.Tag.Contains("="))
              continue;
            if (text.Tag.Length == 0 || text.Data.Length == 0)
            {
              ErrorHandler.ShowWarningMessage("Enemy " + baseEnemy.Name + ": Something wrong with line '" + original + "'. Note: Use '\\:' instead of ':' in the text!");
              effectStr = effectStr.Remove(index, 1);
              continue;
            }
            if (!SubEffect.EffectTypes.ContainsKey(text.Tag.ToLower()))
            {
              ErrorHandler.ShowWarningMessage("Skipping an effect. Effect " + text.Tag + " not recognized.");
              continue;
            }
            var subEffect = SubEffect.EffectTypes[text.Tag.ToLower()];
            var effectType = subEffect.Name;
            // Effect info format varies based on tag (separated by :).
            information = ExtractInformation(information, subEffect, text, baseAttack, baseEnemy);
            var tag = SubEffect.GetTag(effectType);
            if (tag.Length > 0)
              baseEnemy.Tags.Add(tag);
            information = HandleStackingRules(information, subEffect);
            StringBuilder replace = GenerateReplace(information, subEffect, baseAttack);
            var toReplace = text.Tag + ":" + text.Data;
            effectStr = effectStr.Replace(toReplace, replace.ToString());
            index = index - toReplace.Length + replace.Length;
            // Get the first subeffect type to display an icon.
            if (firstType.Equals(""))
            {
              if (information.icon.Equals("-") && effectType == EffectType.Buff)
            startIcon = "Buff";
              else
            startIcon = "<span class=" + Constants.IconClass + " data-src=\"" + information.icon.ToLower() + "\" title=\"" + Helper.ToUpper(information.name.Replace('_', ' ')) + "\">" + Helper.ToUpper(information.name.Replace('_', ' ')) + "</span>";
              firstType = information.name;
              firstStacks = information.stacks;
            }
              }
              return BuildHTML(firstType, startIcon, firstStacks, effectStr, effectChance).ToString();
        }
 private static StringBuilder GenerateReplace(SubEffectInformation effect, SubEffect subEffect, Attack baseAttack)
 {
     // Syntax: <span class="TAGValue">VALUE</span>
       var replace = new StringBuilder();
       var HTMLClass = SubEffect.GetHTMLClass(subEffect.Name);
       //// Put both total and damage per hit.
       if (effect.amount > -1)
       {
     // All amounts need clientside formatting.
     // Add information as data values so it can be recalculated in the browser when enemy level changes.
     replace.Append("<span class=\"").Append(HTMLClass).Append("\" data-effect=\"").Append(effect.name);
     if (effect.name.Equals("confusion"))
       replace.Append("1");
     replace.Append("\" data-amount=\"").Append(effect.totalAmount).Append("\" data-weapon=\"").Append(baseAttack.Weapon).Append("\"></span>");
     replace.Append(" ").Append(effect.suffix);
       }
       if (effect.totalDuration > 0)
       {
     if (effect.amount > -1)
       replace.Append(" over ");
     replace.Append(effect.totalDuration).Append(" second");
     if (effect.totalDuration != 1.0)
       replace.Append("s");
       }
       replace.Append(HitLengthStr(effect.totalLength));
       if (effect.hitCount > 1 && (effect.amount > -1 || effect.duration > 0))
       {
     // Same as above but for a single hit.
     replace.Append("<span class=\"secondary-info\"> (");
     if (effect.amount > -1)
     {
       replace.Append("<span class=\"").Append(HTMLClass).Append("\" data-effect=\"").Append(effect.name);
       if (effect.name.Equals("confusion"))
     replace.Append("1");
       replace.Append("\" data-amount=\"").Append(effect.amount).Append("\"></span>");
       replace.Append(" ").Append(effect.suffix);
     }
     if (effect.duration > 0)
     {
       if (effect.amount > -1)
     replace.Append(" over ");
       replace.Append(effect.duration).Append(" second");
       if (effect.totalDuration != 1.0)
     replace.Append("s");
     }
     replace.Append(" per hit)</span>");
       }
       if (effect.name.Equals("confusion"))
       {
     effect.suffix = "damage per skill usage";
     replace.Append(". ");
     if (effect.amount > -1)
     {
       replace.Append("<span class=\"").Append(HTMLClass).Append("\" data-effect=\"").Append(effect.name).Append("2");
       replace.Append("\" data-amount=\"").Append(effect.totalAmount).Append("\"></span>");
       replace.Append(" ").Append(effect.suffix);
     }
     if (effect.duration > 0)
     {
       if (effect.amount > -1)
     replace.Append(" over ");
       replace.Append(effect.totalLength).Append(" second");
       if (effect.totalLength != 1.0)
     replace.Append("s");
     }
     replace.Append(HitLengthStr(effect.hitLength));
     if (effect.hitCount > 1)
     {
       replace.Append("<span class=\"secondary-info\"> (");
       if (effect.amount > -1)
       {
     replace.Append("<span class=\"").Append(HTMLClass).Append("\" data-effect=\"").Append(effect.name).Append("2");
     if (effect.name.Equals("confusion"))
       replace.Append("1");
     replace.Append("\" data-amount=\"").Append(effect.amount).Append("\"></span>");
     replace.Append(" ").Append(effect.suffix);
       }
       if (effect.duration > 0)
       {
     if (effect.amount > -1)
       replace.Append(" over ");
     replace.Append(effect.duration).Append(" second");
     if (effect.duration != 1.0)
       replace.Append("s");
       }
       replace.Append(" per hit)</span>");
     }
       }
       // Some effects can have variable or unknown hit count. Just add " per hit" in those cases.
       if (effect.variableHitCount)
     replace.Append(" per hit");
       if (effect.hitFrequency > 0.01)
       {
     replace.Append(" every ").Append(effect.hitFrequency).Append(" second");
     if (effect.hitFrequency != 1.0)
       replace.Append("s");
       }
       if (subEffect.Name == EffectType.Buff)
       {
     // Add the buff name because people probably don't recognize all icons.
     replace.Append(" (").Append(effect.buff.Replace('_', ' ')).Append(")");
       }
       return replace;
 }
 private static SubEffectInformation HandleStackingRules(SubEffectInformation effectInformation, SubEffect subEffect)
 {
     if (subEffect.StacksDuration)
       {
     effectInformation.totalAmount = effectInformation.hitCount * effectInformation.amount * effectInformation.stacks;
     effectInformation.totalDuration = effectInformation.hitCount * effectInformation.duration * effectInformation.stacks;
     effectInformation.totalLength = effectInformation.hitLength;
     // 3 different values makes the effect very confusing so just remove the hit length.
     if (effectInformation.totalAmount > 0 && effectInformation.totalDuration > 0)
       effectInformation.totalLength = 0;
     effectInformation.stacks = 0;
       }
       else
       {
     effectInformation.amount = effectInformation.amount * effectInformation.stacks;
     effectInformation.totalAmount = effectInformation.hitCount * effectInformation.amount;
     effectInformation.totalDuration = 0;
     effectInformation.totalLength = effectInformation.hitLength + effectInformation.duration;
     // Without damage don't do "over X seconds". Instead, just add the duration (makes control work properly).
     if (effectInformation.amount == 0)
     {
       effectInformation.totalDuration = effectInformation.duration;
       effectInformation.duration = 0;
     }
     effectInformation.stacks = effectInformation.hitCount * effectInformation.stacks;
       }
       return effectInformation;
 }
 private static SubEffectInformation ExtractInformation(SubEffectInformation effect, SubEffect subEffect, TagData text, Attack baseAttack, Enemy baseEnemy)
 {
     var effectType = subEffect.Name;
       var effectData = text.Data.Split(':');
       effect.name = text.Tag.ToLower();
       effect.amount = -1;
       effect.duration = 0.0;
       effect.stacks = 1;
       effect.buff = "";
       effect.icon = effect.name;
       effect.suffix = "damage";
       if (effectType == EffectType.Damage || effectType == EffectType.DamageFixed || effectType == EffectType.DamagePercent)
       {
     if (effectData[0].Equals("-"))
       effect.amount = baseAttack.Coefficient;
     else if (effectData[0].Equals("?"))
       effect.amount = -1;
     else
       effect.amount = Helper.ParseD(effectData[0]);
     effect.icon = "damage";
     effect.stacks = 1;
       }
       else if (effectType == EffectType.Healing || effectType == EffectType.HealingPercent)
       {
     if (effectData[0].Equals("-"))
       effect.amount = baseAttack.Coefficient;
     else if (effectData[0].Equals("?"))
       effect.amount = -1;
     else
       effect.amount = Helper.ParseD(effectData[0]);
     effect.suffix = "healing";
     effect.icon = "healing";
     effect.stacks = 1;
       }
       if (effectType == EffectType.Boon || effectType == EffectType.Condition || effectType == EffectType.Control)
       {
     baseEnemy.Tags.Add(effect.name);
       }
       if (effectType == EffectType.Agony || effectType == EffectType.Boon || effectType == EffectType.Condition)
       {
     if (effectData[0].Equals("-"))
       effect.duration = baseAttack.Coefficient;
     else if (effectData[0].Equals("?"))
       effect.duration = -1;
     else
       effect.duration = Helper.ParseD(effectData[0]);
     if (effectType == EffectType.Agony || effect.name.Equals("bleeding") || effect.name.Equals("torment") || effect.name.Equals("burning")
       || effect.name.Equals("poison") || effect.name.Equals("confusion") || effect.name.Equals("regeneration") || effect.name.Equals("might"))
       effect.amount = effect.duration;
     if (effectData.Length > 1)
       effect.stacks = Helper.ParseI(effectData[1]);
     if (subEffect.StacksDuration || effectType == EffectType.Boon)
       effect.suffix = "seconds";
     if (effect.name.Equals("regeneration"))
       effect.suffix = "healing";
     if (effect.name.Equals("retaliation"))
       effect.suffix = "damage per hit";
     if (effect.name.Equals("might"))
       effect.suffix = "more damage";
     if (effect.name.Equals("retaliation") || effect.name.Equals("might"))
       effect.amount = 1;
       }
       if (effectType == EffectType.Control)
       {
     if (effectData[0].Equals("?"))
       effect.duration = 0;
     else
       effect.duration = Helper.ParseD(effectData[0]);
     if (effectData.Length > 1)
       effect.stacks = Helper.ParseI(effectData[1]);
       }
       if (effectType == EffectType.Buff)
       {
     effect.buff = effectData[0];
     effect.icon = effect.buff;
     if (effectData.Length > 1 && effectData[1].Length > 0)
       effect.duration = Helper.ParseD(effectData[1]);
     if (effectData.Length > 2 && effectData[2].Length > 0)
       effect.stacks = Helper.ParseI(effectData[2]);
     if (effectData.Length > 3 && effectData[3].Length > 0)
       effect.icon = effectData[3];
     if (effectData.Length > 4)
       subEffect.StacksDuration = Helper.ParseI(effectData[4]) > 0 ? true : false;
       }
       return effect;
 }