public static NeurologyFormTotals LoadNeurologyFormTotalsFrom(XDocument xmlDocument)
        {
            var totalsXml = xmlDocument.Root.Element("NeurologyFormTotals");

            var neurologyFormTotals = new NeurologyFormTotals
            {
                LeftPrickContainsNt = bool.Parse(totalsXml.Element("LeftPrickContainsNt").Value),
                LeftPrickTotal = GetIntValueFrom(totalsXml.Element("LeftPrickTotal").Value),
                LeftPrickTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("LeftPrickTotal").Value),
                LeftTouchContainsNt = bool.Parse(totalsXml.Element("LeftTouchContainsNt").Value),
                LeftTouchTotal = GetIntValueFrom(totalsXml.Element("LeftTouchTotal").Value),
                LeftTouchTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("LeftTouchTotal").Value),
                LeftLowerMotorContainsNt = bool.Parse(totalsXml.Element("LeftLowerMotorContainsNt").Value),
                LeftLowerMotorTotal = GetIntValueFrom(totalsXml.Element("LeftLowerMotorTotal").Value),
                LeftLowerMotorTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("LeftLowerMotorTotal").Value),
                LeftUpperMotorContainsNt = bool.Parse(totalsXml.Element("LeftUpperMotorContainsNt").Value),
                LeftUpperMotorTotal = GetIntValueFrom(totalsXml.Element("LeftUpperMotorTotal").Value),
                LeftUpperMotorTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("LeftUpperMotorTotal").Value),
                LowerMotorTotal = GetIntValueFrom(totalsXml.Element("LowerMotorTotal").Value),
                PrickTotal = GetIntValueFrom(totalsXml.Element("PrickTotal").Value),
                RightLowerMotorContainsNt = bool.Parse(totalsXml.Element("RightLowerMotorContainsNt").Value),
                RightLowerMotorTotal = GetIntValueFrom(totalsXml.Element("RightLowerMotorTotal").Value),
                RightLowerMotorTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("RightLowerMotorTotal").Value),
                RightUpperMotorContainsNt = bool.Parse(totalsXml.Element("RightUpperMotorContainsNt").Value),
                RightUpperMotorTotal = GetIntValueFrom(totalsXml.Element("RightUpperMotorTotal").Value),
                RightUpperMotorTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("RightUpperMotorTotal").Value),
                RightPrickContainsNt = bool.Parse(totalsXml.Element("RightPrickContainsNt").Value),
                RightPrickTotal = GetIntValueFrom(totalsXml.Element("RightPrickTotal").Value),
                RightPrickTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("RightPrickTotal").Value),
                RightTouchContainsNt = bool.Parse(totalsXml.Element("RightTouchContainsNt").Value),
                RightTouchTotal = GetIntValueFrom(totalsXml.Element("RightTouchTotal").Value),
                RightTouchTotalHasImpairmentNotDueToSci = DoesTotalHaveImpairmentNotDueToSci(totalsXml.Element("RightTouchTotal").Value),
                TouchTotal = GetIntValueFrom(totalsXml.Element("TouchTotal").Value),
                UpperMotorTotal = GetIntValueFrom(totalsXml.Element("UpperMotorTotal").Value),
            };

            var levelsDictionary = GetLevelsDictionary();

            foreach (var value in totalsXml.Element("RightSensory").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddRightSensoryValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("LeftSensory").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddLeftSensoryValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("RightMotor").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddRightMotorValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("LeftMotor").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddLeftMotorValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("NeurologicalLevelOfInjury").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddNeurologicalLevelOfInjury(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("RightSensoryZpp").Value.Split(',').Select(v => v.Trim()))
                if (!string.IsNullOrEmpty(value))
                    neurologyFormTotals.AddRightSensoryZppValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("LeftSensoryZpp").Value.Split(',').Select(v => v.Trim()))
                if (!string.IsNullOrEmpty(value))
                    neurologyFormTotals.AddLeftSensoryZppValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("RightMotorZpp").Value.Split(',').Select(v => v.Trim()))
                if (!string.IsNullOrEmpty(value))
                    neurologyFormTotals.AddRightMotorZppValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("LeftMotorZpp").Value.Split(',').Select(v => v.Trim()))
                if (!string.IsNullOrEmpty(value))
                    neurologyFormTotals.AddLeftMotorZppValue(levelsDictionary[value.ToUpper()]);

            foreach (var value in totalsXml.Element("AsiaImpairmentScale").Value.Split(',').Select(v => v.Trim()))
                neurologyFormTotals.AddAsiaImpairmentScaleValue(value.ToUpper());

            return neurologyFormTotals;
        }
        /// <summary>
        /// Returns the results produced by the ISNCSCI Algorithm ir a raw values format.
        /// </summary>
        /// <param name="neurologyForm">Neurology form that has been populated with the values to be used in the algorithm calculations.</param>
        /// <returns>
        /// Totals in raw values format.  The results contain lists with every prossible value for each field.
        /// You can use the resulting object to obtained a summarized version, which uses ranges, by passing the result to the method GetTotalsSummaryFor
        /// </returns>
        public static NeurologyFormTotals GetTotalsFor(NeurologyForm neurologyForm)
        {
            var totals = new NeurologyFormTotals();

            UpdateTotalsWithLevelAt(neurologyForm, totals, neurologyForm.GetLevelWithName("C2"), false, false);

            totals.UpperMotorTotal = totals.RightUpperMotorTotal + totals.LeftUpperMotorTotal;
            totals.LowerMotorTotal = totals.RightLowerMotorTotal + totals.LeftLowerMotorTotal;
            totals.TouchTotal = totals.RightTouchTotal + totals.LeftTouchTotal;
            totals.PrickTotal = totals.RightPrickTotal + totals.LeftPrickTotal;

            var s4_5 = neurologyForm.GetLevelWithName("S4_5");

            var c1 = neurologyForm.GetLevelWithName("C2").Previous;

            if (totals.RightSensoryZppHasOnlySoftValues)
                totals.AddRightSensoryZppValue(c1);

            if (totals.LeftSensoryZppHasOnlySoftValues)
                totals.AddLeftSensoryZppValue(c1);

            if (totals.RightMotorZppHasOnlySoftValues)
                totals.AddRightMotorZppValue(c1);

            if (totals.LeftMotorZppHasOnlySoftValues)
                totals.AddLeftMotorZppValue(c1);

            if (totals.MostRostralRightLevelWithMotorFunction == null)
                totals.MostRostralRightLevelWithMotorFunction = c1;

            if (totals.MostRostralLeftLevelWithMotorFunction == null)
                totals.MostRostralLeftLevelWithMotorFunction = c1;

            if (totals.MostCaudalRightLevelWithMotorFunction == null)
                totals.MostCaudalRightLevelWithMotorFunction = c1;

            if (totals.MostCaudalLeftLevelWithMotorFunction == null)
                totals.MostCaudalLeftLevelWithMotorFunction = c1;

            // [ASIA learning center 2012-11-14] Sensory incomplete: Sacral sparing of sensory function
            var isSensoryIncomplete = neurologyForm.AnalSensation == BinaryObservation.Yes || neurologyForm.AnalSensation == BinaryObservation.NT
                || !"0".Equals(s4_5.RightTouchName) || !"0".Equals(s4_5.LeftTouchName)
                || !"0".Equals(s4_5.RightPrickName) || !"0".Equals(s4_5.LeftPrickName);

            var couldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel = CouldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel(neurologyForm, totals);

            var couldNotBeMotorIncomplete = (neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT)
                && isSensoryIncomplete && couldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel;

            if ((neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT)
                && (neurologyForm.AnalSensation == BinaryObservation.No || neurologyForm.AnalSensation == BinaryObservation.NT)
                && s4_5.RightTouchValue == 0 && !s4_5.RightTouchImpairmentNotDueToSci
                && s4_5.RightPrickValue == 0 && !s4_5.RightPrickImpairmentNotDueToSci
                && s4_5.LeftTouchValue == 0 && !s4_5.LeftTouchImpairmentNotDueToSci
                && s4_5.LeftPrickValue == 0 && !s4_5.LeftPrickImpairmentNotDueToSci)
                totals.AddAsiaImpairmentScaleValue("A");

            if (couldNotBeMotorIncomplete && totals.MostRostralNeurologicalLevelOfInjury.Name != "S4_5")
                totals.AddAsiaImpairmentScaleValue("B");

            //// Not an ASIA E only
            //// AND not an ASIA A only
            if (totals.MostRostralNeurologicalLevelOfInjury.Name != "S4_5"
                && (isSensoryIncomplete || neurologyForm.AnalContraction == BinaryObservation.Yes || neurologyForm.AnalContraction == BinaryObservation.NT))
            {
                bool couldBeAsiaC;
                bool couldBeAsiaD;
                CouldBeAsiaCorD(neurologyForm, totals, out couldBeAsiaC, out couldBeAsiaD);

                if (couldBeAsiaC)
                    totals.AddAsiaImpairmentScaleValue("C");

                if (couldBeAsiaD)
                    totals.AddAsiaImpairmentScaleValue("D");
            }

            if (totals.RightSensoryContains("S4_5") && totals.LeftSensoryContains("S4_5")
                && totals.RightMotorContains("S4_5") && totals.LeftMotorContains("S4_5"))
                totals.AddAsiaImpairmentScaleValue("E");

            return totals;
        }
        /// <summary>
        /// Recursive method which iterates through the values in a nuerology form while it updates the totals generating the results produced by the algorithm.
        /// </summary>
        /// <param name="neurologyForm">Form being evaluated.</param>
        /// <param name="totals">Brand new totals object where the results are to be stored.</param>
        /// <param name="level">Current neurology level being evaluated</param>
        /// <param name="nextNonKeyMuscleShouldBeRightMotor">Flag used to evaluate the Kathy Collins condition on the right motor results.</param>
        /// <param name="nextNonKeyMuscleShouldBeLeftMotor">Flag used to evaluate the Kathy Collins condition on the left motor results.</param>
        private static void UpdateTotalsWithLevelAt(NeurologyForm neurologyForm, NeurologyFormTotals totals, NeurologyFormLevel level, bool nextNonKeyMuscleShouldBeRightMotor, bool nextNonKeyMuscleShouldBeLeftMotor)
        {
            totals.RightTouchTotal += level.RightTouchValue;
            totals.LeftTouchTotal += level.LeftTouchValue;
            totals.RightPrickTotal += level.RightPrickValue;
            totals.LeftPrickTotal += level.LeftPrickValue;

            if (level.IsKeyMuscle)
            {
                if (level.IsLowerMuscle)
                {
                    if (level.RightMotorImpairmentNotDueToSci && !totals.RightLowerMotorTotalHasImpairmentNotDueToSci)
                        totals.RightLowerMotorTotalHasImpairmentNotDueToSci = true;

                    if (level.LeftMotorImpairmentNotDueToSci && !totals.LeftLowerMotorTotalHasImpairmentNotDueToSci)
                        totals.LeftLowerMotorTotalHasImpairmentNotDueToSci = true;

                    if (NtRegex.IsMatch(level.RightMotorName) && !level.RightMotorImpairmentNotDueToSci)
                        totals.RightLowerMotorContainsNt = true;

                    if (NtRegex.IsMatch(level.LeftMotorName) && !level.LeftMotorImpairmentNotDueToSci)
                        totals.LeftLowerMotorContainsNt = true;
                }
                else
                {
                    if (level.RightMotorImpairmentNotDueToSci && !totals.RightUpperMotorTotalHasImpairmentNotDueToSci)
                        totals.RightUpperMotorTotalHasImpairmentNotDueToSci = true;

                    if (level.LeftMotorImpairmentNotDueToSci && !totals.LeftUpperMotorTotalHasImpairmentNotDueToSci)
                        totals.LeftUpperMotorTotalHasImpairmentNotDueToSci = true;

                    if (NtRegex.IsMatch(level.RightMotorName) && !level.RightMotorImpairmentNotDueToSci)
                        totals.RightUpperMotorContainsNt = true;

                    if (NtRegex.IsMatch(level.LeftMotorName) && !level.LeftMotorImpairmentNotDueToSci)
                        totals.LeftUpperMotorContainsNt = true;
                }
            }
            else
            {
                if (nextNonKeyMuscleShouldBeRightMotor)
                {
                    nextNonKeyMuscleShouldBeRightMotor = false;
                    totals.AddRightMotorValue(level.Previous);

                    if (!totals.RightSensoryHasOnlySoftValues)
                        totals.RightMotorHasOnlySoftValues = false;
                }

                if (nextNonKeyMuscleShouldBeLeftMotor)
                {
                    nextNonKeyMuscleShouldBeLeftMotor = false;
                    totals.AddLeftMotorValue(level.Previous);

                    if (!totals.LeftSensoryHasOnlySoftValues)
                        totals.LeftMotorHasOnlySoftValues = false;
                }
            }

            if (level.RightTouchImpairmentNotDueToSci && !totals.RightTouchTotalHasImpairmentNotDueToSci)
                totals.RightTouchTotalHasImpairmentNotDueToSci = true;

            if (level.LeftTouchImpairmentNotDueToSci && !totals.LeftTouchTotalHasImpairmentNotDueToSci)
                totals.LeftTouchTotalHasImpairmentNotDueToSci = true;

            if (level.RightPrickImpairmentNotDueToSci && !totals.RightPrickTotalHasImpairmentNotDueToSci)
                totals.RightPrickTotalHasImpairmentNotDueToSci = true;

            if (level.LeftPrickImpairmentNotDueToSci && !totals.LeftPrickTotalHasImpairmentNotDueToSci)
                totals.LeftPrickTotalHasImpairmentNotDueToSci = true;

            // Check if a column contains any NT value so we can properly format the total presented to the user
            if (NtRegex.IsMatch(level.RightTouchName) && !level.RightTouchImpairmentNotDueToSci && !totals.RightTouchContainsNt)
                totals.RightTouchContainsNt = true;

            if (NtRegex.IsMatch(level.LeftTouchName) && !level.LeftTouchImpairmentNotDueToSci && !totals.LeftTouchContainsNt)
                totals.LeftTouchContainsNt = true;

            if (NtRegex.IsMatch(level.RightPrickName) && !level.RightPrickImpairmentNotDueToSci && !totals.RightPrickContainsNt)
                totals.RightPrickContainsNt = true;

            if (NtRegex.IsMatch(level.LeftPrickName) && !level.LeftPrickImpairmentNotDueToSci && !totals.LeftPrickContainsNt)
                totals.LeftPrickContainsNt = true;

            if (totals.RightSensoryHasOnlySoftValues &&
                ((level.RightTouchValue != 2 && !level.RightTouchImpairmentNotDueToSci) ||
                 (level.RightPrickValue != 2 && !level.RightPrickImpairmentNotDueToSci)))
            {
                totals.AddRightSensoryValue(level.Previous);

                if ("S4_5".Equals(level.Name)
                    && (level.RightTouchValue == 2 || NtRegex.IsMatch(level.RightTouchName))
                    && (level.RightPrickValue == 2 || NtRegex.IsMatch(level.RightPrickName)))
                {
                    totals.AddRightSensoryValue(level);

                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                        totals.AddNeurologicalLevelOfInjury(level);
                }

                if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                    totals.AddNeurologicalLevelOfInjury(level.Previous);

                if ((level.RightTouchValue != 2 && !NtRegex.IsMatch(level.RightTouchName))
                    || (level.RightPrickValue != 2 && !NtRegex.IsMatch(level.RightPrickName)))
                {
                    totals.RightSensoryHasOnlySoftValues = false;
                    totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
                }

                if (level.IsKeyMuscle)
                {
                    nextNonKeyMuscleShouldBeRightMotor = true;
                    totals.HasRightCollins = true;
                }
            }

            if (totals.LeftSensoryHasOnlySoftValues &&
                ((level.LeftTouchValue != 2 && !level.LeftTouchImpairmentNotDueToSci) ||
                 (level.LeftPrickValue != 2 && !level.LeftPrickImpairmentNotDueToSci)))
            {
                totals.AddLeftSensoryValue(level.Previous);

                if ("S4_5".Equals(level.Name)
                    && (level.LeftTouchValue == 2 || NtRegex.IsMatch(level.LeftTouchName))
                    && (level.LeftPrickValue == 2 || NtRegex.IsMatch(level.LeftPrickName)))
                {
                    totals.AddLeftSensoryValue(level);

                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                        totals.AddNeurologicalLevelOfInjury(level);
                }

                if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                    totals.AddNeurologicalLevelOfInjury(level.Previous);

                if ((level.LeftTouchValue != 2 && !NtRegex.IsMatch(level.LeftTouchName))
                    || (level.LeftPrickValue != 2 && !NtRegex.IsMatch(level.LeftPrickName)))
                {
                    totals.LeftSensoryHasOnlySoftValues = false;
                    totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
                }

                if (level.IsKeyMuscle)
                {
                    nextNonKeyMuscleShouldBeLeftMotor = true;
                    totals.HasLeftCollins = true;
                }
            }

            if (totals.RightMotorHasOnlySoftValues && level.RightMotorValue != 5 && !level.RightMotorImpairmentNotDueToSci)
            {
                if (level.IsKeyMuscle && (level.RightMotorValue >= 3 || NtRegex.IsMatch(level.RightMotorName)))
                {
                    totals.AddRightMotorValue(level);

                    // Check if left won't make the NLI have to be the previous level.
                    // Let the logic for left motor handle the SNL instead
                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues
                        && (level.LeftMotorValue > 2 || level.LeftMotorImpairmentNotDueToSci || NtRegex.IsMatch(level.LeftMotorName)))
                    {
                        totals.AddNeurologicalLevelOfInjury(level);

                        if (!NtRegex.IsMatch(level.RightMotorName))
                            totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
                    }
                }

                if (level.RightMotorValue < 3 || NtRegex.IsMatch(level.RightMotorName))
                {
                    totals.AddRightMotorValue(level.Previous);

                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                    {
                        totals.AddNeurologicalLevelOfInjury(level.Previous);

                        if (!NtRegex.IsMatch(level.RightMotorName))
                            totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
                    }
                }

                if (!NtRegex.IsMatch(level.RightMotorName))
                    totals.RightMotorHasOnlySoftValues = false;
            }

            if (totals.LeftMotorHasOnlySoftValues && level.LeftMotorValue != 5 && !level.LeftMotorImpairmentNotDueToSci)
            {
                if (level.IsKeyMuscle && (level.LeftMotorValue >= 3 || NtRegex.IsMatch(level.LeftMotorName)))
                {
                    totals.AddLeftMotorValue(level);

                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                        totals.AddNeurologicalLevelOfInjury(level);
                }

                if (level.LeftMotorValue < 3 || NtRegex.IsMatch(level.LeftMotorName))
                {
                    totals.AddLeftMotorValue(level.Previous);

                    if (totals.NeurologicalLevelOfInjuryHasOnlySoftValues)
                        totals.AddNeurologicalLevelOfInjury(level.Previous);
                }

                if (!NtRegex.IsMatch(level.LeftMotorName))
                {
                    totals.LeftMotorHasOnlySoftValues = false;
                    totals.NeurologicalLevelOfInjuryHasOnlySoftValues = false;
                }
            }

            /* -- RECURSIVE CALL --------------- */
            if (level.Next != null)
                UpdateTotalsWithLevelAt(neurologyForm, totals, level.Next,
                    nextNonKeyMuscleShouldBeRightMotor && totals.RightMotorHasOnlySoftValues,
                    nextNonKeyMuscleShouldBeLeftMotor && totals.LeftMotorHasOnlySoftValues);

            #region This happens when there are INTACT values -------------------------------------------
            if ("S4_5".Equals(level.Name))
            {
                if (totals.RightSensoryHasOnlySoftValues && totals.LeftSensoryHasOnlySoftValues
                && totals.RightMotorHasOnlySoftValues && totals.LeftMotorHasOnlySoftValues)
                    totals.AddNeurologicalLevelOfInjury(level);

                if (totals.RightSensoryHasOnlySoftValues)
                {
                    totals.AddRightSensoryValue(level);
                    totals.RightSensoryHasOnlySoftValues = false;
                }

                if (totals.LeftSensoryHasOnlySoftValues)
                {
                    totals.AddLeftSensoryValue(level);
                    totals.LeftSensoryHasOnlySoftValues = false;
                }

                if (totals.RightMotorHasOnlySoftValues)
                {
                    totals.AddRightMotorValue(level);
                    totals.RightMotorHasOnlySoftValues = false;
                }

                if (totals.LeftMotorHasOnlySoftValues)
                {
                    totals.AddLeftMotorValue(level);
                    totals.LeftMotorHasOnlySoftValues = false;
                }
            }
            #endregion

            if (totals.RightSensoryZppHasOnlySoftValues && (!"0".Equals(level.RightTouchName) || !"0".Equals(level.RightPrickName)))
            {
                if ((level.RightTouchValue > 0 || level.RightTouchImpairmentNotDueToSci)
                    || (level.RightPrickValue > 0 || level.RightPrickImpairmentNotDueToSci))
                    totals.RightSensoryZppHasOnlySoftValues = false;

                totals.AddRightSensoryZppValue(level);
            }

            if (totals.LeftSensoryZppHasOnlySoftValues && (!"0".Equals(level.LeftTouchName) || !"0".Equals(level.LeftPrickName)))
            {
                if ((level.LeftTouchValue > 0 || level.LeftTouchImpairmentNotDueToSci)
                    || (level.LeftPrickValue > 0 || level.LeftPrickImpairmentNotDueToSci))
                    totals.LeftSensoryZppHasOnlySoftValues = false;

                totals.AddLeftSensoryZppValue(level);
            }

            if (totals.RightMotorZppHasOnlySoftValues
                && (level.HasOtherRightMotorFunction
                    || (!"0".Equals(level.RightMotorName) && (level.IsKeyMuscle || totals.RightMotorContains(level.Name)))))
            {
                if ((level.RightMotorImpairmentNotDueToSci || level.HasOtherRightMotorFunction || !NtRegex.IsMatch(level.RightMotorName)) &&
                    (level.IsKeyMuscle
                    || level.Ordinal < 4
                    || (level.Ordinal > 25 && !totals.RightUpperMotorContainsNt && !totals.RightLowerMotorContainsNt && !totals.HasRightCollins)
                    || (level.Ordinal > 8 && level.Ordinal < 21 && !totals.RightUpperMotorContainsNt)))
                    totals.RightMotorZppHasOnlySoftValues = false;

                totals.AddRightMotorZppValue(level);
            }

            if (totals.LeftMotorZppHasOnlySoftValues
                && (level.HasOtherLeftMotorFunction
                    || (!"0".Equals(level.LeftMotorName) && (level.IsKeyMuscle || totals.LeftMotorContains(level.Name)))))
            {
                if ((level.LeftMotorImpairmentNotDueToSci || level.HasOtherLeftMotorFunction || !NtRegex.IsMatch(level.LeftMotorName)) &&
                    (level.IsKeyMuscle
                    || level.Ordinal < 4
                    || (level.Ordinal > 25 && !totals.LeftUpperMotorContainsNt && !totals.LeftLowerMotorContainsNt && !totals.HasLeftCollins)
                    || (level.Ordinal > 8 && level.Ordinal < 21 && !totals.LeftUpperMotorContainsNt)))
                    totals.LeftMotorZppHasOnlySoftValues = false;

                totals.AddLeftMotorZppValue(level);
            }

            // Update most Rostral levels with motor function
            if ((level.IsKeyMuscle || level.HasOtherRightMotorFunction)
                && totals.MostRostralRightLevelWithMotorFunction == null
                && (level.RightMotorImpairmentNotDueToSci || level.HasOtherRightMotorFunction || (level.RightMotorValue != 0 && level.IsKeyMuscle)))
                totals.MostRostralRightLevelWithMotorFunction = level;

            if ((level.IsKeyMuscle || level.HasOtherLeftMotorFunction)
                && totals.MostRostralLeftLevelWithMotorFunction == null
                && (level.LeftMotorImpairmentNotDueToSci || level.HasOtherLeftMotorFunction || (level.LeftMotorValue != 0 && level.IsKeyMuscle)))
                totals.MostRostralLeftLevelWithMotorFunction = level;

            // Update most Caudal levels with motor function
            if ((level.IsKeyMuscle || level.HasOtherRightMotorFunction)
                && totals.MostCaudalRightLevelWithMotorFunction == null
                && (!"0".Equals(level.RightMotorName) || level.HasOtherRightMotorFunction))
                totals.MostCaudalRightLevelWithMotorFunction = level;

            if ((level.IsKeyMuscle || level.HasOtherLeftMotorFunction)
                && totals.MostCaudalLeftLevelWithMotorFunction == null
                && (!"0".Equals(level.LeftMotorName) || level.HasOtherLeftMotorFunction))
                totals.MostCaudalLeftLevelWithMotorFunction = level;

            if (!level.IsKeyMuscle)
                return;

            if (level.IsLowerMuscle)
            {
                totals.RightLowerMotorTotal += level.RightMotorValue;
                totals.LeftLowerMotorTotal += level.LeftMotorValue;
            }
            else
            {
                totals.RightUpperMotorTotal += level.RightMotorValue;
                totals.LeftUpperMotorTotal += level.LeftMotorValue;
            }
        }
        /// <summary>
        /// Evaluates the specified form and totals to determine if any of the different
        /// return conditions could produce a case where there could be motor function more
        /// than 3 levels below the injury level.
        /// </summary>
        /// <param name="neurologyForm">Form that was used to produce the totals.</param>
        /// <param name="totals">Totals retunred by the algorithm.</param>
        /// <returns>Flag indicating if any combination in the totals could have a case with motor function more than 3 levels below the injury level.</returns>
        private static bool CouldNotHaveMotorFunctionMoreThan3LevelsBelowMotorLevel(NeurologyForm neurologyForm, NeurologyFormTotals totals)
        {
            NeurologyFormLevel mostRostralRightLevelWithMotor = null;
            NeurologyFormLevel mostRostralLeftLevelWithMotor = null;

            var currentLevel = neurologyForm.GetLevelWithName("S1");

            while ((mostRostralRightLevelWithMotor == null || mostRostralLeftLevelWithMotor == null)
                && currentLevel != null // Could happen if SNL is C1
                && currentLevel.Ordinal >= totals.MostCaudalNeurologicalLevelOfInjury.Ordinal)
            {
                if (mostRostralRightLevelWithMotor == null &&
                    (currentLevel.RightMotorImpairmentNotDueToSci || currentLevel.HasOtherRightMotorFunction || (currentLevel.RightMotorValue != 0 && currentLevel.IsKeyMuscle)))
                    mostRostralRightLevelWithMotor = currentLevel;

                if (mostRostralLeftLevelWithMotor == null &&
                    (currentLevel.LeftMotorImpairmentNotDueToSci || currentLevel.HasOtherLeftMotorFunction || (currentLevel.LeftMotorValue != 0 && currentLevel.IsKeyMuscle)))
                    mostRostralLeftLevelWithMotor = currentLevel;

                currentLevel = currentLevel.Previous;
            }

            return (mostRostralRightLevelWithMotor == null || mostRostralRightLevelWithMotor.Ordinal - totals.MostCaudalRightMotor.Ordinal <= 3)
                && (mostRostralLeftLevelWithMotor == null || mostRostralLeftLevelWithMotor.Ordinal - totals.MostCaudalLeftMotor.Ordinal <= 3);
        }
        /// <summary>
        /// Evaluates the specified form and totals to determine if any of the different
        /// return conditions could produce a case where the Asia Impairment Scale is C o D.
        /// </summary>
        /// <param name="neurologyForm">Form that was used to produce the totals.</param>
        /// <param name="totals">Totals retunred by the algorithm.</param>
        /// <param name="couldBeAsiaC">out variable use as flag indicating if this case is a possible ASIA C.</param>
        /// <param name="couldBeAsiaD">out variable use as flag indicating if this case is a possible ASIA D.</param>
        private static void CouldBeAsiaCorD(NeurologyForm neurologyForm, NeurologyFormTotals totals, out bool couldBeAsiaC, out bool couldBeAsiaD)
        {
            couldBeAsiaC = false;
            couldBeAsiaD = false;
            NeurologyFormLevel rightMotor = null;
            NeurologyFormLevel leftMotor = null;

            // Check if the patient could be motor incoplete via sphincter contraction
            // Otherwise we need to check for motor function more than 3 levels below motor level.
            var couldHaveAnalContraction = neurologyForm.AnalContraction == BinaryObservation.Yes || neurologyForm.AnalContraction == BinaryObservation.NT;
            //var couldNotHaveAnalContraction = neurologyForm.AnalContraction == BinaryObservation.No || neurologyForm.AnalContraction == BinaryObservation.NT;

            var nliEnum = totals.GetNeurologicalLevelsOfInjury().GetEnumerator();

            while (nliEnum.MoveNext() && (!couldBeAsiaC || !couldBeAsiaD))
            {
                //var otherMotorIsMoreThanThreeLevelsBelowNli = (neurologyForm.)

                // RIGHT MOTOR
                // If not motor incomplete already, find the right motor level that correspond to this particular neurological level
                if (!couldHaveAnalContraction && (rightMotor == null || rightMotor.Ordinal < nliEnum.Current.Ordinal))
                {
                    var rightMotorValues = totals.GetRightMotorValues().GetEnumerator();
                    rightMotor = null;

                    while (rightMotor == null && rightMotorValues.MoveNext())
                    {
                        if (rightMotorValues.Current.Ordinal >= nliEnum.Current.Ordinal)
                            rightMotor = rightMotorValues.Current;
                    }

                    rightMotorValues.Dispose();
                }

                // LEFT MOTOR
                // If not motor incomplete already, find the left motor level that correspond to this particular neurological level
                if (!couldHaveAnalContraction && (leftMotor == null || leftMotor.Ordinal < nliEnum.Current.Ordinal))
                {
                    var leftMotorValues = totals.GetLeftMotorValues().GetEnumerator();
                    leftMotor = null;

                    while (leftMotor == null && leftMotorValues.MoveNext())
                    {
                        if (leftMotorValues.Current.Ordinal >= nliEnum.Current.Ordinal)
                            leftMotor = leftMotorValues.Current;
                    }

                    leftMotorValues.Dispose();
                }

                //if (couldNotHaveAnalContraction
                //    && (rightMotor == null || totals.MostCaudalRightLevelWithMotorFunction.Ordinal - rightMotor.Ordinal <= 3)
                //    && (leftMotor == null || totals.MostCaudalLeftLevelWithMotorFunction.Ordinal - leftMotor.Ordinal <= 3))
                //    couldBeAsiaB = true;

                // If the test is not motor incomplete at this level, do not continue to count motor levels, move to the next nli available.
                if (!couldHaveAnalContraction
                    && totals.MostCaudalRightLevelWithMotorFunction.Ordinal - rightMotor.Ordinal <= 3
                    && totals.MostCaudalLeftLevelWithMotorFunction.Ordinal - leftMotor.Ordinal <= 3)
                    continue;

                // When motor incomplete and the nli is S1 or more caudal, it is automatically D since there are no myotomes to count from.
                // We can break the loop.
                if (nliEnum.Current.Ordinal > 24) // Greater than L5 (24)
                {
                    couldBeAsiaD = true;
                    break;
                }

                // If motor incomplete, count the motor levels with muscle grade greater than two
                var levelsWithMuscleGradeGreaterThanTwo = 0;
                var levelsWithMuscleGradeLessThanThree = 0;
                var eligibleLevelCount = 0;
                var currentLevel = nliEnum.Current.Next;

                while (currentLevel != null)
                {
                    if (currentLevel.IsKeyMuscle)
                    {
                        eligibleLevelCount += 2;

                        if (currentLevel.RightMotorValue > 2 || currentLevel.RightMotorImpairmentNotDueToSci || NtRegex.IsMatch(currentLevel.RightMotorName))
                            levelsWithMuscleGradeGreaterThanTwo++;

                        if ((currentLevel.RightMotorValue < 3 || NtRegex.IsMatch(currentLevel.RightMotorName)) && !currentLevel.RightMotorImpairmentNotDueToSci)
                            levelsWithMuscleGradeLessThanThree++;

                        if (currentLevel.LeftMotorValue > 2 || currentLevel.LeftMotorImpairmentNotDueToSci || NtRegex.IsMatch(currentLevel.LeftMotorName))
                            levelsWithMuscleGradeGreaterThanTwo++;

                        if ((currentLevel.LeftMotorValue < 3 || NtRegex.IsMatch(currentLevel.LeftMotorName)) && !currentLevel.LeftMotorImpairmentNotDueToSci)
                            levelsWithMuscleGradeLessThanThree++;
                    }

                    currentLevel = currentLevel.Next;
                }

                // If not more than half the myotomes contain values less to 3, this is an Asia C
                if (levelsWithMuscleGradeLessThanThree > eligibleLevelCount / 2)
                    couldBeAsiaC = true;

                // If at least half the myotomes below the current NLI containing values greater or equal to 3, hooray! it is ASIA D.
                if (levelsWithMuscleGradeGreaterThanTwo >= eligibleLevelCount / 2)
                    couldBeAsiaD = true;
            }

            nliEnum.Dispose();
        }
        /// <summary>
        /// Produces a summarized version of a NeurologyFormTotals object which can be used to be displayed in an interface or be stored in a database.
        /// The values in the summary return will have ranges instead of lists of values.
        /// </summary>
        /// <param name="totals">The form totals object to be used to generate the summary.</param>
        /// <returns>
        /// Summarized version of the totals where the enumerations get replaced by ranges.
        /// </returns>
        public static NeurologyFormTotalsSummary GetTotalsSummaryFor(NeurologyFormTotals totals)
        {
            var ais = totals.GetAsiaImpairmentScaleValues();
            var isAsiaA = ais.Contains("A");
            var couldBeOtherThanAsiaA = !isAsiaA || ais.Length > 1;

            var summary = new NeurologyFormTotalsSummary
                {
                    AsiaImpairmentScale = ais,
                    Completeness = isAsiaA ? (couldBeOtherThanAsiaA ? "C,I" : "C") : "I",
                    LeftLowerMotorTotal = GetSummaryStringFor(totals.LeftLowerMotorTotal, totals.LeftLowerMotorTotalHasImpairmentNotDueToSci, totals.LeftLowerMotorContainsNt),
                    LeftMotor = GetLevelsRange(totals.GetLeftMotorValues(), false),
                    LeftMotorZpp = isAsiaA ? GetLevelsRange(totals.GetLeftMotorZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
                    LeftMotorTotal = GetSummaryStringFor(totals.LeftUpperMotorTotal + totals.LeftLowerMotorTotal,
                                                            totals.LeftUpperMotorTotalHasImpairmentNotDueToSci || totals.LeftLowerMotorTotalHasImpairmentNotDueToSci,
                                                            totals.LeftUpperMotorContainsNt || totals.LeftLowerMotorContainsNt),
                    LeftPrickTotal = GetSummaryStringFor(totals.LeftPrickTotal, totals.LeftPrickTotalHasImpairmentNotDueToSci, totals.LeftPrickContainsNt),
                    LeftSensory = GetLevelsRange(totals.GetLeftSensoryValues(), false),
                    LeftSensoryZpp = isAsiaA ? GetLevelsRange(totals.GetLeftSensoryZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
                    LeftTouchTotal = GetSummaryStringFor(totals.LeftTouchTotal, totals.LeftTouchTotalHasImpairmentNotDueToSci, totals.LeftTouchContainsNt),
                    LeftUpperMotorTotal = GetSummaryStringFor(totals.LeftUpperMotorTotal, totals.LeftUpperMotorTotalHasImpairmentNotDueToSci, totals.LeftUpperMotorContainsNt),
                    LowerMotorTotal = GetSummaryStringFor(totals.LowerMotorTotal,
                                                        totals.RightLowerMotorTotalHasImpairmentNotDueToSci || totals.LeftLowerMotorTotalHasImpairmentNotDueToSci,
                                                        totals.RightLowerMotorContainsNt || totals.LeftLowerMotorContainsNt),
                    NeurologicalLevelOfInjury = GetLevelsRange(totals.GetNeurologicalLevelsOfInjury(), false),
                    PrickTotal = GetSummaryStringFor(totals.RightPrickTotal + totals.LeftPrickTotal,
                                                        totals.RightPrickTotalHasImpairmentNotDueToSci || totals.LeftPrickTotalHasImpairmentNotDueToSci,
                                                        totals.RightPrickContainsNt || totals.LeftPrickContainsNt),
                    RightLowerMotorTotal = GetSummaryStringFor(totals.RightLowerMotorTotal, totals.RightLowerMotorTotalHasImpairmentNotDueToSci, totals.RightLowerMotorContainsNt),
                    RightMotor = GetLevelsRange(totals.GetRightMotorValues(), false),
                    RightMotorZpp = isAsiaA ? GetLevelsRange(totals.GetRightMotorZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
                    RightMotorTotal = GetSummaryStringFor(totals.RightUpperMotorTotal + totals.RightLowerMotorTotal,
                                                            totals.RightUpperMotorTotalHasImpairmentNotDueToSci || totals.RightLowerMotorTotalHasImpairmentNotDueToSci,
                                                            totals.RightUpperMotorContainsNt || totals.RightLowerMotorContainsNt),
                    RightPrickTotal = GetSummaryStringFor(totals.RightPrickTotal, totals.RightPrickTotalHasImpairmentNotDueToSci, totals.RightPrickContainsNt),
                    RightSensory = GetLevelsRange(totals.GetRightSensoryValues(), false),
                    RightSensoryZpp = isAsiaA ? GetLevelsRange(totals.GetRightSensoryZppValues(), couldBeOtherThanAsiaA) : NotApplicable,
                    RightTouchTotal = GetSummaryStringFor(totals.RightTouchTotal, totals.RightTouchTotalHasImpairmentNotDueToSci, totals.RightTouchContainsNt),
                    RightUpperMotorTotal = GetSummaryStringFor(totals.RightUpperMotorTotal, totals.RightUpperMotorTotalHasImpairmentNotDueToSci, totals.RightUpperMotorContainsNt),
                    TouchTotal = GetSummaryStringFor(totals.RightTouchTotal + totals.LeftTouchTotal,
                                                        totals.RightTouchTotalHasImpairmentNotDueToSci || totals.LeftTouchTotalHasImpairmentNotDueToSci,
                                                        totals.RightTouchContainsNt || totals.LeftTouchContainsNt),
                    UpperMotorTotal = GetSummaryStringFor(totals.UpperMotorTotal,
                                                            totals.RightUpperMotorTotalHasImpairmentNotDueToSci || totals.LeftUpperMotorTotalHasImpairmentNotDueToSci,
                                                            totals.RightUpperMotorContainsNt || totals.LeftUpperMotorContainsNt)
                };

            return summary;
        }