public void Register(ParsedLine line)
        {
            bool add = false;
            OwnerInfo OwnerInfo = null;

            // Record owner of all entities we see.
            if (line.srcEntityType == EntityType.Entity)
            {
                if (entityPlayerCache.TryGetValue(line.srcInt, out OwnerInfo))
                {
                    if (OwnerInfo.ownerInt != line.ownInt)
                    {
                        // Pet Owner changed...  Not sure if possible... but just in case.
                        entityPlayerCache.Remove(OwnerInfo.petInt);
                        add = true;
                    }
                }
                else
                {
                    // Multiple entities may be owned by same owner.
                    add = true;
                }

                if (add)
                {
                    OwnerInfo = new OwnerInfo();
                    OwnerInfo.ownerDsp = line.ownDsp;
                    OwnerInfo.ownerInt = line.ownInt;
                    OwnerInfo.ownerEntityType = line.ownEntityType;
                    OwnerInfo.petDsp = line.srcDsp;
                    OwnerInfo.petInt = line.srcInt;

                    entityPlayerCache.Add(line.srcInt, OwnerInfo);
                }
            }
        }
        private void ProcessSourceNames(ParsedLine line)
        {
            switch (line.srcEntityType)
            {
                case EntityType.Player:
                    {
                        line.encAttackerName = line.srcDsp;
                        line.unitAttackerName = line.srcDsp;
                        break;
                    }

                case EntityType.Pet:
                    {
                        OwnerInfo owner = petOwnerRegistery.Resolve(line.srcInt);

                        if (owner != null)
                        {

                            // Use the pet owner name for encounter name and filtering.
                            line.encAttackerName = owner.ownerDsp;

                            // Pet name:
                            line.unitAttackerName = line.srcDsp + " [" + owner.ownerDsp + "'s Pet]";
                            if (this.checkBox_mergePets.Checked)
                            {
                                line.unitAttackerName = owner.ownerDsp;
                            }
                        }
                        else
                        {
                            // Pet with unknown owner.
                            // Register it under UNKNOWN until it resolves.
                            line.encAttackerName = unk;
                            line.unitAttackerName = unk;
                        }
                        break;
                    }
                case EntityType.Entity:
                    {
                        OwnerInfo owner = entityOwnerRegistery.Resolve(line.srcInt);
                        if (owner != null)
                        {
                            if (owner.ownerEntityType == EntityType.Creature)
                            {
                                line.encAttackerName = owner.ownerDsp;

                                if (checkBox_mergeNPC.Checked)
                                {
                                    // Merge all NPCs to a single name.
                                    line.unitAttackerName = owner.ownerDsp;
                                }
                                else
                                {
                                    // Separate each NPC with its unique creature ID added.
                                    String creatureId = owner.ownerInt.Split()[0].Substring(2);
                                    line.unitAttackerName = owner.ownerDsp + " [" + creatureId + "]";
                                }

                            }
                            else
                            {
                                line.encAttackerName = owner.ownerDsp;
                                line.unitAttackerName = owner.ownerDsp;
                            }
                        }
                        else
                        {
                            line.encAttackerName = line.srcDsp;
                            line.unitAttackerName = line.srcDsp;
                        }
                        break;
                    }
                case EntityType.Creature:
                    {
                        line.encAttackerName = line.srcDsp;

                        if (checkBox_mergeNPC.Checked)
                        {
                            // Merge all NPCs to a single name.
                            line.unitAttackerName = line.srcDsp;
                        }
                        else
                        {
                            // Separate each NPC with its unique creature ID added.
                            String creatureId = line.srcInt.Split()[0].Substring(2);
                            line.unitAttackerName = line.srcDsp + " [" + creatureId + "]";
                        }

                        break;
                    }

                // case ParsedLine.EntityType.Unknown:
                default:
                    {
                        // Use the defaults.
                        line.encAttackerName = line.srcDsp;
                        line.unitAttackerName = line.srcDsp;
                        break;
                    }
            }
        }
        private void ProcessTargetNames(ParsedLine line)
        {
            switch (line.tgtEntityType)
            {
                case EntityType.Player:
                    {
                        line.encTargetName = line.tgtDsp;
                        line.unitTargetName = line.tgtDsp;
                        break;
                    }

                case EntityType.Pet:
                    {
                        line.tgtOwnerInfo = petOwnerRegistery.Resolve(line.tgtInt);

                        if (line.tgtOwnerInfo != null)
                        {

                            // Use the pet owner name for encounter name and filtering.
                            line.encTargetName = line.tgtOwnerInfo.ownerDsp;

                            // Pet name:
                            line.unitTargetName = line.tgtDsp + " [" + line.tgtOwnerInfo.ownerDsp + "'s Pet]";
                            if (this.checkBox_mergePets.Checked)
                            {
                                line.unitTargetName = line.tgtOwnerInfo.ownerDsp;
                            }
                        }
                        else
                        {
                            // Pet with unknown owner.
                            // Register it under UNKNOWN until it resolves.
                            line.encTargetName = unk;
                            line.unitTargetName = unk;
                        }
                        break;
                    }
                case EntityType.Entity:
                    {
                        line.tgtOwnerInfo = entityOwnerRegistery.Resolve(line.tgtInt);
                        if (line.tgtOwnerInfo != null)
                        {
                            // What does this mean???
                        }
                        break;
                    }
                case EntityType.Creature:
                    {
                        if (line.tgtInt.Contains(" Trickster_Baitandswitch"))
                        {
                            // Bait and Switch
                            // 13:07:09:21:57:26.9::Dracnia,P[200787912@7184553 Dracnia@tminhtran],,*,Lodur,C[215 Trickster_Baitandswitch],Lashing Blade,Pn.Gji3ar1,Physical,Critical|Flank|Kill,14778.6,15481.4
                            // Not a pet...

                            line.encTargetName = line.tgtDsp;
                            line.unitTargetName = "Trickster [" + line.tgtDsp + "]";
                        }
                        else
                        {
                            line.encTargetName = line.tgtDsp;
                            String creatureId = line.tgtInt.Split()[0].Substring(2);

                            if (checkBox_mergeNPC.Checked)
                            {
                                // Merge all NPCs to a single name.
                                line.unitTargetName = line.tgtDsp;
                            }
                            else
                            {
                                // Separate each NPC with its unique creature ID added.
                                line.unitTargetName = line.tgtDsp + " [" + creatureId + "]";
                            }
                        }
                        break;
                    }

                // case ParsedLine.EntityType.Unknown:
                default:
                    {
                        // Use the defaults.
                        break;
                    }
            }
        }
 private void ProcessNamesTargetOnly(ParsedLine line)
 {
     // Target only
     ProcessTargetNames(line);
 }
        private void ProcessOwnerSourceNames(ParsedLine line)
        {
            // Owner default:
            line.encAttackerName = line.ownDsp;
            line.unitAttackerName = line.ownDsp;

            // We assume the owner is the owner of the source for this processing.

            if (line.srcEntityType == EntityType.Pet)
            {
                // Use the pet owner name for encounter name and filtering.
                line.encAttackerName = line.ownDsp;

                // Pet name:
                line.unitAttackerName = line.srcDsp + " [" + line.ownDsp + "'s Pet]";
                if (this.checkBox_mergePets.Checked)
                {
                    line.unitAttackerName = line.ownDsp;
                }
            }
            else if (line.ownEntityType == EntityType.Creature)
            {
                line.encAttackerName = line.ownDsp;
                String creatureId = line.ownInt.Split()[0].Substring(2);

                if (checkBox_mergeNPC.Checked)
                {
                    // Merge all NPCs to a single name.
                    line.unitAttackerName = line.ownDsp;
                }
                else
                {
                    // Separate each NPC with its unique creature ID added.
                    line.unitAttackerName = line.ownDsp + " [" + creatureId + "]";
                }
            }
        }
        public void AddShield(MasterSwing ms, ParsedLine line)
        {
            ShieldLine sl = new ShieldLine();
            sl.ms = ms;
            sl.line = line;

            active.AddLast(sl);
        }
        private void ProcessAction(ParsedLine l)
        {
            l.logInfo.detectedType = Color.Gray.ToArgb();

            if (l.type == "HitPoints")
            {
                ProcessActionHeals(l);
            }
            else if (l.type == "Shield")
            {
                ProcessActionShields(l);
            }
            else if (l.type == "AttribModExpire") // Cleanse
            {
                ProcessActionCleanse(l);
            }
            else if (l.type == "Power")
            {
                ProcessActionPower(l);
            }
            else if (l.showPowerDisplayName)
            {
                // Non-damaging effects.
                ProcessActionSPDN(l);
            }
            else
            {
                // What is left should all be damage.
                ProcessActionDamage(l);
            }

            // add action Killing
            if (l.kill)
            {
                l.logInfo.detectedType = Color.Fuchsia.ToArgb();

                // Clean from last MM hit.
                // The Kill can come right before a proc.  Ordering isssue.
                // magicMissileLastHit.Remove(l.tgtInt);

                // TODO: use tgtDsp or unitTargetName?
                ActGlobals.oFormSpellTimers.RemoveTimerMods(l.tgtDsp);
                ActGlobals.oFormSpellTimers.DispellTimerMods(l.tgtDsp);

                // No "Killing : Flank" ever.  Doesn't make sense since there is no damage in the kill tracking.
                // And it messes up the kill counts.
                // AddCombatActionHostile(l, l.swingType, l.critical, l.special, "Killing", Dnum.Death, l.type);

                // Use encounter names attacker and target here.  This allows filtering
                if (ActGlobals.oFormActMain.SetEncounter(l.logInfo.detectedTime, l.encAttackerName, l.encTargetName))
                {
                    MasterSwing ms =
                        new MasterSwing(l.swingType, l.critical, l.special, Dnum.Death, l.logInfo.detectedTime, l.ts,
                            "Killing", l.unitAttackerName, "Death", l.unitTargetName);
                    ms.Tags.Add("Flank", l.flank);
                    ActGlobals.oFormActMain.AddCombatAction(ms);
                }
            }
        }
        private void ProcessActionSPDN(ParsedLine l)
        {
            // Handle all the buff and proc buffs/debuffs
            // type: PowerRecharge, Null, Alacrity, CombatAdvantage, Lightning(Storm Spell), CritSeverity, ...

            l.logInfo.detectedType = Color.DarkTurquoise.ToArgb();

            if (l.evtInt == "Pn.Fwolu") // Chaotic Growth
            {
                // Chaotic Growth (Fixed in latest NW patch)
                // 13:07:18:10:51:58.2::Tifa,P[200500793@6707245 Tifa@liliiith],,*,Guard,C[2205 Mindflayer_Duergarguardthrall],Chaotic Growth,Pn.Fwolu,Null,ShowPowerDisplayName,0,0

                l.logInfo.detectedType = Color.DarkOliveGreen.ToArgb();

                ProcessNamesOST(l);

                ChaoticGrowthInfo cgi = null;
                if (magicMissileLastHit.TryGetValue(l.tgtInt, out cgi))
                {
                    cgi.triggered = true;
                    cgi.ts = l.logInfo.detectedTime;
                    cgi.encName = l.encAttackerName;
                    cgi.unitName = l.unitAttackerName;
                }

                if (ActGlobals.oFormActMain.InCombat)
                {
                    AddCombatActionHostile(l, (int)SwingTypeEnum.NonMelee, l.critical, l.special, l.attackType, Dnum.NoDamage, 0, l.type);
                }
            }
            else if (l.evtInt == "Pn.Zh5vu")
            {
                // Storm Spell
                // 13:07:18:10:49:10.1::Tifa,P[200500793@6707245 Tifa@liliiith],,*,Scourge,C[2143 Mindflayer_Scourge],Storm Spell,Pn.Zh5vu,Lightning,ShowPowerDisplayName,583.917,0

                // Ignore this as there is a damage log line to go with it.
            }
            else if (injuryTypes.ContainsKey(l.evtInt))
            {
                // Injure...

                // Ignore this as it is not reall part of combat.
            }
            else
            {
                // Default

                if (ActGlobals.oFormActMain.InCombat)
                {
                    ProcessNamesOST(l);
                    AddCombatActionHostile(l, (int)SwingTypeEnum.NonMelee, l.critical, l.special, l.attackType, Dnum.NoDamage, 0, l.type);
                }
            }
        }
        private void ProcessBasic(ParsedLine line)
        {
            //
            // Fix up the ParsedLine.
            // Add calculated data fields to the ParsedLine.
            //

            if (line.ownDsp == "" && line.ownInt == "")
            {
                // Ugly fix for lines without an owner
                line.ownDsp = NW_Parser.unk;
                line.ownInt = NW_Parser.unkInt;
            }
            else if (line.ownInt[0] == 'P') { line.ownEntityType = EntityType.Player; }
            else if (line.ownInt[0] == 'C')
            {
                // There should never be a Pet or Entity in this possition??
                line.ownEntityType = EntityType.Creature;
            }

            if (line.srcInt == "*")
            {
                line.srcDsp = line.ownDsp;
                line.srcInt = line.ownInt;
                line.srcEntityType = line.ownEntityType;
            }
            else if ((line.srcInt == "") && (line.srcDsp == ""))
            {
                //
                // // Ugly fix for lines without a source
                // // srcDsp = NW_Parser.unk;
                // // srcInt = NW_Parser.unkInt;
                //
                // Ugly fix does not work.  See this valid example from a log:
                // "13:07:02:13:48:18.1::Kallista Hellbourne,P[200674407@288107 Kallista Hellbourne@tonyleon],,,Sentry,C[1150404 Frost_Goblin_Sentry],Storm Spell,Pn.Zh5vu,Lightning,ShowPowerDisplayName,580.333,0"
                // The Control Wizard effect Storm Spell seems to not have a source.  Should just use the owner in this case.
                // If the owner is unknown this will work as before.

                line.srcDsp = line.ownDsp;
                line.srcInt = line.ownInt;
                line.srcEntityType = line.ownEntityType;
            }
            else if (line.srcInt[0] == 'P')
            {
                line.srcEntityType = EntityType.Player;
            }
            else if (line.srcInt[0] == 'C')
            {
                // Basic Pet and Entity detection..

                if (line.srcInt.Contains(" Pet_"))
                {
                    line.srcEntityType = EntityType.Pet;
                }
                else if (line.srcInt.Contains(" Entity_"))
                {
                    line.srcEntityType = EntityType.Entity;
                }
                else
                {
                    line.srcEntityType = EntityType.Creature;
                }
            }

            if (line.tgtInt == "*")
            {
                line.tgtDsp = line.srcDsp;
                line.tgtInt = line.srcInt;
                line.tgtEntityType = line.srcEntityType;

                // If it is a Pet then the pet owner info needs to get set.
                // But first we can not do it here in case this is the first time we saw the owner.
                // Need to register owner of pet first...
            }
            else if ((line.tgtInt == "") && (line.tgtDsp == ""))
            {
                // Ugly fix for lines without a target
                line.tgtDsp = NW_Parser.unk;
                line.tgtInt = NW_Parser.unkInt;
            }
            else if (line.tgtInt[0] == 'P') { line.tgtEntityType = EntityType.Player; }
            else if (line.tgtInt[0] == 'C')
            {
                // Basic Pet and Entity detection..

                if (line.tgtInt.Contains(" Pet_"))
                {
                    line.tgtEntityType = EntityType.Pet;
                }
                else if (line.tgtInt.Contains(" Entity_"))
                {
                    line.tgtEntityType = EntityType.Entity;
                }
                else
                {
                    line.tgtEntityType = EntityType.Creature;
                }
            }

            // Defaults for the clean names.
            line.encAttackerName = line.ownDsp;
            line.encTargetName = line.tgtDsp;
            line.unitAttackerName = line.ownDsp;
            line.unitTargetName = line.tgtDsp;
        }
        private void ProcessActionPower(ParsedLine l)
        {
            int magAdj = (int)Math.Round(l.mag);
            //int magBaseAdj = (int)Math.Round(l.magBase * 10);

            l.logInfo.detectedType = Color.Black.ToArgb();

            // NOTE: Do NOT use SetEncounter() on power (i.e Non-Hostile Actions)

            if (ActGlobals.oFormActMain.InCombat)
            {
                if (l.evtInt == "Pn.Ygyxld") // Critical Power
                {
                    // Critical Power
                    // 13:07:18:10:40:48.3::Tifa,P[200500793@6707245 Tifa@liliiith],Shard,C[2006 Entity_Shardoftheendlessavalanche],,*,Critical Power,Pn.Ygyxld,Power,,-0,0
                    // This power can trigger on CW's entities... These triggers should be ignored as they are zero effect.

                    if (l.ownInt != l.srcInt)
                    {
                        l.logInfo.detectedType = Color.Gray.ToArgb();
                        return;
                    }
                }

                if (l.evtInt == "Pn.He9xu") // Bait and Switch
                {
                    // TR - Bait and Switch Trigger
                    // special case: Bait and Switch
                    // 13:07:09:20:53:00.9::Lodur,C[835 Trickster_Baitandswitch],,*,Lodur,P[201093074@7545190 Lodur@lodur42],Trigger,Pn.He9xu,Power,,-0.521139,0
                    // 13:07:09:21:43:30.3::Lodur,C[152 Trickster_Baitandswitch],,*,Lodur,P[201093074@7545190 Lodur@lodur42],Trigger,Pn.He9xu,Power,Immune,0,0
                    // 13:07:10:09:11:08.8::Lodur,C[178 Trickster_Baitandswitch],,*,Lodur,P[201093074@7545190 Lodur@lodur42],Trigger,Pn.He9xu,Power,Immune,0,0

                    ProcessNamesTargetOnly(l);

                    // Target is the source as well.

                    AddCombatActionNW(
                        (int)SwingTypeEnum.PowerHealing, l.critical, false, "", "Trickster [" + l.tgtDsp + "]",
                        "Bait and Switch", new Dnum(-magAdj), -l.mag, 0, l.logInfo.detectedTime,
                        l.ts, l.tgtDsp, l.type);

                }
                else if (l.evtInt == "Pn.Jy04um1") // Guard Break
                {
                    // Guard Break
                    // 13:07:18:10:50:08.7::Largoevo,P[201228983@6531604 Largoevo@largoevo],Bodyguard,C[2175 Mindflayer_Thoonhulk_Eventbodyguard],Largoevo,P[201228983@6531604 Largoevo@largoevo],Guard Break,Pn.Jy04um1,Power,,-28.8571,0
                    // Owner    = Guardian Fighter  [Attacker]
                    // source   = Target enemy      [Special]
                    // target   = Guardian Fighter  [Victim]
                    //
                    // NOTE: Do not assume source is a pet of owner.  Source could be a pet or fake pet.  Resolve fake pet to owner.

                    ProcessNamesST(l);

                    AddCombatActionNW(
                        (int)SwingTypeEnum.PowerHealing, l.critical, false, l.unitAttackerName, l.unitTargetName,
                        l.evtDsp, new Dnum(-magAdj), -l.mag, 0, l.logInfo.detectedTime,
                        l.ts, l.unitTargetName, l.type);
                }
                else if (l.evtInt == "Pn.Wxao05") // Maelstrom of Chaos
                {
                    // Maelstrom of Chaos
                    // 13:07:18:10:37:50.5::Tifa,P[200500793@6707245 Tifa@liliiith],,*,,*,Maelstrom of Chaos,Pn.Wxao05,Power,,500,0
                    // Canceling this power early will cost half of your Action Points.

                    // Ignore this for now.
                }
                else
                {
                    // Normal Power case...
                    ProcessNamesOST(l);

                    AddCombatActionNW(
                        (int)SwingTypeEnum.PowerHealing, l.critical, l.flank, l.special,
                        l.unitAttackerName, l.attackType, new Dnum(-magAdj), -l.mag, -l.magBase,
                        l.logInfo.detectedTime, l.ts, l.unitTargetName, l.type);
                }
            }
        }
        private void ProcessActionShields(ParsedLine l)
        {
            int magAdj = (int)Math.Round(l.mag);
            int magBaseAdj = (int)Math.Round(l.magBase);

            // Shielding goes first and acts like a heal to cancel coming damage.  Attacker has his own damage line.  example:

            // 13:07:02:10:48:49.1::Neston,P[200243656@6371989 Neston@adamtech],,*,Flemming Fedtgebis,P[201082649@7532407 Flemming Fedtgebis@feehavregroed],Forgemaster's Flame,Pn.Lbf9ic,Shield,,-349.348,-154.608
            // 13:07:02:10:48:49.1::SorXian,P[201063397@7511146 SorXian@sorxian],,*,Flemming Fedtgebis,P[201082649@7532407 Flemming Fedtgebis@feehavregroed],Entangling Force,Pn.Oonws91,Shield,,-559.613,-247.663
            // 13:07:02:10:48:49.1::Neston,P[200243656@6371989 Neston@adamtech],,*,Flemming Fedtgebis,P[201082649@7532407 Flemming Fedtgebis@feehavregroed],Forgemaster's Flame,Pn.Lbf9ic,Radiant,,154.608,349.348
            // 13:07:02:10:48:49.1::SorXian,P[201063397@7511146 SorXian@sorxian],,*,Flemming Fedtgebis,P[201082649@7532407 Flemming Fedtgebis@feehavregroed],Entangling Force,Pn.Oonws91,Arcane,,247.663,559.613

            // NOTE:
            // Notice that the mag and magBase numbers are swap in the shield line verse the damage line.
            // Therefore the amount shield == magBase ???
            // The mag is meaningless ???
            // If mag > magBase on the attack is all damage not shielded ???  (ie high armor pen)

            // NOTE:
            // NW Patch on 7/17/2013 changed shield to report blocked damage in the mag field.
            // 13:07:18:10:25:54.2::Miner,C[1445 Mindflayer_Duergarminerthrall],,*,Largoevo,P[201228983@6531604 Largoevo@largoevo],Melee Attack,Pn.M7kie6,Shield,,-242.837,0
            // Actuall not sure on this....

            //
            // Target prevented damage.
            //

            l.logInfo.detectedType = l.critical ? Color.Green.ToArgb() : Color.DarkGreen.ToArgb();

            ProcessNamesOST(l);

            // Use encounter names attacker and target here.  This allows filtering
            // Hostile action triggered.  Use SetEncounter().
            if (ActGlobals.oFormActMain.SetEncounter(l.logInfo.detectedTime, l.encAttackerName, l.encTargetName))
            {
                // Put the attacker and the attack type in the special field.
                string special = l.unitAttackerName + " : " + l.attackType;

                Dnum shielded = null;
                float mag = 0;
                float magBase = 0;

                // This is just weird...
                if (l.magBase == 0) // Don't use magBaseAdj here.  Rounded to zero is not zero.
                {
                    shielded = new Dnum( -magAdj );
                    mag = -l.mag;
                    magBase = -l.magBase;
                }
                else
                {
                    shielded = new Dnum(-magBaseAdj);
                    mag = -l.magBase;
                    magBase = -l.mag;
                }

                // SwingType = Heal
                // special = attacker
                // attacker & victim = target
                MasterSwing ms = new MasterSwing(
                    (int)SwingTypeEnum.Healing,
                    l.critical, special, shielded, l.logInfo.detectedTime, l.ts, l.type, l.unitTargetName, l.type, l.unitTargetName);

                ms.Tags.Add("DamageF", mag);
                ms.Tags.Add("Flank", l.flank);

                ActGlobals.oFormActMain.AddCombatAction(ms);

                unmatchedShieldLines.AddShield(ms, l);
            }
        }
        private void ProcessActionHeals(ParsedLine l)
        {
            int magAdj = (int)Math.Round(l.mag);
            int magBaseAdj = (int)Math.Round(l.magBase);

            l.logInfo.detectedType = l.critical ? Color.Green.ToArgb() : Color.DarkGreen.ToArgb();

            // NOTE: Do NOT use SetEncounter() on heals (i.e Non-Hostile Actions)

            // Heals can not start an encounter.
            if (ActGlobals.oFormActMain.InCombat)
            {
                ProcessNamesOST(l);

                // PVP Rune Heal - Needs some cleanup.  Use the player as the source since they grabbed it.
                // Does 'Pn.R0jdk' == PVP RUNE HEAL???
                // 13:07:09:14:00:23.2::Rune,C[317 Pvp_Rune_Heal],,*,Mus'Mugen Uhlaalaa,P[201045055@5998737 Mus'Mugen Uhlaalaa@bupfen],Heal,Pn.R0jdk,HitPoints,,-1136.92,0

                if (l.evtInt == "Pn.R0jdk") // Assume this is PVP Rune Heal for now...
                {
                    AddCombatActionNW(
                        (int)SwingTypeEnum.Healing, l.critical, false, l.special, l.unitTargetName,
                        "PVP Heal Rune", new Dnum(-magAdj), -l.mag, -l.magBase, l.logInfo.detectedTime,
                        l.ts, l.unitTargetName, l.type);
                }
                else if (l.evtInt == "Pn.Hemuxg") // PvP Kill downed player
                {
                    // PVP finish off
                    // 13:07:10:09:13:09.2::CamierDerWeisse,P[200083978@5783571 CamierDerWeisse@faru2],,*,FIVEFINGERZ,P[200862049@7260841 FIVEFINGERZ@fivefingerz],Kill,Pn.Hemuxg,HitPoints,,0,0

                    // TODO:  Should this be recorded or ignored...
                }
                else if (l.evtInt == "Pn.Qiwkdx1") // Pretty sure this is end of pvp auto heal.
                {
                    // TODO: Make sure this is really only an end of pvp match auto heal.
                    // 13:07:10:11:03:42.1::Nephylia Necromon,P[201238857@7793332 Nephylia Necromon@nephodin],,*,,*,,Pn.Qiwkdx1,HitPoints,,-7240.66,0
                    // Ignore it.
                }
                else if (l.evtInt == "Pn.Dbm4um1") // Campfire
                {
                    // Camp fire.
                    // Give credit to the player for standing in it.
                    // Note: Trying to eliminate the [unknown] source.
                    // 13:07:10:11:02:20.6::,,,,Brandeor,P[201267923@5148411 Brandeor@brandeor],Campfire,Pn.Dbm4um1,HitPoints,,-525.321,0

                    AddCombatActionNW(
                        (int)SwingTypeEnum.Healing, l.critical, false, l.special, l.unitTargetName,
                        l.evtDsp, new Dnum(-magAdj), -l.mag, -l.magBase, l.logInfo.detectedTime,
                        l.ts, l.unitTargetName, l.type);
                }
                else if (l.evtInt == "Pn.Zrqjy1") // Chaotic Growth
                {
                    // Chaotic Growth - Proc debuff from CW Magic Missile.  Debuffed target AOE heals casters allies.
                    // But the log shows the debuffed target as the healer...
                    // Credit should go to the CW that casted the MM, but that is not clear in the logs.

                    // 13:07:09:20:52:51.5::Rassler,P[200973822@6215544 Rassler@lendal4],,*,Rhiyan Torr,P[200010914@5686857 Rhiyan Torr@wyvernonenine],Chaotic Growth,Pn.Zrqjy1,HitPoints,,-215,0

                    // NOTE:  Track the last person to hit each target with magic missile.  Give healing credit to that person.
                    //        IF that fails then it is a self heal...  Keeps it on the same team in pvp at least.

                    bool handled = false;
                    ChaoticGrowthInfo cgi = null;
                    if (magicMissileLastHit.TryGetValue(l.srcInt, out cgi))
                    {
                        if (!cgi.triggered)
                        {
                            cgi.triggered = true;
                            cgi.ts = l.logInfo.detectedTime;
                        }

                        // Use encounter names attacker and target here.  This allows filtering
                        // NOTE: Use SetEncounter() as this heal is part of a hostile action.
                        if (ActGlobals.oFormActMain.SetEncounter(l.logInfo.detectedTime, cgi.encName, l.encTargetName))
                        {
                            AddCombatActionNW(
                                (int)SwingTypeEnum.Healing, l.critical, l.flank, l.unitAttackerName, cgi.unitName,
                                l.evtDsp, new Dnum(-magAdj), -l.mag, -l.magBase, l.logInfo.detectedTime,
                                l.ts, l.unitTargetName, l.type);
                        }

                        handled = true;
                    }

                    if (!handled)
                    {
                        // Use encounter names attacker and target here.  This allows filtering
                        // NOTE: Use SetEncounter() as this heal is part of a hostile action.
                        if (ActGlobals.oFormActMain.SetEncounter(l.logInfo.detectedTime, l.encTargetName, l.encTargetName))
                        {
                            AddCombatActionNW(
                                (int)SwingTypeEnum.Healing, l.critical, l.flank, l.unitAttackerName, unk,
                                l.evtDsp, new Dnum(-magAdj), -l.mag, -l.magBase, l.logInfo.detectedTime,
                                l.ts, l.unitTargetName, l.type);
                        }
                    }
                }
                else if (l.evtInt == "Pn.R1tsg4")
                {
                    // Shocking execution
                    // There is a HitPoints of value zero that is assioatied with shocking execution.
                    // Note that the <EvtInt> is different from the actual damaging log entry.
                    // Just ignore it...
                    // 13:07:17:10:33:02.1::Lodur,P[201093074@7545190 Lodur@lodur42],,*,KingOfSwordsx2,P[201247997@5290133 KingOfSwordsx2@sepherosrox],Shocking Execution,Pn.R1tsg4,HitPoints,,0,0

                }
                else
                {
                    // Default heal.

                    AddCombatActionNW(
                        (int)SwingTypeEnum.Healing, l.critical, l.flank, l.special, l.unitAttackerName,
                        l.attackType, new Dnum(-magAdj), -l.mag, -l.magBase, l.logInfo.detectedTime,
                        l.ts, l.unitTargetName, l.type);
                }
            }
        }
        private void ProcessActionDamage(ParsedLine l)
        {
            int magAdj = (int)Math.Round(l.mag);
            int magBaseAdj = (int)Math.Round(l.magBase);

            l.logInfo.detectedType = l.critical ? Color.Red.ToArgb() : Color.DarkRed.ToArgb();

            string special = l.special;

            MasterSwing msShielded = unmatchedShieldLines.MatchDamage(l);
            if (msShielded != null)
            {
                // Fix up the shield line.
                // Tags are about the only thing that can be altered on MS that is already added via AddCombatAction.
                // So do most of it with adding Tags.

                // Shield line:  add column for attack damage and % blocked
                // Attack line:  add amount shielded to the 'special' column.

                object val;
                if (msShielded.Tags.TryGetValue("DamageF", out val))
                {
                    float df = (float) val;
                    string shieldSpecialText = "Shield(" + df.ToString("F1") + ")";

                    if (special == "None")
                    {
                        special = shieldSpecialText;
                    }
                    else
                    {
                        special = l.special + " | " + shieldSpecialText;
                    }

                    float shielded = df / l.mag;
                    msShielded.Tags.Add("ShieldDmgF", l.mag);
                    msShielded.Tags.Add("ShieldP", shielded);
                }
            }

            if (l.evtInt == "Autodesc.Combatevent.Falling")
            {
                // Falling damage does not start combat...
                if (ActGlobals.oFormActMain.InCombat)
                {
                    ProcessNamesOST(l);
                    AddCombatActionHostile(l, l.swingType, l.critical, special, l.attackType, magAdj, l.mag, l.type, l.magBase);
                }
            }
            else if (l.evtInt == "Pn.Wypyjw1") // Knight's Valor,
            {
                // "13:07:18:10:30:48.3::Largoevo,P[201228983@6531604 Largoevo@largoevo],Ugan the Abominable,C[1469 Mindflayer_Miniboss_Ugan],Largoevo,P[201228983@6531604 Largoevo@largoevo],Knight's Valor,Pn.Wypyjw1,Physical,,449.42,1195.48
                // Attack goes SRC -> TRG and ignore the owner.  The SRC is not the owner's pet.

                ProcessNamesST(l);
                AddCombatActionHostile(l, l.swingType, l.critical, special, l.attackType, magAdj, l.mag, l.type, l.magBase);
            }
            else
            {
                ProcessNamesOST(l);

                if ((l.evtInt == "Pn.3t6cw8") && (magAdj > 0)) // Magic Missile
                {
                    ChaoticGrowthInfo cgi = null;
                    if (magicMissileLastHit.TryGetValue(l.tgtInt, out cgi))
                    {
                        if (cgi.triggered)
                        {
                            TimeSpan t = l.logInfo.detectedTime - cgi.ts;
                            if (t.TotalSeconds > 10.0)
                            {
                                cgi.triggered = false;
                            }
                        }

                        if (!cgi.triggered)
                        {
                            cgi.encName = l.encAttackerName;
                            cgi.unitName = l.unitAttackerName;
                            cgi.ts = l.logInfo.detectedTime;
                        }
                    }
                    else
                    {
                        cgi = new ChaoticGrowthInfo();
                        cgi.encName = l.encAttackerName;
                        cgi.unitName = l.unitAttackerName;
                        cgi.triggered = false;
                        cgi.ts = l.logInfo.detectedTime;

                        magicMissileLastHit.Add(l.tgtInt, cgi);
                    }
                }

                //
                // Note:  There seems to be many cases where dmgBase == 0 while damage is applied.
                //

                /*
                if (l.flags.Contains("Miss"))
                {
                    // TODO:  Not sure I have ever seen a "miss" in a log.  This actually valid?
                    AddCombatActionHostile(l, l.swingType, l.critical, l.special, l.attackType, Dnum.Miss, l.type, magBaseAdj);
                }
                else
                */

                if (l.immune)
                {
                    if ((magAdj == 0) && (magBaseAdj == 0))
                    {
                        // 13:07:18:10:49:21.6::Tristan,C[2120 Pet_Dog],,*,Oll'noth the Dominator,C[1997 Mindflayer_Eventboss],Takedown,Pn.Ebxsjf,KnockBack,Immune,0,0

                        // Ignore these for now...
                        l.logInfo.detectedType = Color.Gray.ToArgb();
                    }
                    else
                    {
                        // Generally damaging attacks have mag=0 and magBase > 0 when Immune.
                        l.logInfo.detectedType = Color.Maroon.ToArgb();
                        AddCombatActionHostile(l, l.swingType, l.critical, special, l.attackType, Dnum.NoDamage, l.mag, l.type, l.magBase);
                    }
                }
                else if (l.dodge)
                {
                    // It really looks like Dodge does not stop all damage - just reduces it by about 80%...
                    // I have seen damaging attacks that are both Dodge and Kill in the flags.
                    // So the target dodged but still died.
                    l.logInfo.detectedType = Color.Maroon.ToArgb();
                    AddCombatActionHostile(l, l.swingType, l.critical, special, l.attackType, magAdj, l.mag, l.type, l.magBase);
                }
                else
                {
                    if ((magAdj == 0) && (magBaseAdj == 0))
                    {
                        // Ignore it...  This is generally a Non-Target entity getting AOE'd...
                        l.logInfo.detectedType = Color.Gray.ToArgb();
                    }
                    else
                    {
                        // NOT All attacks have a magBase (anymore).
                        AddCombatActionHostile(l, l.swingType, l.critical, special, l.attackType, magAdj, l.mag, l.type, l.magBase);
                    }
                }
            }
        }
        private void ProcessActionCleanse(ParsedLine l)
        {
            l.logInfo.detectedType = Color.Blue.ToArgb();

            // Cleanse
            // 13:07:17:10:37:53.5::righteous,P[201081445@5908801 righteous@r1ghteousg],,*,KingOfSwordsx2,P[201247997@5290133 KingOfSwordsx2@sepherosrox],Cleanse,Pn.H8hm3x1,AttribModExpire,ShowPowerDisplayName,0,0

            if (ActGlobals.oFormActMain.InCombat)
            {
                ProcessNamesOST(l);

                AddCombatActionNW(
                    (int)SwingTypeEnum.CureDispel, l.critical, l.flank, l.special,
                    l.unitAttackerName, l.attackType, Dnum.NoDamage, l.mag, l.magBase,
                    l.logInfo.detectedTime, l.ts, l.unitTargetName, l.type );
            }
        }
        // For hostile actions only.  Handles the SetEncounter().
        private void AddCombatActionHostile(
            ParsedLine line, int swingType, bool critical, string special, string theAttackType, Dnum Damage, float realDamage, string theDamageType, float baseDamage=0)
        {
            // Use encounter names attacker and target here.  This allows filtering
            if (ActGlobals.oFormActMain.SetEncounter(line.logInfo.detectedTime, line.encAttackerName, line.encTargetName))
            {
                // add Flank to AttackType if setting is set
                string tempAttack = theAttackType;
                if (line.flank && this.checkBox_flankSkill.Checked) tempAttack = theAttackType + ": Flank";

                AddCombatActionNW(
                    swingType, line.critical, line.flank, special, line.unitAttackerName,
                    tempAttack, Damage, realDamage, baseDamage, line.logInfo.detectedTime,
                    line.ts, line.unitTargetName, theDamageType);
            }
        }
        private void ProcessNamesOST(ParsedLine line)
        {
            // Owner, Source (belongs to owner), Target
            petOwnerRegistery.Register(line);
            entityOwnerRegistery.Register(line);

            ProcessOwnerSourceNames(line);
            ProcessTargetNames(line);
        }
        public void Register(ParsedLine line)
        {
            // Problem lines:
            // "13:07:02:13:48:18.1::Kallista Hellbourne,P[200674407@288107 Kallista Hellbourne@tonyleon],,,Sentry,C[1150404 Frost_Goblin_Sentry],Storm Spell,Pn.Zh5vu,Lightning,ShowPowerDisplayName,580.333,0"
            // "13:07:03:11:47:12.6::Grizzard,P[200743305@6022049 Grizzard@shamedy],Cutter,C[9395 Winterforge_Frost_Goblin_Cutter],Grizzard,P[200743305@6022049 Grizzard@shamedy],Guard Break,Pn.Jy04um1,Power,,-23.1135,0"
            // "13:07:09:11:01:08.4::Correk,P[201028460@1546238 Correk@Gleyvien],Target Dummy,C[265291 Entity_Targetdummy],,*,Doom!,Pn.F1j0yx1,Radiant,Critical,10557.2,8445.78"
            // "13:07:09:11:01:59.5::Nasus king,P[201132249@7587600 Nasus king@portazorras],SerGay,C[265715 Pet_Clericdisciple],Target Dummy,C[265291 Entity_Targetdummy],Sacred Flame,Pn.Tegils,Physical,Flank,59.7605,0"

            bool add = false;
            OwnerInfo OwnerInfo = null;

            // Record owner of all pets we see.
            if (line.srcEntityType == EntityType.Pet)
            {
                if (petPlayerCache.TryGetValue(line.srcInt, out OwnerInfo))
                {
                    if (OwnerInfo.ownerInt != line.ownInt)
                    {
                        // Pet Owner changed...  Not sure if possible... but just in case.
                        petPlayerCache.Remove(OwnerInfo.petInt);
                        playerPetCache.Remove(OwnerInfo.ownerInt);
                        add = true;
                    }
                }
                else
                {
                    // Check if this player had another pet registered and clean up.
                    // Only one pet is allowed.
                    if (playerPetCache.TryGetValue(line.ownInt, out OwnerInfo))
                    {
                        playerPetCache.Remove(line.ownInt);
                        petPlayerCache.Remove(OwnerInfo.petInt);
                    }

                    add = true;
                }

                if (add)
                {
                    OwnerInfo = new OwnerInfo();
                    OwnerInfo.ownerDsp = line.ownDsp;
                    OwnerInfo.ownerInt = line.ownInt;
                    OwnerInfo.ownerEntityType = line.ownEntityType;
                    OwnerInfo.petDsp = line.srcDsp;
                    OwnerInfo.petInt = line.srcInt;

                    petPlayerCache.Add(line.srcInt, OwnerInfo);
                    playerPetCache.Add(line.ownInt, OwnerInfo);
                }
            }
        }
 private void ProcessNamesST(ParsedLine line)
 {
     // Source, Target: All independant
     ProcessSourceNames(line);
     ProcessTargetNames(line);
 }
        public MasterSwing MatchDamage(ParsedLine line)
        {
            LinkedListNode<ShieldLine> slnNext = active.First;

            while (slnNext != null)
            {
                LinkedListNode<ShieldLine> cur = slnNext;
                ShieldLine sl = cur.Value;
                slnNext = slnNext.Next;

                // Examaple:
                // 13:09:26:09:31:31.2::Lorne Fellbane,P[201332730@6101294 Lorne Fellbane@todesfaelle],,*,HeLLCaT,P[201327748@7398668 HeLLCaT@phantom3535],Bilethorn Weapon,Pn.Nhw1351,Shield,,-1.3,0
                // 13:09:26:09:31:31.2::Lorne Fellbane,P[201332730@6101294 Lorne Fellbane@todesfaelle],,*,HeLLCaT,P[201327748@7398668 HeLLCaT@phantom3535],Bilethorn Weapon,Pn.Nhw1351,Shield,,-10.9585,0
                // 13:09:26:09:31:31.2::Lorne Fellbane,P[201332730@6101294 Lorne Fellbane@todesfaelle],HeLLCaT,P[201327748@7398668 HeLLCaT@phantom3535],,*,Bilethorn Weapon,Pn.Nhw1351,Poison,,1.3,6.5
                // 13:09:26:09:31:31.2::Lorne Fellbane,P[201332730@6101294 Lorne Fellbane@todesfaelle],HeLLCaT,P[201327748@7398668 HeLLCaT@phantom3535],,*,Bilethorn Weapon,Pn.Nhw1351,Poison,,10.9585,54.7925

                // Notice that the Source field doesn't always match...

                /* Odd case of zero damage:
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Shield,,-2,-0.484209
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Shield,,-2,-0.242104
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Fire,Dodge,0,0
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Fire,,0,0
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Fire,,0.484209,2
            13:09:26:09:36:11.8::briserus,P[201401849@8271148 briserus@briserus],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Flaming Weapon,Pn.Mzftfj,Fire,Dodge,0.242104,2
                */

                /* Two Shield lines for one damage line:  TODO: Handle this...
            13:09:26:09:36:20.8::DRUGnROLL,P[200404637@6548010 DRUGnROLL@drugnroll],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Entangling Force,Pn.Oonws91,Shield,,-17.2139,-3.44278
            13:09:26:09:36:20.8::DRUGnROLL,P[200404637@6548010 DRUGnROLL@drugnroll],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Entangling Force,Pn.Oonws91,Shield,,-340.835,0
            13:09:26:09:36:20.8::Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],,*,,*,Health Steal,Pn.S6b30w1,HitPoints,,-0.0190611,0
            13:09:26:09:36:20.8::Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],,*,,*,Health Steal,Pn.S6b30w1,HitPoints,,-0.117442,0
            13:09:26:09:36:20.8::DRUGnROLL,P[200404637@6548010 DRUGnROLL@drugnroll],,*,Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],Entangling Force,Pn.Oonws91,Arcane,Critical,344.278,1721.39
            13:09:26:09:36:20.9::Lord DopeVIII,P[200441364@6420568 Lord DopeVIII@lorddopeviii],,*,,*,Shielded Resurgence,Pn.Mrczs41,Null,ShowPowerDisplayName,0,0
                */

                // Compare
                if ((sl.line.evtInt == line.evtInt) &&
                        (sl.line.ownInt == line.ownInt) &&
                        // (sl.line.srcInt == line.srcInt) &&
                        (sl.line.tgtInt == line.tgtInt) &&
                        (line.type != "Shield") &&
                        (line.mag > 0.0)  // <- Skip zero damage lines.
                    )
                {
                    // Matched
                    active.Remove(cur);
                    return sl.ms;
                }
                else
                {
                    // Check expired.
                    TimeSpan diff = line.logInfo.detectedTime - sl.ms.Time;

                    if (diff.TotalMilliseconds > 500)
                    {
                        // Drop old and unmatch shield lines.
                        // Generally shield line should match in <= 100ms.
                        active.Remove(cur);
                    }
                }
            }

            return null;
        }
        // Must match LogLineEventDelegate signature
        void oFormActMain_BeforeLogLineRead(bool isImport, LogLineEventArgs logInfo)
        {
            parsedLineCount++;

            if (logInfo.logLine.Length < 30 || logInfo.logLine[19] != ':' || logInfo.logLine[20] != ':')
            {
                logInfo.detectedType = Color.DarkGray.ToArgb();
                errorLineCount++;
                return;
            }

            if (logInfo.detectedTime != curActionTime)
            {
                // Different times mean new action block, any pending actions won't be related to those of the new block
                curActionTime = logInfo.detectedTime;
            }

            ParsedLine pl = new ParsedLine(logInfo);

            if (pl.error)
            {
                logInfo.detectedType = Color.DarkGray.ToArgb();
                errorLineCount++;
                return;
            }

            // Fix up the ParsedLine to be easy to process.
            ProcessBasic(pl);

            // Detect Player names..
            if ( ! (playersCharacterFound || isImport ) )
            {
                if (pl.ownEntityType == EntityType.Player)
                {
                    if (playerCharacterNames.ContainsKey(pl.ownDsp))
                    {
                        ActGlobals.charName = pl.ownDsp;
                        playersCharacterFound = true;
                    }
                }
            }

            // Do the real stuff..
            ProcessAction(pl);
        }