public override Result decodeRow(int rowNumber, BitArray row, IDictionary <DecodeHintType, object> hints)
        {
            for (var index = 0; index < counters.Length; index++)
            {
                counters[index] = 0;
            }

            if (!setCounters(row))
            {
                return(null);
            }

            int startOffset = findStartPattern();

            if (startOffset < 0)
            {
                return(null);
            }

            int nextStart = startOffset;

            decodeRowResult.Length = 0;
            do
            {
                int charOffset = toNarrowWidePattern(nextStart);
                if (charOffset == -1)
                {
                    return(null);
                }
                // Hack: We store the position in the alphabet table into a
                // StringBuilder, so that we can access the decoded patterns in
                // validatePattern. We'll translate to the actual characters later.
                decodeRowResult.Append((char)charOffset);
                nextStart += 8;
                // Stop as soon as we see the end character.
                if (decodeRowResult.Length > 1 &&
                    arrayContains(STARTEND_ENCODING, ALPHABET[charOffset]))
                {
                    break;
                }
            } while (nextStart < counterLength); // no fixed end pattern so keep on reading while data is available

            // Look for whitespace after pattern:
            int trailingWhitespace = counters[nextStart - 1];
            int lastPatternSize    = 0;

            for (int i = -8; i < -1; i++)
            {
                lastPatternSize += counters[nextStart + i];
            }

            // We need to see whitespace equal to 50% of the last pattern size,
            // otherwise this is probably a false positive. The exception is if we are
            // at the end of the row. (I.e. the barcode barely fits.)
            if (nextStart < counterLength && trailingWhitespace < lastPatternSize / 2)
            {
                return(null);
            }

            if (!validatePattern(startOffset))
            {
                return(null);
            }

            // Translate character table offsets to actual characters.
            for (int i = 0; i < decodeRowResult.Length; i++)
            {
                decodeRowResult[i] = ALPHABET[decodeRowResult[i]];
            }
            // Ensure a valid start and end character
            char startchar = decodeRowResult[0];

            if (!arrayContains(STARTEND_ENCODING, startchar))
            {
                return(null);
            }
            char endchar = decodeRowResult[decodeRowResult.Length - 1];

            if (!arrayContains(STARTEND_ENCODING, endchar))
            {
                return(null);
            }

            // remove stop/start characters character and check if a long enough string is contained
            if (decodeRowResult.Length <= MIN_CHARACTER_LENGTH)
            {
                // Almost surely a false positive ( start + stop + at least 1 character)
                return(null);
            }

            if (!SupportClass.GetValue(hints, DecodeHintType.RETURN_CODABAR_START_END, false))
            {
                decodeRowResult.Remove(decodeRowResult.Length - 1, 1);
                decodeRowResult.Remove(0, 1);
            }

            int runningCount = 0;

            for (int i = 0; i < startOffset; i++)
            {
                runningCount += counters[i];
            }
            float left = runningCount;

            for (int i = startOffset; i < nextStart - 1; i++)
            {
                runningCount += counters[i];
            }
            float right = runningCount;

            var resultPointCallback = SupportClass.GetValue(hints, DecodeHintType.NEED_RESULT_POINT_CALLBACK, (ResultPointCallback)null);

            if (resultPointCallback != null)
            {
                resultPointCallback(new ResultPoint(left, rowNumber));
                resultPointCallback(new ResultPoint(right, rowNumber));
            }

            return(new Result(
                       decodeRowResult.ToString(),
                       null,
                       new[]
            {
                new ResultPoint(left, rowNumber),
                new ResultPoint(right, rowNumber)
            },
                       BarcodeFormat.CODABAR));
        }