public PairResult GetBamAlignmentsAndClassification(ReadPair readPair, IReadPairHandler pairHandler)
        {
            if (readPair.PairStatus == PairStatus.Duplicate)
            {
                // TODO hasIndels and numMismatches and split don't have meaning in an unusable dup, but it's a bit misleading to set them..
                // Also, it's kind of silly to extract those alignments if we're going to set it to unusable anyway.
                var alignments = readPair.GetAlignments().ToList();

                return(new PairResult(alignments: alignments, readPair: readPair, classification: PairClassification.Duplicate, hasIndels: false,
                                      isSplit: false, numMismatchesInSingleton: 0, softclipLengthForIndelRead: 0)
                {
                    IsReputableIndelContaining = false
                });
            }


            var classification = PairClassification.Unknown;
            IEnumerable <BamAlignment> bamAlignmentList = null;

            int?numMismatchesInR1 = null;
            int?numMismatchesInR2 = null;

            var r1HasIndels = OverlappingIndelHelpers.ReadContainsIndels(readPair.Read1);
            var r2HasIndels = OverlappingIndelHelpers.ReadContainsIndels(readPair.Read2);
            var hasIndels   = r1HasIndels || r2HasIndels;


            if (IsCompletedPairedPair(readPair))
            {
                if (BothReadsHighQuality(readPair))
                {
                    numMismatchesInR1 = readPair.Read1.GetIntTag("NM");
                    numMismatchesInR2 = readPair.Read2.GetIntTag("NM");

                    var tryStitch = true;

                    if (hasIndels)
                    {
                        if (numMismatchesInR1 == null && numMismatchesInR2 == null)
                        {
                            Logger.WriteWarningToLog(
                                $"Found indel-containing read without NM: '{readPair.Name}', likely indicating that NM is not set on any read. Consider preprocessing the BAM to calculate NM tags for best results.");
                        }

                        return(HandleIndelPairIfStitchUnallowed(readPair, numMismatchesInR1 ?? 0,
                                                                numMismatchesInR2 ?? 0, r1HasIndels, r2HasIndels));
                    }
                    else
                    {
                        // TODO if not realigning anything (or not realigning imperfects), go ahead and stitch immediately
                        // TODO why are we using this bool multiple times
                        // ^ Because there's no point checking for imperfectinos if we don't care about softclips -- we already know there are no indels because this is in the else of if(hasIndels)

                        if (!_trustSoftclips && (ReadContainsImperfections(readPair.Read1, _trustSoftclips) ||
                                                 ReadContainsImperfections(readPair.Read2, _trustSoftclips)))
                        {
                            tryStitch      = false;
                            classification =
                                ClassifySoftclipContainingPairGivenSoftclipDistrust(readPair, numMismatchesInR1,
                                                                                    numMismatchesInR2);
                            bamAlignmentList = readPair.GetAlignments();
                        }
                        else
                        {
                            if (numMismatchesInR1 == null)
                            {
                                numMismatchesInR1 = readPair.Read1.GetIntTag("NM");
                            }

                            if (numMismatchesInR2 == null)
                            {
                                numMismatchesInR2 = readPair.Read2.GetIntTag("NM");
                            }

                            if (numMismatchesInR1 >= _numMismatchesToBeConsideredMessy ||
                                numMismatchesInR2 >= _numMismatchesToBeConsideredMessy)
                            {
                                classification = PairClassification.UnstitchMessy;
                                tryStitch      = false;

                                if (numMismatchesInR1 <= 1 || numMismatchesInR2 <= 1)
                                {
                                    // One of the reads is clean

                                    tryStitch = false;

                                    if (numMismatchesInR1 <= 1)
                                    {
                                        // R1 is the clean one.
                                        if (readPair.Read2.IsReverseStrand())
                                        {
                                            classification = PairClassification.UnstitchReverseMessy;
                                        }
                                        else
                                        {
                                            classification = PairClassification.UnstitchForwardMessy;
                                        }
                                    }
                                    else
                                    {
                                        if (readPair.Read1.IsReverseStrand())
                                        {
                                            classification = PairClassification.UnstitchReverseMessy;
                                        }
                                        else
                                        {
                                            classification = PairClassification.UnstitchForwardMessy;
                                        }
                                    }
                                }

                                bamAlignmentList = readPair.GetAlignments();
                            }
                            else if (numMismatchesInR1 + numMismatchesInR2 == 0)
                            {
                                classification   = PairClassification.UnstitchPerfect;
                                bamAlignmentList = readPair.GetAlignments();
                            }
                            else if (numMismatchesInR1 <= 1 && numMismatchesInR2 <= 1)
                            {
                                classification   = PairClassification.UnstitchSingleMismatch;
                                bamAlignmentList = readPair.GetAlignments();
                            }
                            else
                            {
                                classification   = PairClassification.UnstitchImperfect;
                                bamAlignmentList = readPair.GetAlignments();
                            }
                        }

                        classification = AdjustClassificationForMultimapper(readPair, classification);
                    }

                    if (classification == PairClassification.UnstitchMessySuspiciousRead)
                    {
                        tryStitch = false;
                    }

                    if (_skipStitch)
                    {
                        tryStitch = false;
                    }

                    if (classification != PairClassification.UnstitchPerfect)
                    {
                        //For now we can't stitch anything else because we can't properly calculate NM!!
                        tryStitch = false;
                    }

                    if (!tryStitch)
                    {
                        if (bamAlignmentList == null)
                        {
                            bamAlignmentList = readPair.GetAlignments().ToList();
                            classification   = PairClassification.Unstitchable;
                        }
                    }
                    else
                    {
                        bamAlignmentList = TryStitch(readPair, pairHandler, out classification);
                    }
                }
                else if (OneReadIsHighQuality(readPair))
                {
                    classification = PairClassification.Split;
                    if (hasIndels)
                    {
                        numMismatchesInR1 = numMismatchesInR1 ?? readPair.Read1?.GetIntTag("NM") ?? 0;
                        numMismatchesInR2 = numMismatchesInR2 ?? readPair.Read2?.GetIntTag("NM") ?? 0;

                        return(HandlePairContainingIndels(readPair, r1HasIndels, r2HasIndels, numMismatchesInR1.Value,
                                                          numMismatchesInR2.Value, true, PairClassification.Split, true));
                    }
                }
                else
                {
                    classification   = PairClassification.Unusable;
                    bamAlignmentList = readPair.GetAlignments().ToList();
                }
            }
            else
            {
                numMismatchesInR1 = numMismatchesInR1 ?? readPair.Read1?.GetIntTag("NM") ?? 0;
                numMismatchesInR2 = numMismatchesInR2 ?? readPair.Read2?.GetIntTag("NM") ?? 0;
                return(ClassifyIncompletePair(readPair, r1HasIndels, r2HasIndels, numMismatchesInR1.Value, numMismatchesInR2.Value));
            }

            // TODO - not sure why I originally had this double-check on whether pairs were split? shouldn't this already be evident from the pair status?
            //var isSplit = bamAlignmentList?.Count() > 0 && bamAlignmentList?.Select(x => x.RefID).Distinct().Count() > 1;
            var isSplit = false;

            if (isSplit || classification == PairClassification.Split || readPair.PairStatus == PairStatus.SplitChromosomes ||
                readPair.PairStatus == PairStatus.MateNotFound || readPair.PairStatus == PairStatus.MateUnmapped)
            {
                return(HandleSplitNonIndelPair(readPair, bamAlignmentList, hasIndels, isSplit));
            }

            var pr = new PairResult(bamAlignmentList.ToList(), readPair, classification, hasIndels, isSplit);

            if (classification == PairClassification.UnstitchMessy || classification == PairClassification.UnstitchMessySuspiciousRead)
            {
                if (_checkMd && HasSuspiciousMd(readPair, numMismatchesInR1, numMismatchesInR2, pr))
                {
                    classification    = PairClassification.UnstitchMessySuspiciousMd;
                    pr.Classification = classification;
                }
            }

            return(pr);
        }
        private bool HasSuspiciousMd(ReadPair pair, int?nm1, int?nm2, PairResult pairResult = null)
        {
            try
            {
                // Assumption that this is only ever called on non-indel containing, paired reads. I know this to be the case but for future dev would be good to add checks in here.
                // ^ Sadly this assumption doesn't hold. I'm not sure whether it's valid MD format or not, but we've now seen reads come in with MDs that reflect indels and those indels have been softclipped away. So now we have to actively check.
                var md1 = pair.Read1.GetStringTag("MD");
                var md2 = pair.Read2.GetStringTag("MD");

                if (string.IsNullOrEmpty(md1) || string.IsNullOrEmpty(md2))
                {
                    return(false);
                }

                var mdCounts1 = Helper.GetMdCountsWithSubstitutions(md1, pairResult.ReadPair.Read1.Bases,
                                                                    pairResult.ReadPair.Read1.CigarData[0].Type == 'S'
                        ? (int)pairResult.ReadPair.Read1.CigarData[0].Length
                        : 0, (int)pairResult.ReadPair.Read1.CigarData.GetSuffixClip());
                var mdCounts2 = Helper.GetMdCountsWithSubstitutions(md2,
                                                                    pairResult.ReadPair.Read2.Bases,
                                                                    pairResult.ReadPair.Read2.CigarData[0].Type == 'S'
                        ? (int)pairResult.ReadPair.Read2.CigarData[0].Length
                        : 0, (int)pairResult.ReadPair.Read2.CigarData.GetSuffixClip());

                pairResult.md1 = mdCounts1;
                pairResult.md2 = mdCounts2;

                var mdTotal1 = mdCounts1.A + mdCounts1.T + mdCounts1.C + mdCounts1.G;
                var mdTotal2 = mdCounts2.A + mdCounts2.T + mdCounts2.C + mdCounts2.G;

                var numNs1 = mdTotal1 - nm1;
                var numNs2 = mdTotal2 - nm2;

                if (numNs1 > _numMismatchesToBeConsideredMessy || numNs2 > _numMismatchesToBeConsideredMessy)
                {
                    // We already know this pair is messy and now we find out that it's even messier than we thought: there are a bunch of Ns here.
                    return(true);
                }

                if (pair.DontOverlap.HasValue)
                {
                    return(false);
                }

                const int totalMdToBeConsideredSuspicious   = 8;
                const int runLengthsConsideredSuspicious    = 2;
                const int numInRunsToBeConsideredSuspicious = 4;

                var suspiciousBecauseOfTotalMd = mdTotal1 > totalMdToBeConsideredSuspicious ||
                                                 mdTotal2 > totalMdToBeConsideredSuspicious;

                var suspiciousBecauseOfMismatchesInRuns =
                    Math.Max(mdCounts1.NumInRuns, mdCounts2.NumInRuns) > numInRunsToBeConsideredSuspicious ||
                    Math.Max(mdCounts1.RunLength, mdCounts2.RunLength) > runLengthsConsideredSuspicious;

                if (suspiciousBecauseOfTotalMd || suspiciousBecauseOfMismatchesInRuns)
                {
                    // Only one of them has a very high mismatch count
                    if (OneMuchWorse(mdTotal1, mdTotal2, totalMdToBeConsideredSuspicious))
                    {
                        return(true);
                    }

                    // There is an imbalance in the mismatches that are seen, suggesting that they are not the same mismatches
                    // We have to allow for a little wiggle-room because they're not always perfectly overlapping, but if, say, there are twice as many X>C's on one mate 1 as there are on mate 2, it may indicate a sequencing error.
                    const int numMismatchesOfTypeToBeConsideredSuspicious = 4;
                    if (OneMuchWorse(mdCounts1.A, mdCounts2.A, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.T, mdCounts2.T, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.C, mdCounts2.C, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.G, mdCounts2.G, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.SubA, mdCounts2.SubA, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.SubT, mdCounts2.SubT, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.SubC, mdCounts2.SubC, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                    if (OneMuchWorse(mdCounts1.SubG, mdCounts2.SubG, numMismatchesOfTypeToBeConsideredSuspicious))
                    {
                        return(true);
                    }
                }

                return(false);
            }
            catch (ArgumentException e)
            {
                Logger.WriteWarningToLog(
                    $"Error parsing MD for {pair.Name}: {pair.Read1.CigarData} {pair.Read2.CigarData}, will treat as non-suspicious MD. Error details: {e.Message}");
                return(false);
            }
        }