public void calculateDPS()
        {
            if (CalcOpts.PetFamily == PETFAMILY.None) return;

            int levelDifference = BossOpts.Level - character.Level;

            // setup
            #region Attack Power
            float damageBonusMeleeFromAP = 0f; //0.07f * PetStats.AttackPower; // PetAPBonus
            float damageBonusSpellsFromAP = 0f; //(1.5f / 35f) * PetStats.AttackPower; // PetSpellScaling * PetAP
            #endregion
            #region Spell Hit
            // Full Resists (Spell Hit)
            float levelResistFactor = 0.04f;
            if (levelDifference == 1) levelResistFactor = 0.05f;
            if (levelDifference == 2) levelResistFactor = 0.06f;
            if (levelDifference == 3) levelResistFactor = 0.08f;
            
            float fullResist = levelResistFactor - (1f - PetChanceToSpellMiss);
            if (fullResist < 0) fullResist = 0;
            float fullResistDamageAdjust = 1f - fullResist;

            // Partial Resists (Spell Hit)
#if RAWR3 || RAWR4 || SILVERLIGHT
            float averageResist = (BossOpts.Level - character.Level) * 0.02f;
#else
            float averageResist = (CalcOpts.TargetLevel - character.Level) * 0.02f;
#endif
            float resist10 = 5.0f * averageResist;
            float resist20 = 2.5f * averageResist;
            float partialResistDamageAdjust = 1f - (resist10 * 0.1f + resist20 * 0.2f);
            #endregion
            #region Damage Adjustments
            // Dodge adjust can't help us, only hinder
            float damageAdjustDodge = Math.Min(1f, 1f - PetChanceToBeDodged);

            //float damageAdjustHitCritMelee    = (2f * calculatedStats.petCritTotalMelee   ) + (1f - (PetChanceToMiss + PetChanceToBeDodged) - calculatedStats.petCritTotalMelee);
            //float damageAdjustHitCritSpecials = (2f * calculatedStats.petCritTotalSpecials) + (1f - (PetChanceToMiss + PetChanceToBeDodged) - calculatedStats.petCritTotalSpecials); // CritAdjustments

            //float damageAdjustFerociousInspiration = calculatedStats.ferociousInspirationDamageAdjust;
            //float damageAdjustTargetDebuffs = calculatedStats.targetDebuffsPetDamage;
            float damageAdjustPetFamily = 1.05f;
            float damageAdjustMarkedForDeath = 1f + (Talents.MarkedForDeath * 0.02f);
//            float damageAdjustCobraReflexes = 1f - (PetTalents.CobraReflexes * 0.075f); // this is a negative effect!

            // Feeding Frenzy
            float damageAdjustFeedingFrenzy = 1;
            if (PetTalents.FeedingFrenzy > 0) {
                float feedingFrenzyTimeSpent = ((float)BossOpts.Under20Perc + (float)BossOpts.Under35Perc) * BossOpts.BerserkTimer;
                float feedingFrenzyUptime = feedingFrenzyTimeSpent > 0 ? feedingFrenzyTimeSpent / BossOpts.BerserkTimer : 0;
                damageAdjustFeedingFrenzy = 1f + feedingFrenzyUptime * PetTalents.FeedingFrenzy * 0.08f;
            }

            // Glancing Blows
            float glancingBlowsSkillDiff = (BossOpts.Level * 5) - (CalcOpts.PetLevel * 5); // F55
            if (glancingBlowsSkillDiff < 0) glancingBlowsSkillDiff = 0;
            float glancingBlowsChance  = glancingBlowsSkillDiff > 15 ? 0.25f : 0.1f + glancingBlowsSkillDiff * 0.01f; // F56
            float glancingBlowsLowEnd  = (float)Math.Min(1.3f - 0.05f * glancingBlowsSkillDiff, 0.91f); // F57
            float glancingBlowsHighEnd = (float)Math.Min(1.2f - 0.03f * glancingBlowsSkillDiff, 0.99f); // F58
            float damageAdjustGlancingBlows = 1 - (glancingBlowsChance * (1 - ((glancingBlowsLowEnd + glancingBlowsHighEnd) / 2)));

            // Savage Rend
            float damageAdjustSavageRend = 1;
            float savageRendFrequency = priorityRotation.getSkillFrequency(PetAttacks.SavageRend);
            if (savageRendFrequency > 0)
            {
                //float savageRendTimeBetweenProcs = savageRendFrequency * (1f / calculatedStats.petCritTotalSpecials);
                //float savageRendUptime = savageRendTimeBetweenProcs > 0 ? 30f / savageRendTimeBetweenProcs : 0;
                //damageAdjustSavageRend = 1f + 0.1f * savageRendUptime;
            }

            #region Monstrous Bite
            float damageAdjustMonstrousBite = 1;
            float monstrousBiteFrequency = priorityRotation.getSkillFrequency(PetAttacks.MonstrousBite);
            if (monstrousBiteFrequency > 0)
            {
                float monstrousBiteUseFreq = monstrousBiteFrequency;
                float monstrousBiteDuration = 12;

                float monstrousBiteChanceToApply = 1f - PetChanceToMiss - PetChanceToBeDodged;
                float monstrousBiteChancesToMaintain = 1;

                float monstrousBiteChanceToApplyFirst = monstrousBiteChancesToMaintain == 0 ? 0 : monstrousBiteChanceToApply;
                float monstrousBiteChanceToStop = 1f - monstrousBiteChanceToApplyFirst;

                float monstrousBiteAverageIncTime = 1f - monstrousBiteChanceToStop == 0 ? 0 : monstrousBiteUseFreq;
                float monstrousBiteAverageStackTime = monstrousBiteAverageIncTime + monstrousBiteDuration * monstrousBiteChanceToStop;

                PetSkillStack[] stacks = new PetSkillStack[4];
                stacks[0] = new PetSkillStack();
                stacks[1] = new PetSkillStack();
                stacks[2] = new PetSkillStack();
                stacks[3] = new PetSkillStack();

                stacks[0].time_to_reach = 0;
                stacks[1].time_to_reach = 1 * monstrousBiteAverageIncTime;
                stacks[2].time_to_reach = 2 * monstrousBiteAverageIncTime;
                stacks[3].time_to_reach = 3 * monstrousBiteAverageIncTime;

                stacks[0].chance_to_max = 0;
                stacks[1].chance_to_max = monstrousBiteChanceToStop == 1 ? 0 : monstrousBiteChanceToStop;
                stacks[2].chance_to_max = monstrousBiteChanceToStop == 1 ? 0 : monstrousBiteChanceToStop * (1 - stacks[1].chance_to_max);
                stacks[3].chance_to_max = monstrousBiteChanceToStop == 1 ? 0 : 1 - (stacks[0].chance_to_max + stacks[1].chance_to_max + stacks[2].chance_to_max);

                stacks[0].time_spent = stacks[3].chance_to_max == 1 ? 0 : stacks[1].time_to_reach;
                stacks[1].time_spent = stacks[3].chance_to_max == 1 ? 0 : (stacks[1].time_to_reach == 0 ? 0 : monstrousBiteAverageStackTime) * (1 - (stacks[0].chance_to_max));
                stacks[2].time_spent = stacks[3].chance_to_max == 1 ? 0 : (stacks[2].time_to_reach == 0 ? 0 : monstrousBiteAverageStackTime) * (1 - (stacks[0].chance_to_max + stacks[1].chance_to_max));
                stacks[3].time_spent = monstrousBiteChanceToStop == 0 ? 1 : stacks[3].time_to_reach == 0 ? 0 : 1 / monstrousBiteChanceToStop * monstrousBiteUseFreq * (1 - (stacks[0].chance_to_max + stacks[1].chance_to_max + stacks[2].chance_to_max));

                float monstrousBiteTotalTime = stacks[0].time_spent + stacks[1].time_spent + stacks[2].time_spent + stacks[3].time_spent;

                stacks[0].percent_time = monstrousBiteTotalTime == 0 ? 1 : stacks[0].time_spent / monstrousBiteTotalTime;
                stacks[1].percent_time = monstrousBiteTotalTime == 0 ? 0 : stacks[1].time_spent / monstrousBiteTotalTime;
                stacks[2].percent_time = monstrousBiteTotalTime == 0 ? 0 : stacks[2].time_spent / monstrousBiteTotalTime;
                stacks[3].percent_time = monstrousBiteTotalTime == 0 ? 0 : stacks[3].time_spent / monstrousBiteTotalTime;

                stacks[0].total = 0f;
                stacks[1].total = 0.03f * stacks[1].percent_time;
                stacks[2].total = 0.06f * stacks[1].percent_time;
                stacks[3].total = stacks[3].chance_to_max == 1f ? 0.09f : 0.09f * stacks[1].percent_time;

                float monstrousBiteProcEffect = stacks[0].total + stacks[1].total + stacks[2].total + stacks[3].total;

                damageAdjustMonstrousBite = 1f + monstrousBiteProcEffect;
            }
            #endregion
            float damageAdjustMangle = 1f + PetStats.BonusBleedDamageMultiplier;

            // Pets don't get ArP Ratings passed, spreadsheet agrees and no mention of it in the community
            float damageAdjustMitigation = 1f - StatConversion.GetArmorDamageReduction(BossOpts.Level, BossOpts.Armor, 0f, 0f, 0f);

            float damageAdjustBase = 1f
                    * (1f + PetStats.BonusDamageMultiplier)
                    * (1f + PetStats.BonusPetDamageMultiplier)
                    //* damageAdjustFerociousInspiration
                    * damageAdjustMonstrousBite
                    * damageAdjustSavageRend
                    * damageAdjustFeedingFrenzy
                    //* damageAdjustTargetDebuffs
                    * damageAdjustPetFamily;

            float damageAdjustDots     = damageAdjustBase; // BasePetModifier
            //float damageAdjustWhite    = damageAdjustBase * damageAdjustHitCritMelee;
            //float damageAdjustMelee    = damageAdjustBase * damageAdjustHitCritSpecials; // MeleeAttackAdjustment
            //float damageAdjustSpecials = damageAdjustBase * damageAdjustHitCritSpecials * damageAdjustMarkedForDeath; // DamageAdjustment
            //float damageAdjustMagic    = damageAdjustBase * damageAdjustMarkedForDeath / damageAdjustTargetDebuffs; // MagicDamageAdjustments

            #endregion

            // damage
            #region White Damage
            float whiteDamageBase = (52f + 78f) / 2f;
            float whiteDamageFromAP = (float)Math.Floor(PetStats.AttackPower / 14f * 2f);
            float whiteDamageNormal = whiteDamageBase + whiteDamageFromAP;
            //float whiteDamageAdjust = damageAdjustWhite * /*damageAdjustCobraReflexes **/ damageAdjustMitigation * damageAdjustGlancingBlows;
            //float whiteDamageReal = whiteDamageNormal * whiteDamageAdjust;

            //float whiteDPS = whiteDamageReal / attackSpeedEffective;

            //calculatedStats.petWhiteDPS = whiteDPS;
            #endregion
            #region Priority Rotation
            // loop over each skill, figuring out the damage value
            foreach (PetSkillInstance S in priorityRotation.skills) {
                S.damage = 0;

                #region Skill Groups
                if (S.skillData.type == PetSkillType.FocusDump) {
                    float focusDumpDamageAverage = ((118f + 168f) / 2f) + damageBonusMeleeFromAP;
                    S.damage = focusDumpDamageAverage * /*damageAdjustSpecials **/ damageAdjustMitigation;
                }
                if (S.skillData.type == PetSkillType.SpecialMelee) {
                    float meleeDamageAverage = S.skillData.average + damageBonusMeleeFromAP;
                    S.damage = meleeDamageAverage * /*damageAdjustSpecials **/ damageAdjustMitigation;
                }
                if (S.skillData.type == PetSkillType.SpecialSpell) {
                    float spellDamageAverage = S.skillData.average + damageBonusSpellsFromAP;
                    S.damage = spellDamageAverage * critSpecialsAdjust * fullResistDamageAdjust * partialResistDamageAdjust;
                                //* damageAdjustMagic * calculatedStats.targetDebuffsNature;
                }
                #endregion
                #region Unique Skills
                if (S.skillType == PetAttacks.Rake) {
                    float rakeDamageFromAP = PetStats.AttackPower * 0.0175f;
                    float rakeAverageDamage = ((47  + 67) / 2) + rakeDamageFromAP;
                    float rakeAverageDamageDot = ((19 + 25) / 2) + rakeDamageFromAP;

                    //float rakeInitialHitDamage = rakeAverageDamage * damageAdjustSpecials * damageAdjustMangle; // spreadsheet doesn't add armor mitigation, appears to be wow bug?
                    float rakeDotDamage = rakeAverageDamageDot * damageAdjustMangle * damageAdjustDots;
                    float rakeDots = S.frequency > 9 ? 3 : 2;

                    S.damage = /*rakeInitialHitDamage +*/ rakeDotDamage * rakeDots;
                }else if(S.skillType == PetAttacks.FireBreath) {
                    float fireBreathDamageAverage = ((43 + 57) / 2) + damageBonusSpellsFromAP;
                    float fireBreathDamageInitial = fireBreathDamageAverage * fullResistDamageAdjust * partialResistDamageAdjust;//* damageAdjustMagic;
                    float fireBreathDamageDot = 50 + damageBonusSpellsFromAP;
                    S.damage = fireBreathDamageInitial + fireBreathDamageDot;
                }else if (S.skillType == PetAttacks.SavageRend) {
                    float savageRendAverageHitDamage = ((59 + 83) / 2) + damageBonusMeleeFromAP;
                    float savageRendAverageBleedDamage = ((21 + 27) / 2) + damageBonusMeleeFromAP;
                    
                    float savageRendHitDamage = savageRendAverageHitDamage * /*damageAdjustSpecials **/ damageAdjustMangle * damageAdjustMitigation;
                    float savageRendBleedDamage = savageRendAverageBleedDamage * damageAdjustDots * damageAdjustMangle;

                    S.damage = savageRendHitDamage + 3 * savageRendBleedDamage;
                }else if (S.skillType == PetAttacks.ScorpidPoison) {
                    float scorpidPoisionDamageAverage = ((100 + 130) / 2) + damageBonusSpellsFromAP;
                    float scorpidPoisionDamageNormal = scorpidPoisionDamageAverage * fullResistDamageAdjust * partialResistDamageAdjust;
                                                        //* damageAdjustMagic * calculatedStats.targetDebuffsNature;
                    float scorpidPoisionDamageTick = scorpidPoisionDamageNormal / 5;
                    float scorpidPoisionTicks = (float)Math.Floor(S.cooldown / 2f);
                    S.damage = scorpidPoisionDamageTick * scorpidPoisionTicks;
                }else if (S.skillType == PetAttacks.PoisonSpit) {
                    float poisonSpitDamageAverage = ((104 + 136) / 2) + damageBonusSpellsFromAP;
                    float poisonSpitDamageNormal = poisonSpitDamageAverage * fullResistDamageAdjust * partialResistDamageAdjust ;
                                                    //* damageAdjustMagic * calculatedStats.targetDebuffsNature;
                    float poisonSpitDamageTick = poisonSpitDamageNormal / 3;
                    float poisonSpitTicks = S.cooldown >= 9 ? 3 : 2;
                    S.damage = poisonSpitDamageTick * poisonSpitTicks;
                }else if (S.skillType == PetAttacks.VenomWebSpray) {
                    float venomWebSprayDamageAverage = ((46 + 68) / 2) + damageBonusSpellsFromAP;
                    float venomWebSprayDamageNormal = venomWebSprayDamageAverage * fullResistDamageAdjust * partialResistDamageAdjust;
                                                        //* damageAdjustMagic * calculatedStats.targetDebuffsNature;
                    S.damage = venomWebSprayDamageNormal * 4;
                }else if (S.skillType == PetAttacks.SpiritStrike) {
                    float spiritStrikeAdjust = fullResistDamageAdjust * partialResistDamageAdjust;
                                                //* damageAdjustMagic * calculatedStats.targetDebuffsNature;

                    float spiritStrikeInitialDamageAverage = ((49 + 65) / 2) + damageBonusSpellsFromAP;
                    float spiritStrikeInitialDamageNormal = spiritStrikeInitialDamageAverage * spiritStrikeAdjust;// * damageAdjustHitCritSpecials;

                    float spiritStrikeDotDamageAverage = ((49 + 65) / 2) + damageBonusSpellsFromAP;
                    float spiritStrikeDotDamageNormal = spiritStrikeDotDamageAverage * spiritStrikeAdjust;

                    S.damage = spiritStrikeInitialDamageNormal + spiritStrikeDotDamageNormal;
                }else if (S.skillType == PetAttacks.SporeCloud) {
                    float spellDamageAverage = S.skillData.average + damageBonusSpellsFromAP;
                    S.damage = spellDamageAverage * fullResistDamageAdjust * partialResistDamageAdjust;
                                    //* damageAdjustMagic * calculatedStats.targetDebuffsNature;
                }else if (S.skillType == PetAttacks.AcidSpit) {
                    float acidSpitAverage = S.skillData.average + damageBonusSpellsFromAP;
                    S.damage = acidSpitAverage * /* (calculatedStats.petCritTotalSpecials * 1.5f + 1) **/ fullResistDamageAdjust * partialResistDamageAdjust /** damageAdjustMagic * calculatedStats.targetDebuffsNature*/;
                }
                #endregion

                S.CalculateDPS();
            }
            // now add everything up...
            priorityRotation.calculateDPS();
            #endregion
            #region Kill Command

            float killCommandDPS = 0;

            if (killCommandCooldown > 0)
            {
                float killCommandDamagePerSpecial = priorityRotation.petSpecialFrequency > 0 ? priorityRotation.kc_dps / (1 / priorityRotation.petSpecialFrequency) : 0;

                float killCommandBonusDamage = killCommandDamagePerSpecial * 1.2f;

                float killCommandDPSOverCooldown = killCommandCooldown > 0 ? killCommandBonusDamage / killCommandCooldown : 0;

#if !RAWR4
                float killCommandFocusedFireCritBonus = character.HunterTalents.FocusedFire * 0.1f;
#else
                float killCommandFocusedFireCritBonus = 0f;
#endif
                // 31-10-2009 Drizz: Remade the AdjustedBonus Calculation from the one below
                // float killCommandAdjustedBonus = killCommandFocusedFireCritBonus *  damageAdjustDodge * damageAdjustMitigation;
                float killCommandAdjustedBonus = killCommandFocusedFireCritBonus
                                               * (2f - 1f);
                                               //* (1f - calculatedStats.petTargetArmorReduction);

                killCommandDPS = killCommandDPSOverCooldown * (1f + killCommandAdjustedBonus);
            }

            //calculatedStats.petKillCommandDPS = killCommandDPS;

            #endregion

            //calculatedStats.petSpecialDPS = priorityRotation.dps;

            calculatedStats.PetDpsPoints = 0f;//(float)(calculatedStats.petWhiteDPS 
                                           //     + calculatedStats.petSpecialDPS 
                                           //     + calculatedStats.petKillCommandDPS);
        }
        public void GenPetStats()
        {
            // Initial Variables
            int levelDiff = BossOpts.Level - character.Level;
            StatsHunter petStatsBase = BasePetStats;
            #region From Hunter
            StatsHunter petStatsFromHunter = new StatsHunter() {
                AttackPower = (HunterStats.RangedAttackPower * 0.424f),
                SpellPower = HunterStats.RangedAttackPower * 0.211807381f,
                Stamina = HunterStats.Stamina,
                Agility = HunterStats.Agility,
                CritRating = HunterStats.CritRating,
                Strength = HunterStats.Strength,
                Spirit = HunterStats.PetSpirit,
                PhysicalHaste = HunterStats.PhysicalHaste,
                ArcaneResistance = HunterStats.ArcaneResistance * 0.4f,
                FireResistance   = HunterStats.FireResistance   * 0.4f,
                NatureResistance = HunterStats.NatureResistance * 0.4f,
                ShadowResistance = HunterStats.ShadowResistance * 0.4f,
                FrostResistance  = HunterStats.FrostResistance  * 0.4f,
                Armor = HunterStats.Armor * 0.7f,
                SpellPenetration = HunterStats.SpellPenetration,
                Resilience = HunterStats.Resilience,
                BonusDamageMultiplier = ((HunterStats.BonusDamageMultiplier /* / (1f + Talents.TheBeastWithin * 0.10f)*/))
                                      * ((character.Race == CharacterRace.Orc ? 0.05f : 0f)),
                BonusPetDamageMultiplier = HunterStats.BonusPetDamageMultiplier,
                BonusBleedDamageMultiplier = HunterStats.BonusBleedDamageMultiplier,
                BonusPetAttackPowerMultiplier = HunterStats.BonusPetAttackPowerMultiplier,
//                PetAttackPower = HunterStats.PetAttackPower,
            };
            #endregion
            #region From Talents (Pet or Hunter)
            Stats petStatsTalents = new Stats() {
                BonusStaminaMultiplier = PetTalents.GreatStamina * 0.04f,
                MovementSpeed = PetTalents.BoarsSpeed * 0.30f,
                PhysicalHaste = PetTalents.SerpentSwiftness * 0.05f,
                PhysicalCrit = PetTalents.SpidersBite * 0.03f
                             ,
                BaseArmorMultiplier = (1f + PetTalents.NaturalArmor * 0.05f)
                                    * (1f + PetTalents.PetBarding * 0.05f)
                                    * (1.05f) // Base 5% Armor Bonus
                                    - 1f,
                Dodge = PetTalents.PetBarding * 0.01f,
                CritChanceReduction = PetTalents.GraceOfTheMantis * 0.03f,
                ArcaneResistance = PetTalents.GreatResistance * 0.05f,
                FireResistance = PetTalents.GreatResistance * 0.05f,
                NatureResistance = PetTalents.GreatResistance * 0.05f,
                ShadowResistance = PetTalents.GreatResistance * 0.05f,
                FrostResistance = PetTalents.GreatResistance * 0.05f,
                FearDurReduc = PetTalents.Lionhearted * 0.15f,
                StunDurReduc = PetTalents.Lionhearted * 0.15f,
                BonusDamageMultiplier = (1 + (PetTalents.SharkAttack * 0.03f)) * (1.05f) - 1f, // Base 5% Damage
                //BonusAttackPowerMultiplier = calculatedStats.aspectBonusAPBeast,
                BonusHealthMultiplier = 0.05f, // Base 5% Health
            };
            float LongevityCdAdjust = 1f - Talents.Longevity * 0.10f;
            if (PetTalents.Rabid > 0) {
                float rabidCooldown = 45f * LongevityCdAdjust;
                SpecialEffect primary = new SpecialEffect(Trigger.Use, new Stats() { }, 20f, rabidCooldown);
                SpecialEffect secondary = new SpecialEffect(Trigger.MeleeHit,
                    new Stats() { BonusAttackPowerMultiplier = 0.05f }, 20f, 0f, 0.50f, 5);
                primary.Stats.AddSpecialEffect(secondary);
                petStatsTalents.AddSpecialEffect(primary);
            }
            if (Talents.Frenzy > 0) {
                if (frenzy == null) {
                    frenzy = new SpecialEffect(Trigger.MeleeCrit, new Stats() { PhysicalHaste = 0.30f, }, 8f, 1f, Talents.Frenzy * 0.20f);
                }
                petStatsTalents.AddSpecialEffect(frenzy);
            }
            if (PetTalents.LastStand > 0) {
                SpecialEffect laststand = new SpecialEffect(Trigger.Use, new Stats() { BonusHealthMultiplier = 0.30f, }, 20f, (1f * 60f) * LongevityCdAdjust);
                petStatsTalents.AddSpecialEffect(laststand);
            }
            #endregion
            #region From Options
            Stats petStatsOptionsPanel = new Stats() {
                PhysicalCrit = StatConversion.NPC_LEVEL_CRIT_MOD[levelDiff],
                //BonusStaminaMultiplier = 0.05f,
            };
            CalculateTimings();
            if(priorityRotation.getSkillFrequency(PetAttacks.SerenityDust) > 0){
                // TODO: Need to make sure this freq actually works
                float freq = priorityRotation.getSkillFrequency(PetAttacks.SerenityDust);
                SpecialEffect serenitydust = new SpecialEffect(Trigger.Use,
                    new Stats() { BonusAttackPowerMultiplier = 0.10f, },
                    15f, BossOpts.BerserkTimer / freq);
                petStatsOptionsPanel.AddSpecialEffect(serenitydust);
                petStatsOptionsPanel.HealthRestore += (BossOpts.BerserkTimer / freq) * 825f;
            }
            #endregion

            // Totals
//            Stats petStatsGearEnchantsBuffs = new Stats();
//            petStatsGearEnchantsBuffs.Accumulate(petStatsBuffs);
            StatsHunter petStatsTotal = new StatsHunter();
            petStatsTotal.Accumulate(petStatsBase);
            petStatsTotal.Accumulate(petStatsFromHunter);
//            petStatsTotal.Accumulate(petStatsBuffs);
            petStatsTotal.Accumulate(petStatsTalents);
            petStatsTotal.Accumulate(petStatsOptionsPanel);
            StatsHunter petStatsProcs = new StatsHunter();

            Dictionary<Trigger, float> triggerIntervals = new Dictionary<Trigger, float>();
            Dictionary<Trigger, float> triggerChances = new Dictionary<Trigger, float>();

            #region Stamina & Health
            float totalBSTAM = petStatsTotal.BonusStaminaMultiplier;
            //calculatedStats.petBaseHealth = BasePetStats.Health;
            //calculatedStats.petHealthfromStamina = (petStatsFromHunter.Stamina) * 10.4173919f;
            petStatsTotal.Stamina = (float)Math.Floor((1f + totalBSTAM) * petStatsTotal.Stamina);
            
            // Health | all pets have a 5% Bonus Stam
            petStatsTotal.Health += (petStatsTotal.Stamina) * 10.4173919f;
            petStatsTotal.Health *= 1f + petStatsTotal.BonusHealthMultiplier;
            //calculatedStats.petBonusHealth = petStatsTotal.Health - calculatedStats.petBaseHealth - calculatedStats.petHealthfromStamina;

            if (PetTalents.Bloodthirsty > 0) {
                SpecialEffect bloodthirsty = new SpecialEffect(Trigger.MeleeHit,
                    new Stats() { HealthRestore = petStatsTotal.Health * 0.05f },
                    0f, 0f, PetTalents.Bloodthirsty * 0.10f);
                petStatsTotal.AddSpecialEffect(bloodthirsty);
            }
            #endregion

            #region Strength
            float totalBSTRM = petStatsTotal.BonusStrengthMultiplier;
            petStatsTotal.Strength = /*(float)Math.Floor(*/(1f + totalBSTRM) * petStatsTotal.Strength/*)*/;
            #endregion

            #region Agility
            float totalBAGIM = petStatsTotal.BonusAgilityMultiplier;
            petStatsTotal.Agility = /*(float)Math.Floor(*/(1f + totalBAGIM) * petStatsTotal.Agility/*)*/;
            #endregion

            #region Attack Power
            petStatsTotal.BonusAttackPowerMultiplier *= (1f + petStatsTotal.BonusPetAttackPowerMultiplier);
            float totalBAPM    = petStatsTotal.BonusAttackPowerMultiplier;

            float apFromBase   = (1f + totalBAPM) * BasePetStats.PetAttackPower;
//            float apFromBonus  = (1f + totalBAPM) * (petStatsTotal.PetAttackPower - BasePetStats.PetAttackPower);

            float apFromHunter = ((1f + totalBAPM) * petStatsFromHunter.AttackPower);
//            float apFromSTR    = ((1f + totalBAPM) * (petStatsFromHunter.Strength)) * 0.85f;//(petStatsTotal.Strength - 10f) * 2f;
//            float apFromHvW    = 0f; //(1f + totalBAPM) * (HunterStats.Stamina * 0.10f * Talents.HunterVsWild);

            petStatsTotal.AttackPower = apFromBase /*+ apFromBonus*/ + apFromHunter /*+ apFromSTR + apFromHvW*/;
            #endregion

            #region Haste
            /*if (StatsPetBuffs._rawSpecialEffectData != null && StatsPetBuffs._rawSpecialEffectData.Length > 0) {
                Stats add = StatsPetBuffs._rawSpecialEffectData[0].GetAverageStats();
                StatsPetBuffs.PhysicalHaste *= (1f + add.PhysicalHaste);
            }*/
            #endregion

            #region Armor
            petStatsTotal.Armor = (float)Math.Floor((petStatsTotal.Armor - petStatsFromHunter.Armor) * (1f + petStatsTotal.BaseArmorMultiplier) + petStatsFromHunter.Armor);
//            petStatsTotal.BonusArmor += petStatsTotal.Agility * 2f;
            petStatsTotal.BonusArmor = (float)Math.Floor(petStatsTotal.BonusArmor * (1f + petStatsTotal.BonusArmorMultiplier));
            petStatsTotal.Armor += petStatsTotal.BonusArmor;
            #endregion

            #region Hit/Dodge Chances
            // This is tied directly to the Hunter's chance to miss
            PetChanceToMiss = Math.Max(0f, StatConversion.YELLOW_MISS_CHANCE_CAP[levelDiff] - HunterStats.PhysicalHit);
            PetChanceToSpellMiss = -1f * Math.Min(0f, StatConversion.GetSpellMiss(levelDiff, false) - HunterStats.SpellHit);

            //calculatedStats.petHitTotal = HunterStats.PhysicalHit;
            //calculatedStats.petHitSpellTotal = HunterStats.PhysicalHit * 17f / 8f;

            //petStatsTotal.PhysicalHit = calculatedStats.petHitTotal;
            //petStatsTotal.SpellHit = calculatedStats.petHitSpellTotal;

            // If the Hunter is Hit Capped, the pet is also Exp Capped
            // If not, Pet is proportionately lower based on Hunter's Hit
            // Expertise itself doesn't factor in at all
            PetChanceToBeDodged = StatConversion.YELLOW_DODGE_CHANCE_CAP[levelDiff]
                                * Math.Min(1f, (PetChanceToMiss / StatConversion.YELLOW_MISS_CHANCE_CAP[levelDiff]));
            //calculatedStats.petTargetDodge = PetChanceToBeDodged;

            float[] avoidChances = { PetChanceToMiss, PetChanceToSpellMiss, PetChanceToBeDodged };
            #endregion

            #region Crit Chance
            petStatsTotal.PhysicalCrit += StatConversion.GetCritFromAgility(petStatsTotal.Agility, CharacterClass.Warrior)
                                        + StatConversion.GetCritFromRating(petStatsTotal.CritRating);

            //calculatedStats.petCritTotalMelee = petStatsTotal.PhysicalCrit;

            // Cobra Strikes
            //calculatedStats.petCritFromCobraStrikes = 0;
            float cobraStrikesProc = Talents.CobraStrikes * 0.2f;
            if (cobraStrikesProc > 0)
            {
                #if FALSE
                float cobraStrikesCritFreqSteady = calculatedStats.steadyShot.Freq * (1f / calculatedStats.steadyShot.CritChance);
                float cobraStrikesCritFreqArcane = calculatedStats.arcaneShot.Freq * (1f / calculatedStats.arcaneShot.CritChance);
                float cobraStrikesCritFreqKill = calculatedStats.killShot.Freq * (1f / calculatedStats.killShot.CritChance);

                float cobraStrikesCritFreqSteadyInv = cobraStrikesCritFreqSteady > 0 ? 1f / cobraStrikesCritFreqSteady : 0;
                float cobraStrikesCritFreqArcaneInv = cobraStrikesCritFreqArcane > 0 ? 1f / cobraStrikesCritFreqArcane : 0;
                float cobraStrikesCritFreqKillInv = cobraStrikesCritFreqKill > 0 ? 1f / cobraStrikesCritFreqKill : 0;
                float cobraStrikesCritFreqInv = cobraStrikesCritFreqSteadyInv + cobraStrikesCritFreqArcaneInv + cobraStrikesCritFreqKillInv;
                float cobraStrikesCritFreq = cobraStrikesCritFreqInv > 0 ? 1f / cobraStrikesCritFreqInv : 0;
                float cobraStrikesProcAdjust = cobraStrikesCritFreq / cobraStrikesProc;

                float cobraStrikesFreqSteadyInv = calculatedStats.steadyShot.Freq > 0 ? 1f / calculatedStats.steadyShot.Freq : 0;
                float cobraStrikesFreqArcaneInv = calculatedStats.arcaneShot.Freq > 0 ? 1f / calculatedStats.arcaneShot.Freq : 0;
                float cobraStrikesFreqKillInv = calculatedStats.killShot.Freq > 0 ? 1f / calculatedStats.killShot.Freq : 0;
                float cobraStrikesFreqInv = cobraStrikesFreqSteadyInv + cobraStrikesFreqArcaneInv + cobraStrikesFreqKillInv;

                float cobraStrikesTwoSpecials = 2f * priorityRotation.petSpecialFrequency;
                float cobraStrikesUptime = 1f - (float)Math.Pow(1f - calculatedStats.steadyShot.CritChance * cobraStrikesProc, cobraStrikesFreqInv * cobraStrikesTwoSpecials);

                //calculatedStats.petCritFromCobraStrikes = (cobraStrikesUptime + (1f - cobraStrikesUptime) * calculatedStats.petCritTotalMelee) - calculatedStats.petCritTotalMelee;
                #endif

            }

            //calculatedStats.petCritTotalSpecials = petStatsTotal.PhysicalCrit + calculatedStats.petCritFromCobraStrikes; // PetCritChance
            //critSpecialsAdjust = calculatedStats.petCritTotalSpecials * 1.5f + 1f;
            #endregion

            #region Handle Special Effects
            CalculateTimings();
            WhAtkTable = new PetAttackTable(character, petStatsTotal, CalcOpts, avoidChances, false, false);
            YwAtkTable = new PetAttackTable(character, petStatsTotal, CalcOpts, avoidChances, PetAttacks.Claw, false, false);

            float AllAttemptedAtksInterval = GenPetFullAttackSpeed(petStatsTotal);
            float WhtAttemptedAtksInterval = PetAttackSpeed(petStatsTotal);
            float YlwAttemptedAtksInterval = AllAttemptedAtksInterval - WhtAttemptedAtksInterval;

            float[] hitRates  = { WhAtkTable.AnyLand,   // Whites
                                  YwAtkTable.AnyLand }; // Yellows
            float[] critRates = { WhAtkTable.Crit, // Whites
                                  YwAtkTable.Crit /*+ calculatedStats.petCritFromCobraStrikes*/ }; // Yellows

            float bleedHitInterval = 0f;
            float rakefreq = priorityRotation.getSkillFrequency(PetAttacks.Rake ); if (rakefreq > 0) { bleedHitInterval      += rakefreq; }

            float dmgDoneInterval = 1f; // Need to Fix this

            float clawbitesmackinterval = 0f;
            float clawfreq = priorityRotation.getSkillFrequency(PetAttacks.Claw ); if (clawfreq > 0) { clawbitesmackinterval += clawfreq; }
            float bitefreq = priorityRotation.getSkillFrequency(PetAttacks.Bite ); if (bitefreq > 0) { clawbitesmackinterval += bitefreq; }
            float smakfreq = priorityRotation.getSkillFrequency(PetAttacks.Smack); if (smakfreq > 0) { clawbitesmackinterval += smakfreq; }
            PetClawBiteSmackInterval = clawbitesmackinterval;

            float[] AttemptedAtkIntervals = {
                AllAttemptedAtksInterval, // All
                WhtAttemptedAtksInterval, // Whites
                YlwAttemptedAtksInterval, // Yellows
                PetClawBiteSmackInterval, // ClawBiteSmack
            };

            petStatsProcs.Accumulate(GetSpecialEffectsStats(character, triggerIntervals, triggerChances, AttemptedAtkIntervals, hitRates, critRates,
                                    bleedHitInterval, dmgDoneInterval, petStatsTotal, null) as StatsHunter);

            #region Stat Results of Special Effects
            // Base Stats
            petStatsProcs.Stamina  = (float)Math.Floor(petStatsProcs.Stamina  * (1f + totalBSTAM) * (1f + petStatsProcs.BonusStaminaMultiplier));
            petStatsProcs.Strength = (float)Math.Floor(petStatsProcs.Strength * (1f + totalBSTRM) * (1f + petStatsProcs.BonusStrengthMultiplier));
            petStatsProcs.Agility  = (float)Math.Floor(petStatsProcs.Agility  * (1f + totalBAGIM) * (1f + petStatsProcs.BonusAgilityMultiplier));
            petStatsProcs.Health  += (float)Math.Floor(petStatsProcs.Stamina  * 10f);
            petStatsProcs.Health  += (float)Math.Floor((petStatsProcs.Health + petStatsTotal.Health) * petStatsProcs.BonusHealthMultiplier);

            // Armor
            petStatsProcs.Armor = (float)Math.Floor(petStatsProcs.Armor * (1f + petStatsTotal.BaseArmorMultiplier + petStatsProcs.BaseArmorMultiplier));
            petStatsProcs.BonusArmor += petStatsProcs.Agility * 2f;
            petStatsProcs.BonusArmor = (float)Math.Floor(petStatsProcs.BonusArmor * (1f + petStatsTotal.BonusArmorMultiplier + petStatsProcs.BonusArmorMultiplier));
            petStatsProcs.Armor += petStatsProcs.BonusArmor;
            petStatsProcs.BonusArmor = 0; //it's been added to Armor so kill it

            // Attack Power
            petStatsProcs.BonusAttackPowerMultiplier *= (1f + petStatsProcs.BonusPetAttackPowerMultiplier);
            float totalBAPMProcs    = (1f + totalBAPM) * (1f + petStatsProcs.BonusAttackPowerMultiplier) - 1f;
            float apFromSTRProcs    = (1f + totalBAPMProcs) * (petStatsProcs.Strength * 2f);
            float apBonusOtherProcs = (1f + totalBAPMProcs) * (petStatsProcs.AttackPower + petStatsProcs.PetAttackPower);
            float apBonusFromBasetoNewMulti = (petStatsProcs.BonusAttackPowerMultiplier) * (petStatsTotal.AttackPower);
            petStatsProcs.AttackPower = (float)Math.Floor(apFromSTRProcs + apBonusOtherProcs + apBonusFromBasetoNewMulti);

            // Crit
            petStatsProcs.PhysicalCrit += StatConversion.GetCritFromAgility(petStatsProcs.Agility, CharacterClass.Warrior);
            //petStatsProcs.PhysicalCrit += StatConversion.GetCritFromRating(petStatsProcs.CritRating, CharacterClass.Warrior);

            #endregion
            petStatsTotal.Accumulate(petStatsProcs);

            GenPetFullAttackSpeed(petStatsTotal);
            CalculateTimings();
            clawbitesmackinterval = 0f;
            clawfreq = priorityRotation.getSkillFrequency(PetAttacks.Claw); if (clawfreq > 0) { clawbitesmackinterval += clawfreq; }
            bitefreq = priorityRotation.getSkillFrequency(PetAttacks.Bite); if (bitefreq > 0) { clawbitesmackinterval += bitefreq; }
            smakfreq = priorityRotation.getSkillFrequency(PetAttacks.Smack); if (smakfreq > 0) { clawbitesmackinterval += smakfreq; }
            PetClawBiteSmackInterval = clawbitesmackinterval;
            #endregion

            PetStats = petStatsTotal;

            #region Special Abilities Priority Rotation
            CalculateTimings();
            #endregion
            #region Kill Command MPS
            //calculatedStats.petKillCommandMPS = 0;
            killCommandCooldown = 0;
            if (CalcOpts.useKillCommand) {
                float killCommandManaCost = 40f /* * calculatedStats.baseMana*/;

//                float killCommandReadinessFactor = calculatedStats.priorityRotation.containsShot(Shots.Readiness) ? 1.0f / 180f : 0f;
                float killCommandCooldownBase = 1.0f;

//                killCommandCooldown = 1.0f / (killCommandCooldownBase + killCommandReadinessFactor);

                //calculatedStats.petKillCommandMPS = killCommandCooldown > 0 ? killCommandManaCost / killCommandCooldown : 0;
            }
            #endregion
            #region Target Debuffs
            float armorDebuffSporeCloud = 0;
            float sporeCloudFrequency = priorityRotation.getSkillFrequency(PetAttacks.SporeCloud);
            if (sporeCloudFrequency > 0)
            {
                float sporeCloudDuration = 9f;
                float sporeCloudUptime = sporeCloudDuration > sporeCloudFrequency ? 1f : sporeCloudDuration / sporeCloudFrequency;

                armorDebuffSporeCloud = sporeCloudUptime * 0.03f;
            }

            float armorDebuffAcidSpit = 0;
            float acidSpitFrequency = priorityRotation.getSkillFrequency(PetAttacks.AcidSpit); // AcidSpitEffectiveRate
            if (acidSpitFrequency > 0)
            {
                float acidSpitCalcFreq = priorityRotation.getSkillCooldown(PetAttacks.AcidSpit);
                float acidSpitDuration = 30;

                float acidSpitChanceToApply = 1f - PetChanceToMiss - PetChanceToBeDodged; // V45
                float acidSpitChancesToMaintain = (float)Math.Floor((acidSpitDuration - 1f) / acidSpitFrequency); // V46
                float acidSpitChanceToApplyFirst = acidSpitChancesToMaintain == 0 ? 0 : acidSpitChanceToApply; // V47
                float acidSpitChanceToStop = 1f - acidSpitChanceToApplyFirst; // AcidSpitChanceToStop
                float acidSpitAverageTimeToInc = acidSpitChanceToApplyFirst > 0 ? acidSpitFrequency : 0; // AcidSpitTimeToInc
                float acidSpitTimeSpentAtMax = acidSpitAverageTimeToInc + (acidSpitDuration * acidSpitChanceToStop); // AcidSpitAverageStackTime

                PetSkillStack[] stacks = new PetSkillStack[3];
                stacks[0] = new PetSkillStack();
                stacks[1] = new PetSkillStack();
                stacks[2] = new PetSkillStack();

                stacks[0].time_to_reach = 0;
                stacks[1].time_to_reach = acidSpitAverageTimeToInc * 1;
                stacks[2].time_to_reach = acidSpitAverageTimeToInc * 2;

                stacks[0].chance_to_max = 0;
                stacks[1].chance_to_max = acidSpitChanceToStop == 1 ? 0 : acidSpitChanceToStop;
                stacks[2].chance_to_max = acidSpitChanceToStop == 1 ? 0 : 1 - (stacks[0].chance_to_max + stacks[1].chance_to_max);

                stacks[0].time_spent = stacks[1].time_to_reach;
                stacks[1].time_spent = stacks[1].time_to_reach == 0 ? 0 : acidSpitTimeSpentAtMax * (1 - stacks[0].chance_to_max);
                stacks[2].time_spent = stacks[1].time_to_reach == 0 ? 0 : acidSpitChanceToStop == 0 ? 1 : 1 / acidSpitChanceToStop * acidSpitCalcFreq * (1-(stacks[0].chance_to_max + stacks[1].chance_to_max));

                float acidSpitTotalTime = stacks[0].time_spent + stacks[1].time_spent + stacks[2].time_spent;

                stacks[0].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[0].time_spent / acidSpitTotalTime;
                stacks[1].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[1].time_spent / acidSpitTotalTime;
                stacks[2].percent_time = acidSpitTotalTime == 0 ? 1 : stacks[2].time_spent / acidSpitTotalTime;

                stacks[0].total = stacks[0].percent_time * 0.0f;
                stacks[1].total = stacks[0].percent_time * 0.1f;
                stacks[2].total = stacks[0].percent_time * 0.2f;

                armorDebuffAcidSpit = stacks[0].total + stacks[1].total + stacks[2].total;
            }

            float armorDebuffSting = 0;
            float stingFrequency = priorityRotation.getSkillFrequency(PetAttacks.Sting);
            if (stingFrequency > 0) {
                float stingUseFreq = priorityRotation.getSkillFrequency(PetAttacks.Sting);
                float stingDuration = 20;
                float stingUptime = stingDuration > stingUseFreq ? 1 : stingDuration / stingUseFreq;

                armorDebuffSting = stingUptime * 0.05f;
            }

            // these local buffs can be overridden
            if (character.ActiveBuffsConflictingBuffContains("Sting")) {
                armorDebuffSporeCloud = 0;
                armorDebuffSting = 0;
            }
            if (character.ActiveBuffsConflictingBuffContains("Acid Spit")) {
                armorDebuffAcidSpit = 0;
            }

            //calculatedStats.petArmorDebuffs = 0f - (1f - armorDebuffSporeCloud) * (1f - armorDebuffAcidSpit) * (1f - armorDebuffSting) + 1;

            #endregion
            #region Hunter Effects
            // Ferocious Inspiraion
            // (Same as above)
            //calculatedStats.ferociousInspirationDamageAdjust = 1;
            if (character.HunterTalents.FerociousInspiration > 0) {
                if (CalcOpts.PetFamily != PETFAMILY.None)
                {
                    float ferociousInspirationSpecialsEffect = priorityRotation.petSpecialFrequency == 0 ? 0 : 10f / priorityRotation.petSpecialFrequency;
                    //float ferociousInspirationUptime = 1f - (float)Math.Pow(1f - calculatedStats.petCritTotalSpecials, (10f / attackSpeedEffective) + ferociousInspirationSpecialsEffect);
                    float ferociousInspirationEffect = 0.01f * character.HunterTalents.FerociousInspiration;

                    //calculatedStats.ferociousInspirationDamageAdjust = 1.0f + ferociousInspirationUptime * ferociousInspirationEffect;                    
                }
            }

            // Roar of Recovery
            //calculatedStats.manaRegenRoarOfRecovery = 0;
            float roarOfRecoveryFreq = priorityRotation.getSkillFrequency(PetAttacks.RoarOfRecovery);
            if (roarOfRecoveryFreq > 0) {
                float roarOfRecoveryUseCount = (float)Math.Ceiling(BossOpts.BerserkTimer / roarOfRecoveryFreq);
                float roarOfRecoveryManaRestored = HunterStats.Mana * 0.30f * roarOfRecoveryUseCount; // E129
                //calculatedStats.manaRegenRoarOfRecovery = roarOfRecoveryUseCount > 0 ? roarOfRecoveryManaRestored / BossOpts.BerserkTimer : 0;
            }

            //Invigoration
            //calculatedStats.manaRegenInvigoration = 0;
            float invigorationProcChance = Talents.Invigoration * 0.50f; // C32
            if (invigorationProcChance > 0) {
                float invigorationProcFreq = (priorityRotation.petSpecialFrequency /*/ calculatedStats.petCritTotalSpecials*/) / invigorationProcChance; //C35
                float invigorationEffect = Talents.Invigoration > 0 ? 0.01f : 0;
                float invigorationManaGainedPercent = invigorationProcFreq > 0 ? 60f / invigorationProcFreq * invigorationEffect : 0; // C36
                float invigorationManaPerMinute = invigorationProcFreq > 0 ? 60f / invigorationProcFreq * invigorationEffect * HunterStats.Mana : 0; // C37
                //calculatedStats.manaRegenInvigoration = invigorationManaPerMinute / 60f;
            }
            #endregion

            #region Target Armor Effect
            //31-10-2009 Drizz: added Armor effect
#if RAWR3 || RAWR4 || SILVERLIGHT
            //double petEffectiveArmor = BossOpts.Armor * (1f - calculatedStats.petArmorDebuffs);
            //calculatedStats.petTargetArmorReduction = StatConversion.GetArmorDamageReduction(character.Level, BossOpts.Armor, calculatedStats.petArmorDebuffs, 0, 0);
#else
            double petEffectiveArmor = CalcOpts.TargetArmor * (1f - calculatedStats.petArmorDebuffs);
            calculatedStats.petTargetArmorReduction = StatConversion.GetArmorDamageReduction(character.Level, CalcOpts.TargetArmor, calculatedStats.petArmorDebuffs, 0, 0);
#endif
            //petEffectiveArmor/(petEffectiveArmor - 22167.5f + (467.5f*80f));
            #endregion
        }