private static KeySpeedGroup GetCharSpeedGroup(List <KeyStroke> ksGroup)
        {
            var charSpeedGroup = new KeySpeedGroup
            {
                CharCount      = ksGroup.Count,
                CharsPerSecond = 0,
                CharsPerMinute = 0,
                WordsPerMinute = 0,
                Elapsed        = new TimeSpan(),
                Start          = ksGroup[ksGroup.Count - 1].Created,
                Stop           = ksGroup[0].Created,
                KeyStrokes     = new List <KeyStroke>()
            };

            foreach (var ks in ksGroup)
            {
                charSpeedGroup.KeyStrokes.Add((KeyStroke)ks.Clone());
            }


            if (charSpeedGroup.Start != null && charSpeedGroup.Stop != null)
            {
                charSpeedGroup.Elapsed = charSpeedGroup.Stop.Value.Subtract(charSpeedGroup.Start.Value);
            }
            if (charSpeedGroup.Elapsed != null)
            {
                charSpeedGroup.CharsPerSecond = Math.Round(charSpeedGroup.CharCount / charSpeedGroup.Elapsed.Value.TotalSeconds, 2);
            }
            charSpeedGroup.CharsPerMinute = Math.Round(charSpeedGroup.CharsPerSecond * 60, 2);
            charSpeedGroup.WordsPerMinute = Math.Round(charSpeedGroup.CharsPerMinute / 5, 2);

            return(charSpeedGroup);
        }
        private static KeySpeedGroup GetAverageCharSpeedGroup(IReadOnlyList <KeySpeedGroup> charSpeedGroupList)
        {
            var charSpeedGroup = new KeySpeedGroup();

            if (charSpeedGroupList.Count > 1)
            {
                #region  |  get average  |
                var charsPerSecond = new List <double>();
                charSpeedGroup.KeyStrokes = new List <KeyStroke>();
                charSpeedGroup.Elapsed    = new TimeSpan();
                foreach (var keySpeedGroup in charSpeedGroupList)
                {
                    charSpeedGroup.Elapsed += keySpeedGroup.Elapsed;
                    charSpeedGroup.KeyStrokes.AddRange(keySpeedGroup.KeyStrokes);
                }
                charSpeedGroup.Start     = charSpeedGroupList[charSpeedGroupList.Count - 1].Start;
                charSpeedGroup.Stop      = charSpeedGroupList[0].Stop;
                charSpeedGroup.CharCount = charSpeedGroup.KeyStrokes.Count;


                if (charSpeedGroup.Elapsed != null)
                {
                    charSpeedGroup.CharsPerSecond = Math.Round(charSpeedGroup.CharCount / charSpeedGroup.Elapsed.Value.TotalSeconds, 2);
                }
                charSpeedGroup.CharsPerMinute = Math.Round(charSpeedGroup.CharsPerSecond * 60, 2);
                charSpeedGroup.WordsPerMinute = Math.Round(charSpeedGroup.CharsPerMinute / 5, 2);
                #endregion
            }
            else if (charSpeedGroupList.Count == 1)
            {
                charSpeedGroup = charSpeedGroupList[0];
            }

            return(charSpeedGroup);
        }
        private void ListView_Grid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            KeySpeedGroup        charSpeedGroupReturn = null;
            List <KeySpeedGroup> charSpeedGroupList   = null;

            ListViewKeyStroke = new ObservableCollection <KeyStroke>();
            if (ListViewGrid.SelectedItems.Count > 0)
            {
                var record = (Record)ListViewGrid.SelectedItems[0];

                if (record.TargetKeyStrokes != null && record.TargetKeyStrokes.Count > 0)
                {
                    foreach (var ks in record.TargetKeyStrokes)
                    {
                        if (ks.Key == " ")
                        {
                            ks.Key = "[Space]";
                        }

                        ListViewKeyStroke.Add(ks);
                    }

                    try
                    {
                        #region  |  calculate the char speed  |

                        //WPM_TotalSecondsTimeOut = 5;
                        //WPM_MinimumNumberOfChars = 5;
                        charSpeedGroupList = new List <KeySpeedGroup>();
                        var ksSorted = record.TargetKeyStrokes.OrderByDescending(k => k.Created).ToList();
                        var ksGroup  = new List <KeyStroke>();
                        foreach (var t in ksSorted)
                        {
                            if (ksGroup.Count > 0)
                            {
                                //check that the time span difference between last item in the list against the current
                                //keystroke is not greater than 5 seconds; otherwise it enters a seperate group
                                var dateTime = ksGroup[ksGroup.Count - 1].Created;
                                if (dateTime == null)
                                {
                                    continue;
                                }
                                if (t.Created == null)
                                {
                                    continue;
                                }
                                var ts = dateTime.Value.Subtract(t.Created.Value);
                                if (ts.TotalSeconds > WpmTotalSecondsTimeOut)
                                {
                                    //the translator needs to type more than 5 chars
                                    if (ksGroup.Count > WpmMinimumNumberOfChars)
                                    {
                                        charSpeedGroupList.Add(GetCharSpeedGroup(ksGroup));
                                    }

                                    ksGroup = new List <KeyStroke> {
                                        t
                                    };
                                }
                                else
                                {
                                    ksGroup.Add(t);
                                }
                            }
                            else
                            {
                                ksGroup.Add(t);
                            }
                        }
                        if (ksGroup.Count > 0)
                        {
                            //the translator needs to type more than 5 chars
                            if (ksGroup.Count > WpmMinimumNumberOfChars)
                            {
                                charSpeedGroupList.Add(GetCharSpeedGroup(ksGroup));
                            }
                        }

                        #endregion

                        //if more than on group was return, then get an accurate average!
                        charSpeedGroupReturn = GetAverageCharSpeedGroup(charSpeedGroupList);
                    }
                    catch
                    {
                        // ignored
                    }
                }
            }

            ListingDataViewKeyStroke.Source = ListViewKeyStroke;
            if (ListViewKeyStroke.Count > 0)
            {
                ListViewGridKeyStroke.SelectedIndex = 0;
            }

            TextBoxWordSpeedData.Inlines.Clear();
            if (charSpeedGroupReturn == null || !(charSpeedGroupReturn.CharCount > 0))
            {
                return;
            }
            try
            {
                TextBoxWordSpeedData.Padding = new Thickness(1, 5, 1, 1);

                var run = new Run
                {
                    Text            = "Estimated Typing Speed",
                    TextDecorations = TextDecorations.Underline,
                    Foreground      = Brushes.Black
                };
                run.FontSize = run.FontSize + 0;
                TextBoxWordSpeedData.Inlines.Add(run);
                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);
                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Block Count:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                run = new Run {
                    Text = charSpeedGroupList.Count.ToString()
                };
                TextBoxWordSpeedData.Inlines.Add(run);
                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Start Time:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                if (charSpeedGroupReturn.Start != null)
                {
                    run = new Run {
                        Text = charSpeedGroupReturn.Start.Value.ToLongTimeString()
                    }
                }
                ;
                TextBoxWordSpeedData.Inlines.Add(run);
                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Stop Time:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                if (charSpeedGroupReturn.Stop != null)
                {
                    run = new Run {
                        Text = charSpeedGroupReturn.Stop.Value.ToLongTimeString()
                    }
                }
                ;
                TextBoxWordSpeedData.Inlines.Add(run);

                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Total Seconds:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                if (charSpeedGroupReturn.Elapsed != null)
                {
                    run = new Run {
                        Text = Math.Round(charSpeedGroupReturn.Elapsed.Value.TotalSeconds, 2).ToString(CultureInfo.InvariantCulture)
                    }
                }
                ;
                TextBoxWordSpeedData.Inlines.Add(run);

                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Characters:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                run = new Run {
                    Text = charSpeedGroupReturn.CharCount.ToString(CultureInfo.InvariantCulture)
                };
                TextBoxWordSpeedData.Inlines.Add(run);


                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Characters p/m:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                run = new Run
                {
                    Text       = charSpeedGroupReturn.CharsPerMinute.ToString(CultureInfo.InvariantCulture),
                    Foreground = Brushes.SteelBlue
                };

                TextBoxWordSpeedData.Inlines.Add(run);

                TextBoxWordSpeedData.Inlines.Add(Environment.NewLine);

                run = new Run {
                    Text = "Words p/m:\t"
                };
                TextBoxWordSpeedData.Inlines.Add(run);

                run = new Run
                {
                    Text       = charSpeedGroupReturn.WordsPerMinute.ToString(CultureInfo.InvariantCulture),
                    FontWeight = FontWeights.Bold,
                    Foreground = Brushes.DarkGreen
                };
                run.FontSize = run.FontSize + 1;
                TextBoxWordSpeedData.Inlines.Add(run);
            }
            catch
            {
                // ignored
            }
        }