// função que calcula e faz chamadas para métodos que ajudarão a construir o relatório final
        public string CalcularResultadoCorrida(List <IVoltaCorrida> pVoltas)
        {
            // Dicionário que conterá dados relevantes de cada piloto ao final da corrida
            var dDadosPilotosCorrida = new Dictionary <int, DadosCorridaPiloto>();

            // agrupa voltas por cada piloto
            var voltasGroupedByPiloto = pVoltas.GroupBy(x => x.Piloto.Codigo);

            // para cada piloto, itere sobre suas voltas

            foreach (var voltaG in voltasGroupedByPiloto)
            {
                var voltasPiloto = voltaG.Select(volta => volta);

                var somadorVelocidades = 0.0f;

                var ultimaVolta = voltasPiloto.Last();

                //para cada volta do piloto atual
                foreach (var volta in voltasPiloto)
                {
                    if (!dDadosPilotosCorrida.ContainsKey(voltaG.Key))
                    {
                        somadorVelocidades += volta.VelocidadeMediaVolta;

                        var dadosCorrida = new DadosCorridaPiloto(volta.HoraVolta, volta.NumeroVolta, volta.TempoVolta, volta.VelocidadeMediaVolta, volta.Piloto);

                        dDadosPilotosCorrida.Add(voltaG.Key, dadosCorrida);
                    }
                    else
                    {
                        //se o piloto já esta no hashmap dados_pilotos_final_da_corrida, atualize seus dados//
                        DadosCorridaPiloto dadosPiloto = dDadosPilotosCorrida[voltaG.Key];

                        //atualiza hora da última volta
                        dadosPiloto.HoraUltimaVolta = volta.HoraVolta;
                        //atualiza número da volta
                        dadosPiloto.NumeroDeVoltasTotais = volta.NumeroVolta;
                        //atualiza velocidadeTotal
                        dadosPiloto.VelocidadeTotalCorrida = dadosPiloto.VelocidadeTotalCorrida + volta.VelocidadeMediaVolta;
                        //atualiza tempo total durante a corrida
                        dadosPiloto.TempoTotalCorrida = TimeSpanUtils.OperarTimeSpans(dadosPiloto.TempoTotalCorrida, volta.TempoVolta, ETimeSpanOperacao.Adicao);

                        //se tempo da volta atual for melhor (menor) que o anterior, atualize este valor
                        if (volta.TempoVolta < dadosPiloto.MelhorVolta)
                        {
                            dadosPiloto.MelhorVolta = volta.TempoVolta;
                        }

                        // variável que armazena a soma das velocidades de cada volta do piloto atual
                        somadorVelocidades += volta.VelocidadeMediaVolta;

                        // se for a última volta do piloto atual calcule sua Velocidade Média
                        if (volta.Equals(ultimaVolta))
                        {
                            float velocidadeMedia = somadorVelocidades / voltasPiloto.Count();
                            dadosPiloto.VelocidadeMediaCorrida = velocidadeMedia;

                            somadorVelocidades = 0.0F; // reseta somadorVelocidades
                        }

                        //atualiza dados do piloto atual no dicionário dDadosPilotosCorrida
                        dDadosPilotosCorrida[voltaG.Key] = dadosPiloto;
                    }
                }
            }

            //Dicionário com classificação final dos pilotos
            Dictionary <int, DadosCorridaPiloto> posicaoFinal = ComputarPosicaoFinalDosPilotos(dDadosPilotosCorrida);

            //gera relatório da classificação final da corrida
            CalcularClassificacaoFinalPilotos(posicaoFinal);

            //gera relatório da melhor volta de cada piloto na corrida
            CalcularMelhorVoltaDeCadaPiloto(dDadosPilotosCorrida);

            //gera relatório da melhor volta da corrida
            CalcularMelhorVoltaDaCorrida(dDadosPilotosCorrida);

            //gera relatório da velocidade média de cada piloto na corrida
            CalcularVelocidadeMediaDeCadaPiloto(dDadosPilotosCorrida);

            return(RelatorioFinal.ToString());
        }
        public void CalcularClassificacaoFinalPilotos(Dictionary <int, DadosCorridaPiloto> pPosicaoFinal)
        {
            StringBuilder classificacaoFinal = new StringBuilder();

            var formatString = "|{0,-30}|{1,-30}|{2,-30}|{3,-30}|{4,-30}|{5,-30}|";

            classificacaoFinal.AppendLine()
            .AppendFormat(Culture, formatString, "Posição Chegada", "Código Piloto", "Nome Piloto", "Qtde Voltas Completadas", "Tempo Total de Prova", "Tempo de Chegada Após Vencedor")
            .AppendLine();

            String primeiraLinha = CriarLinhaFormatadoraColunasTabelas(classificacaoFinal);

            classificacaoFinal.Insert(0, primeiraLinha);
            classificacaoFinal.Append(primeiraLinha).AppendLine();

            TimeSpan tempoChegadaVencedor = pPosicaoFinal.FirstOrDefault().Value.HoraUltimaVolta;
            int      count = 1;

            // constrói  relatorio da classificação final de cada piloto
            pPosicaoFinal.ToList().ForEach(x =>
            {
                int codigoPiloto     = x.Key;
                string nomePiloto    = x.Value.Piloto.NomePiloto;
                int qtdVoltas        = x.Value.NumeroDeVoltasTotais;
                string tempoTotal    = x.Value.TempoTotalCorrida.ToString(@"hh\:mm\:ss\:fff");
                TimeSpan horaChegada = x.Value.HoraUltimaVolta;

                string diferencaToVencedor = (tempoChegadaVencedor.CompareTo(horaChegada) == 0) ? "(Não se Aplica)" : TimeSpanUtils.OperarTimeSpans(horaChegada, tempoChegadaVencedor, ETimeSpanOperacao.Subtracao).ToString(@"hh\:mm\:ss\:fff");

                classificacaoFinal.AppendFormat(Culture, formatString, count++, codigoPiloto.ToString("D3"), nomePiloto, qtdVoltas, tempoTotal, diferencaToVencedor).AppendLine();
            });


            RelatorioFinal.Append("[ RESULTADO FINAL DA CORRIDA (CLASSIFICAÇÃO) E DIFERENÇA DE TEMPO DE CHEGADA DE CADA PILOTO PARA O VENCEDOR ]").AppendLine().AppendLine();
            RelatorioFinal.Append(classificacaoFinal.ToString());
        }