/// <summary>
        /// Adjusts the codeword count.
        /// </summary>
        /// <param name="detectionResult">Detection result.</param>
        /// <param name="barcodeMatrix">Barcode matrix.</param>
        private static bool adjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix)
        {
            var barcodeMatrix01 = barcodeMatrix[0][1];

            int[] numberOfCodewords           = barcodeMatrix01.getValue();
            int   calculatedNumberOfCodewords = detectionResult.ColumnCount *
                                                detectionResult.RowCount -
                                                getNumberOfECCodeWords(detectionResult.ErrorCorrectionLevel);

            if (numberOfCodewords.Length == 0)
            {
                if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE)
                {
                    return(false);
                }
                barcodeMatrix01.setValue(calculatedNumberOfCodewords);
            }
            else if (numberOfCodewords[0] != calculatedNumberOfCodewords)
            {
                // The calculated one is more reliable as it is derived from the row indicator columns
                barcodeMatrix01.setValue(calculatedNumberOfCodewords);
            }

            return(true);
        }
        /// <summary>
        /// Gets the start column.
        /// </summary>
        /// <returns>The start column.</returns>
        /// <param name="detectionResult">Detection result.</param>
        /// <param name="barcodeColumn">Barcode column.</param>
        /// <param name="imageRow">Image row.</param>
        /// <param name="leftToRight">If set to <c>true</c> left to right.</param>
        private static int getStartColumn(DetectionResult detectionResult,
                                          int barcodeColumn,
                                          int imageRow,
                                          bool leftToRight)
        {
            int      offset   = leftToRight ? 1 : -1;
            Codeword codeword = null;

            if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset))
            {
                codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodeword(imageRow);
            }
            if (codeword != null)
            {
                return(leftToRight ? codeword.EndX : codeword.StartX);
            }
            codeword = detectionResult.DetectionResultColumns[barcodeColumn].getCodewordNearby(imageRow);
            if (codeword != null)
            {
                return(leftToRight ? codeword.StartX : codeword.EndX);
            }
            if (isValidBarcodeColumn(detectionResult, barcodeColumn - offset))
            {
                codeword = detectionResult.DetectionResultColumns[barcodeColumn - offset].getCodewordNearby(imageRow);
            }
            if (codeword != null)
            {
                return(leftToRight ? codeword.EndX : codeword.StartX);
            }
            int skippedColumns = 0;

            while (isValidBarcodeColumn(detectionResult, barcodeColumn - offset))
            {
                barcodeColumn -= offset;
                foreach (Codeword previousRowCodeword in detectionResult.DetectionResultColumns[barcodeColumn].Codewords)
                {
                    if (previousRowCodeword != null)
                    {
                        return((leftToRight ? previousRowCodeword.EndX : previousRowCodeword.StartX) +
                               offset *
                               skippedColumns *
                               (previousRowCodeword.EndX - previousRowCodeword.StartX));
                    }
                }
                skippedColumns++;
            }
            return(leftToRight ? detectionResult.Box.MinX : detectionResult.Box.MaxX);
        }
        /// <summary>
        /// Creates the decoder result.
        /// </summary>
        /// <returns>The decoder result.</returns>
        /// <param name="detectionResult">Detection result.</param>
        private static DecoderResult createDecoderResult(DetectionResult detectionResult)
        {
            BarcodeValue[][] barcodeMatrix = createBarcodeMatrix(detectionResult);
            if (barcodeMatrix == null)
            {
                return(null);
            }

            if (!adjustCodewordCount(detectionResult, barcodeMatrix))
            {
                return(null);
            }
            List <int> erasures = new List <int>();

            int[]        codewords = new int[detectionResult.RowCount * detectionResult.ColumnCount];
            List <int[]> ambiguousIndexValuesList = new List <int[]>();
            List <int>   ambiguousIndexesList     = new List <int>();

            for (int row = 0; row < detectionResult.RowCount; row++)
            {
                for (int column = 0; column < detectionResult.ColumnCount; column++)
                {
                    int[] values        = barcodeMatrix[row][column + 1].getValue();
                    int   codewordIndex = row * detectionResult.ColumnCount + column;
                    if (values.Length == 0)
                    {
                        erasures.Add(codewordIndex);
                    }
                    else if (values.Length == 1)
                    {
                        codewords[codewordIndex] = values[0];
                    }
                    else
                    {
                        ambiguousIndexesList.Add(codewordIndex);
                        ambiguousIndexValuesList.Add(values);
                    }
                }
            }
            int[][] ambiguousIndexValues = new int[ambiguousIndexValuesList.Count][];
            for (int i = 0; i < ambiguousIndexValues.Length; i++)
            {
                ambiguousIndexValues[i] = ambiguousIndexValuesList[i];
            }
            return(createDecoderResultFromAmbiguousValues(detectionResult.ErrorCorrectionLevel, codewords,
                                                          erasures.ToArray(), ambiguousIndexesList.ToArray(), ambiguousIndexValues));
        }
        /// <summary>
        /// Creates the barcode matrix.
        /// </summary>
        /// <returns>The barcode matrix.</returns>
        /// <param name="detectionResult">Detection result.</param>
        private static BarcodeValue[][] createBarcodeMatrix(DetectionResult detectionResult)
        {
            // Manually setup Jagged Array in C#
            var barcodeMatrix = new BarcodeValue[detectionResult.RowCount][];

            for (int row = 0; row < barcodeMatrix.Length; row++)
            {
                barcodeMatrix[row] = new BarcodeValue[detectionResult.ColumnCount + 2];
                for (int col = 0; col < barcodeMatrix[row].Length; col++)
                {
                    barcodeMatrix[row][col] = new BarcodeValue();
                }
            }

            int column = 0;

            foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns())
            {
                if (detectionResultColumn != null)
                {
                    foreach (Codeword codeword in detectionResultColumn.Codewords)
                    {
                        if (codeword != null)
                        {
                            int rowNumber = codeword.RowNumber;
                            if (rowNumber >= 0)
                            {
                                if (rowNumber >= barcodeMatrix.Length)
                                {
                                    // We have more rows than the barcode metadata allows for, ignore them.
                                    continue;
                                }
                                barcodeMatrix[rowNumber][column].setValue(codeword.Value);
                            }
                        }
                    }
                }
                column++;
            }

            return(barcodeMatrix);
        }
        /// <summary>
        /// Decode the specified image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight, minCodewordWidth
        /// and maxCodewordWidth.
        /// TODO: don't pass in minCodewordWidth and maxCodewordWidth, pass in barcode columns for start and stop pattern
        /// columns. That way width can be deducted from the pattern column.
        /// This approach also allows to detect more details about the barcode, e.g. if a bar type (white or black) is wider
        /// than it should be. This can happen if the scanner used a bad blackpoint.
        /// </summary>
        /// <param name="image">Image.</param>
        /// <param name="imageTopLeft">Image top left.</param>
        /// <param name="imageBottomLeft">Image bottom left.</param>
        /// <param name="imageTopRight">Image top right.</param>
        /// <param name="imageBottomRight">Image bottom right.</param>
        /// <param name="minCodewordWidth">Minimum codeword width.</param>
        /// <param name="maxCodewordWidth">Max codeword width.</param>
        public static DecoderResult decode(BitMatrix image,
                                           ResultPoint imageTopLeft,
                                           ResultPoint imageBottomLeft,
                                           ResultPoint imageTopRight,
                                           ResultPoint imageBottomRight,
                                           int minCodewordWidth,
                                           int maxCodewordWidth)
        {
            BoundingBox boundingBox = BoundingBox.Create(image, imageTopLeft, imageBottomLeft, imageTopRight, imageBottomRight);

            if (boundingBox == null)
            {
                return(null);
            }

            DetectionResultRowIndicatorColumn leftRowIndicatorColumn  = null;
            DetectionResultRowIndicatorColumn rightRowIndicatorColumn = null;
            DetectionResult detectionResult = null;

            for (int i = 0; i < 2; i++)
            {
                if (imageTopLeft != null)
                {
                    leftRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopLeft, true, minCodewordWidth, maxCodewordWidth);
                }
                if (imageTopRight != null)
                {
                    rightRowIndicatorColumn = getRowIndicatorColumn(image, boundingBox, imageTopRight, false, minCodewordWidth, maxCodewordWidth);
                }
                detectionResult = merge(leftRowIndicatorColumn, rightRowIndicatorColumn);
                if (detectionResult == null)
                {
                    // TODO Based on Owen's Comments in <see cref="ZXing.ReaderException"/>, this method has been modified to continue silently
                    // if a barcode was not decoded where it was detected instead of throwing a new exception object.
                    return(null);
                }
                if (i == 0 && detectionResult.Box != null &&
                    (detectionResult.Box.MinY < boundingBox.MinY || detectionResult.Box.MaxY > boundingBox.MaxY))
                {
                    boundingBox = detectionResult.Box;
                }
                else
                {
                    detectionResult.Box = boundingBox;
                    break;
                }
            }
            int maxBarcodeColumn = detectionResult.ColumnCount + 1;

            detectionResult.DetectionResultColumns[0] = leftRowIndicatorColumn;

            detectionResult.DetectionResultColumns[maxBarcodeColumn] = rightRowIndicatorColumn;

            bool leftToRight = leftRowIndicatorColumn != null;

            for (int barcodeColumnCount = 1; barcodeColumnCount <= maxBarcodeColumn; barcodeColumnCount++)
            {
                int barcodeColumn = leftToRight ? barcodeColumnCount : maxBarcodeColumn - barcodeColumnCount;
                if (detectionResult.DetectionResultColumns[barcodeColumn] != null)
                {
                    // This will be the case for the opposite row indicator column, which doesn't need to be decoded again.
                    continue;
                }
                DetectionResultColumn detectionResultColumn;
                if (barcodeColumn == 0 || barcodeColumn == maxBarcodeColumn)
                {
                    detectionResultColumn = new DetectionResultRowIndicatorColumn(boundingBox, barcodeColumn == 0);
                }
                else
                {
                    detectionResultColumn = new DetectionResultColumn(boundingBox);
                }
                detectionResult.DetectionResultColumns[barcodeColumn] = detectionResultColumn;
                int startColumn         = -1;
                int previousStartColumn = startColumn;
                // TODO start at a row for which we know the start position, then detect upwards and downwards from there.
                for (int imageRow = boundingBox.MinY; imageRow <= boundingBox.MaxY; imageRow++)
                {
                    startColumn = getStartColumn(detectionResult, barcodeColumn, imageRow, leftToRight);
                    if (startColumn < 0 || startColumn > boundingBox.MaxX)
                    {
                        if (previousStartColumn == -1)
                        {
                            continue;
                        }
                        startColumn = previousStartColumn;
                    }
                    Codeword codeword = detectCodeword(image, boundingBox.MinX, boundingBox.MaxX, leftToRight,
                                                       startColumn, imageRow, minCodewordWidth, maxCodewordWidth);
                    if (codeword != null)
                    {
                        detectionResultColumn.setCodeword(imageRow, codeword);
                        previousStartColumn = startColumn;
                        minCodewordWidth    = Math.Min(minCodewordWidth, codeword.Width);
                        maxCodewordWidth    = Math.Max(maxCodewordWidth, codeword.Width);
                    }
                }
            }
            return(createDecoderResult(detectionResult));
        }
 /// <summary>
 /// Tests to see if the Barcode Column is Valid
 /// </summary>
 /// <returns><c>true</c>, if barcode column is valid, <c>false</c> otherwise.</returns>
 /// <param name="detectionResult">Detection result.</param>
 /// <param name="barcodeColumn">Barcode column.</param>
 private static bool isValidBarcodeColumn(DetectionResult detectionResult, int barcodeColumn)
 {
     return((barcodeColumn >= 0) && (barcodeColumn < detectionResult.DetectionResultColumns.Length));
 }