/// <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>
        /// 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#
            BarcodeValue[][] 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 = -1;

            foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns())
            {
                column++;
                if (detectionResultColumn == null)
                {
                    continue;
                }
                foreach (Codeword codeword in detectionResultColumn.Codewords)
                {
                    if (codeword == null || codeword.RowNumber == -1)
                    {
                        continue;
                    }
                    barcodeMatrix[codeword.RowNumber][column].setValue(codeword.Value);
                }
            }
            return(barcodeMatrix);
        }
        /// <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>
        /// Adjusts the codeword count.
        /// </summary>
        /// <param name="detectionResult">Detection result.</param>
        /// <param name="barcodeMatrix">Barcode matrix.</param>
        private static void AdjustCodewordCount(DetectionResult detectionResult, BarcodeValue[][] barcodeMatrix)
        {
            int[] numberOfCodewords           = barcodeMatrix[0][1].GetValue();
            int   calculatedNumberOfCodewords = detectionResult.ColumnCount *
                                                detectionResult.RowCount -
                                                GetNumberOfECCodeWords(detectionResult.ErrorCorrectionLevel);

            if (numberOfCodewords.Length == 0)
            {
                if (calculatedNumberOfCodewords < 1 || calculatedNumberOfCodewords > PDF417Common.MAX_CODEWORDS_IN_BARCODE)
                {
                    throw ReaderException.Instance;
                }
                barcodeMatrix[0][1].SetValue(calculatedNumberOfCodewords);
            }
            else if (numberOfCodewords[0] != calculatedNumberOfCodewords)
            {
                // The calculated one is more reliable as it is derived from the row indicator columns
                barcodeMatrix[0][1].SetValue(calculatedNumberOfCodewords);
            }
        }
      /// <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>
 /// 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 + 1);
 }
      /// <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#
         BarcodeValue[][] 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 = -1;
         foreach (DetectionResultColumn detectionResultColumn in detectionResult.getDetectionResultColumns())
         {
            column++;
            if (detectionResultColumn == null)
            {
               continue;
            }
            foreach (Codeword codeword in detectionResultColumn.Codewords)
            {
               if (codeword == null || codeword.RowNumber == -1)
               {
                  continue;
               }
               barcodeMatrix[codeword.RowNumber][column].setValue(codeword.Value);
            }
         }
         return barcodeMatrix;
      }
 /// <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 (!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>
      /// 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)
      {
         int[] numberOfCodewords = barcodeMatrix[0][1].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;
            }
            barcodeMatrix[0][1].setValue(calculatedNumberOfCodewords);
         }
         else if (numberOfCodewords[0] != calculatedNumberOfCodewords)
         {
            // The calculated one is more reliable as it is derived from the row indicator columns
            barcodeMatrix[0][1].setValue(calculatedNumberOfCodewords);
         }

         return true;
      }
      /// <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));
 }
 /// <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);
     // System.Diagnostics.Debug.WriteLine("Before Adjustment: \n" + ToString(barcodeMatrix));
     AdjustCodewordCount(detectionResult, barcodeMatrix);
     // System.Diagnostics.Debug.WriteLine("After Adjustment: \n" + ToString(barcodeMatrix));
     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);
 }