private void HandleBiting(ParserContext context, SnifferSession session, AttackStrike strike)
        {
            var text = session.GetReportText(strike);

            if (text == null)
            {
                return;
            }
            var attackerName = strike.KeyValues[SnifferTags.AttackerName];
            var defenderName = strike.KeyValues[SnifferTags.DefenderName];

            var bitePattern = string.Format("{0} bites {1} ", attackerName, defenderName);

            if (text.Contains(bitePattern))
            {
                context.SetBiting(attackerName, defenderName);
            }
            var latchPattern = string.Format("{0} latches on", attackerName);

            if (text.Contains(latchPattern))
            {
                context.SetLatching(attackerName, defenderName);
                if (!context.IsBiting(attackerName, defenderName))
                {
                    context.SetBiting(attackerName, defenderName);
                }
            }
            // TODO - check releases, those they might not actually be attack strikes
        }
        private void LoadSession(SnifferSession session, string filterRegex)
        {
            StrikeTree.Nodes.Clear();
            StrikesNodeDisplayListView.Clear();

            UnitsTree.Nodes.Clear();
            UnitsNodeDisplayListView.Clear();

            TargetsTree.Nodes.Clear();

            reportLogListView.Items.Clear();

            UnitNodes.Clear();
            UnitHyperLinks.Clear();
            StrikeHyperLinks.Clear();
            TreeToSnif.Clear();
            TreeToStrike.Clear();
            StrikeToTree.Clear();
            ReportTextToStrike.Clear();

            TreeNode treeNode    = null;
            var      strikeNodes = new List <TreeNode>();

            foreach (var strike in session.Strikes)
            {
                var strikeReportText = session.GetReportText(strike);
                if (strikeReportText == null)
                {
                    string killHint = strike.KeyValues[SnifferTags.WoundId] == "-1" ? "(Looks like kill)" : "";
                    strikeReportText = string.Format("{0} vs {1} {2}", strike.KeyValues[SnifferTags.AttackerName], strike.KeyValues[SnifferTags.DefenderName], killHint);
                }
                if (Regex.IsMatch(strikeReportText, filterRegex))
                {
                    var attackerUnit = session.Units.First(x => x.Name.Equals(strike.AttackerName));
                    var defenderUnit = session.Units.First(x => x.Name.Equals(strike.DefenderName));

                    int woundCount = 1;
                    var woundNodes = new List <TreeNode>();
                    foreach (var wound in strike.Wounds)
                    {
                        var wbpNodes = new List <TreeNode>();
                        foreach (var wbp in wound.Parts)
                        {
                            var layerNodes = new List <TreeNode>();
                            foreach (var tl in wbp.Layers)
                            {
                                treeNode = new TreeNode(tl.KeyValues[SnifferTags.TissueLayerName]);
                                layerNodes.Add(treeNode);
                                TreeToSnif[treeNode] = tl;
                            }

                            var wbpNodeName = wbp.KeyValues[SnifferTags.BodyPartNameSingular];
                            if (wbp.KeyValues.ContainsKey(SnifferTags.LayerName))
                            {
                                wbpNodeName = string.Format("{0} - {1}", wbpNodeName, wbp.KeyValues[SnifferTags.LayerName]);
                            }
                            treeNode = new TreeNode(wbpNodeName, layerNodes.ToArray());
                            wbpNodes.Add(treeNode);
                            TreeToSnif[treeNode] = wbp;
                        }
                        treeNode = new TreeNode(string.Format("Wound #{0}", woundCount++), wbpNodes.ToArray());
                        woundNodes.Add(treeNode);
                        TreeToSnif[treeNode] = wound;
                    }

                    var attackerHyperNode = new TreeNode(string.Format("Attacker: {0}", attackerUnit.Name));
                    UnitHyperLinks[attackerHyperNode] = attackerUnit;
                    TreeToSnif[attackerHyperNode]     = attackerUnit;
                    var defenderHyperNode = new TreeNode(string.Format("Defender: {0}", defenderUnit.Name));
                    UnitHyperLinks[defenderHyperNode] = defenderUnit;
                    TreeToSnif[defenderHyperNode]     = defenderUnit;

                    var strikeNode = new TreeNode(strikeReportText,
                                                  new TreeNode[] {
                        attackerHyperNode,
                        defenderHyperNode
                    }.Concat(woundNodes).ToArray());

                    strikeNodes.Add(strikeNode);
                    TreeToSnif[strikeNode] = strike;

                    TreeToStrike[strikeNode] = strike;
                    StrikeToTree[strike]     = strikeNode;

                    if (strike.ReportTextIndex != -1)
                    {
                        ReportTextToStrike[strike.ReportTextIndex] = strike;
                    }

                    StrikeTree.Nodes.Add(strikeNode);

                    // Update the targets tree
                    if (strike.Target != null)
                    {
                        var targetNode = TargetsTree.Nodes.Find(strike.Target, false).SingleOrDefault();
                        if (targetNode == null)
                        {
                            targetNode = TargetsTree.Nodes.Add(strike.Target, strike.Target);
                        }

                        var targetStrikeNode = new TreeNode(strikeReportText);
                        StrikeHyperLinks[targetStrikeNode] = strike;

                        targetNode.Nodes.Add(targetStrikeNode);
                    }
                }
            }

            int reportTextIndex = 0;

            foreach (var reportText in session.ReportTexts.Where(l => Regex.IsMatch(l, filterRegex)))
            {
                var lvi = new ListViewItem(reportText);
                if (session.Strikes.Any(strike => strike.ReportTextIndex == reportTextIndex))
                {
                    lvi.BackColor = Color.LightGreen;
                }
                else
                {
                    if (Parser.IsCombatText(reportText))
                    {
                        lvi.BackColor   = Color.LightPink;
                        lvi.ToolTipText = "Looks like a missing attack event went with this report text.";
                    }
                }
                reportLogListView.Items.Add(lvi);
                reportTextIndex++;
            }

            foreach (var unit in session.Units.OrderBy(u => u.Id))
            {
                var partNodes = new List <TreeNode>();
                foreach (var bp in unit.Body.BodyParts)
                {
                    var layerNodes = new List <TreeNode>();
                    foreach (var layer in bp.Layers)
                    {
                        var layerNode = new TreeNode(layer.Name);
                        TreeToSnif[layerNode] = layer;
                        layerNodes.Add(layerNode);
                    }

                    var partNode = new TreeNode(bp.Name, layerNodes.ToArray());
                    TreeToSnif[partNode] = bp;
                    partNodes.Add(partNode);
                }
                var bodyPartsNode = new TreeNode("Body Parts", partNodes.ToArray());

                var bpAttackNodes = new List <TreeNode>();
                foreach (var bpAttack in unit.BodyPartAttacks)
                {
                    var bpAttackNode = new TreeNode(bpAttack.Name);
                    TreeToSnif[bpAttackNode] = bpAttack;
                    bpAttackNodes.Add(bpAttackNode);
                }
                var bpAttacksNode = new TreeNode("Body Part Attacks", bpAttackNodes.ToArray());

                var armorNodes = new List <TreeNode>();
                foreach (var armor in unit.Armors)
                {
                    var armorNode = new TreeNode(armor.Name);
                    TreeToSnif[armorNode] = armor;
                    armorNodes.Add(armorNode);
                }
                var armorsNode = new TreeNode("Armor", armorNodes.ToArray());

                var weaponNodes = new List <TreeNode>();
                foreach (var weapon in unit.Weapons)
                {
                    var attackNodes = new List <TreeNode>();
                    foreach (var attack in weapon.Attacks)
                    {
                        var attackNode = new TreeNode(attack.Name);
                        TreeToSnif[attackNode] = attack;
                        attackNodes.Add(attackNode);
                    }
                    var weaponNode = new TreeNode(weapon.Name, attackNodes.ToArray());
                    TreeToSnif[weaponNode] = weapon;
                    weaponNodes.Add(weaponNode);
                }
                var weaponsNode = new TreeNode("Weapons", weaponNodes.ToArray());

                var bodyNode = new TreeNode("Body", new TreeNode[] {
                    bodyPartsNode,
                    bpAttacksNode,
                });

                TreeToSnif[bodyNode] = unit.Body;

                var unitNode = new TreeNode(unit.Name, new TreeNode[] {
                    bodyNode,
                    armorsNode,
                    weaponsNode,
                });

                TreeToSnif[unitNode] = unit;
                UnitNodes[unit]      = unitNode;

                UnitsTree.Nodes.Add(unitNode);
            }
        }