private FinalStatsAll GetFinalStats(Player p, int phaseIndex)
        {
            PhaseData      phase       = _statistics.Phases[phaseIndex];
            long           start       = phase.Start + _log.FightData.FightStart;
            long           end         = phase.End + _log.FightData.FightStart;
            FinalStatsAll  final       = new FinalStatsAll();
            HashSet <long> nonCritable = new HashSet <long>
            {
                9292,
                52370
            };

            foreach (DamageLog dl in p.GetJustPlayerDamageLogs(null, _log, phase.Start, phase.End))
            {
                if (dl.IsCondi == 0)
                {
                    if (dl.Result == ParseEnum.Result.Crit)
                    {
                        final.CriticalRate++;
                        final.CriticalDmg += dl.Damage;
                    }

                    if (dl.IsNinety > 0)
                    {
                        final.ScholarRate++;
                        final.ScholarDmg += (int)(dl.Damage / 11.0); //regular+10% damage
                    }

                    if (dl.IsMoving > 0)
                    {
                        final.MovingRate++;
                        final.MovingDamage += (int)(dl.Damage / 21.0);
                    }

                    final.FlankingRate += dl.IsFlanking;

                    if (dl.Result == ParseEnum.Result.Glance)
                    {
                        final.GlanceRate++;
                    }

                    if (dl.Result == ParseEnum.Result.Blind)
                    {
                        final.Missed++;
                    }

                    if (dl.Result == ParseEnum.Result.Interrupt)
                    {
                        final.Interrupts++;
                    }

                    if (dl.Result == ParseEnum.Result.Absorb)
                    {
                        final.Invulned++;
                    }
                    final.PowerLoopCount++;
                    if (!nonCritable.Contains(dl.SkillId))
                    {
                        final.CritablePowerLoopCount++;
                    }
                }
            }
            if (p.Account == ":Conjured Sword")
            {
                return(final);
            }
            foreach (CastLog cl in p.GetCastLogs(_log, phase.Start, phase.End))
            {
                if (cl.EndActivation == ParseEnum.Activation.CancelCancel)
                {
                    final.Wasted++;
                    final.TimeWasted += cl.ActualDuration;
                }
                if (cl.EndActivation == ParseEnum.Activation.CancelFire)
                {
                    final.Saved++;
                    if (cl.ActualDuration < cl.ExpectedDuration)
                    {
                        final.TimeSaved += cl.ExpectedDuration - cl.ActualDuration;
                    }
                }
            }
            final.TimeSaved  = final.TimeSaved / 1000f;
            final.TimeWasted = final.TimeWasted / 1000f;

            double avgBoons = 0;

            foreach (long duration in p.GetBoonPresence(_log, phaseIndex).Values)
            {
                avgBoons += duration;
            }
            final.AvgBoons = avgBoons / phase.GetDuration();

            double avgCondis = 0;

            foreach (long duration in p.GetCondiPresence(_log, phaseIndex).Values)
            {
                avgCondis += duration;
            }
            final.AvgConditions = avgCondis / phase.GetDuration();


            // Counts
            CombatData combatData = _log.CombatData;

            final.SwapCount  = combatData.GetStates(p.InstID, ParseEnum.StateChange.WeaponSwap, start, end).Count;
            final.DownCount  = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeDown, start, end).Count;
            final.DodgeCount = combatData.GetSkillCount(p.InstID, SkillItem.DodgeId, start, end) + combatData.GetBuffCount(p.InstID, 40408, start, end);//dodge = 65001 mirage cloak =40408

            if (_settings.ParseCombatReplay && _log.FightData.Logic.CanCombatReplay)
            {
                if (_statistics.StackCenterPositions == null)
                {
                    _statistics.StackCenterPositions = new List <Point3D>();
                    List <List <Point3D> > GroupsPosList = new List <List <Point3D> >();
                    foreach (Player player in _log.PlayerList)
                    {
                        if (player.Account == ":Conjured Sword")
                        {
                            continue;
                        }
                        GroupsPosList.Add(player.CombatReplay.GetActivePositions());
                    }
                    for (int time = 0; time < GroupsPosList[0].Count; time++)
                    {
                        float x             = 0;
                        float y             = 0;
                        float z             = 0;
                        int   activePlayers = GroupsPosList.Count;
                        foreach (List <Point3D> points in GroupsPosList)
                        {
                            Point3D point = points[time];
                            if (point != null)
                            {
                                x += point.X;
                                y += point.Y;
                                z += point.Z;
                            }
                            else
                            {
                                activePlayers--;
                            }
                        }
                        x = x / activePlayers;
                        y = y / activePlayers;
                        z = z / activePlayers;
                        _statistics.StackCenterPositions.Add(new Point3D(x, y, z, _settings.PollingRate * time));
                    }
                }
                List <Point3D> positions = p.CombatReplay.Positions.Where(x => x.Time >= phase.Start && x.Time <= phase.End).ToList();
                int            offset    = p.CombatReplay.Positions.Count(x => x.Time < phase.Start);
                if (positions.Count > 1)
                {
                    List <float> distances = new List <float>();
                    for (int time = 0; time < positions.Count; time++)
                    {
                        float deltaX = positions[time].X - _statistics.StackCenterPositions[time + offset].X;
                        float deltaY = positions[time].Y - _statistics.StackCenterPositions[time + offset].Y;
                        //float deltaZ = positions[time].Z - StackCenterPositions[time].Z;


                        distances.Add((float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY));
                    }
                    final.StackDist = distances.Sum() / distances.Count;
                }
                else
                {
                    final.StackDist = -1;
                }
            }

            List <CombatItem> dead = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeDead, start, end);
            List <CombatItem> up   = combatData.GetStates(p.InstID, ParseEnum.StateChange.ChangeUp, start, end);

            final.Died = 0.0;
            if (dead.Count > 0)
            {
                final.Died = up.Count > 0 && up.Last().Time > dead.Last().Time ? -dead.Count : dead.Last().Time - start;
            }

            List <CombatItem> disconnect = combatData.GetStates(p.InstID, ParseEnum.StateChange.Despawn, start, end);

            final.Dcd = 0.0;
            if (disconnect.Count > 0)
            {
                final.Dcd = disconnect.Last().Time - start;
            }
            return(final);
        }