/// <summary> <p>Reads format information from one of its two locations within the QR Code.</p>
        ///
        /// </summary>
        /// <returns> {@link FormatInformation} encapsulating the QR Code's format info
        /// </returns>
        /// <throws>  ReaderException if both format information locations cannot be parsed as </throws>
        /// <summary> the valid encoding of format information
        /// </summary>
        internal FormatInformation readFormatInformation()
        {
            if (parsedFormatInfo != null)
            {
                return(parsedFormatInfo);
            }

            // Read top-left format info bits
            int formatInfoBits = 0;

            for (int j = 1; j <= 8; j++)
            {
                formatInfoBits = copyBit(j, 8, formatInfoBits);
            }
            for (int i = 7; i > 0; i--)
            {
                formatInfoBits = copyBit(8, i, formatInfoBits);
            }

            parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
            if (parsedFormatInfo != null)
            {
                return(parsedFormatInfo);
            }

            throw ReaderException.Instance;
        }
        public override bool Equals(System.Object o)
        {
            if (!(o is FormatInformation))
            {
                return(false);
            }
            FormatInformation other = (FormatInformation)o;

            return(this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask);
        }
        /// <param name="maskedFormatInfo">format info indicator, with mask still applied
        /// </param>
        /// <returns> information about the format it specifies, or <code>null</code>
        /// if doesn't seem to match any known pattern
        /// </returns>
        internal static FormatInformation decodeFormatInformation(int maskedFormatInfo)
        {
            FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo);

            if (formatInfo != null)
            {
                return(formatInfo);
            }
            // Should return null, but, some QR codes apparently
            // do not mask this info. Try again by actually masking the pattern
            // first
            return(doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR));
        }
        /// <summary>
        /// <p>Reads the bits in the {@link BitMatrix} representing the finder pattern in the
        /// correct order in order to reconstitute the codewords bytes contained within the
        /// QR Code.</p>
        ///
        /// </summary>
        /// <returns> bytes encoded within the QR Code
        /// </returns>
        /// <throws>  ReaderException if the exact number of bytes expected is not read </throws>
        internal sbyte[] readCodewords()
        {
            FormatInformation formatInfo = readFormatInformation();
            Version           version    = readVersion();

            // Get the data mask for the format used in this QR Code. This will exclude
            // some bits from reading as we wind through the bit matrix.
            DataMask dataMask  = DataMask.forReference((int)formatInfo.DataMask);
            int      dimension = bitMatrix.Dimension;

            dataMask.unmaskBitMatrix(bitMatrix, dimension);

            BitMatrix functionPattern = version.buildFunctionPattern();
            bool      readingUp       = true;

            sbyte[] result       = new sbyte[version.TotalCodewords];
            int     resultOffset = 0;
            int     currentByte  = 0;
            int     bitsRead     = 0;

            //D3 in a Version M1 symbol, D11 in a Version M3-L symbol and D9
            //in a Version M3-M symbol is a 2 ¡Á 2 square 4-module block.
            int  numVersion    = version.VersionNumber;
            int  ecl           = formatInfo.ErrorCorrectionLevel.ordinal();
            bool has4mblock    = numVersion == 1 || numVersion == 3;
            int  _4mblockIndex = numVersion == 1 ? 3 : ecl == 1 ? 11 : 9;

            // Read columns in pairs, from right to left
            for (int j = dimension - 1; j > 0; j -= 2)
            {
                // Read alternatingly from bottom to top then top to bottom
                for (int count = 1; count < dimension; count++)
                {
                    int i = readingUp ? dimension - count : count;
                    for (int col = 0; col < 2; col++)
                    {
                        // Ignore bits covered by the function pattern
                        if (!functionPattern.get_Renamed(j - col, i))
                        {
                            // Read a bit
                            bitsRead++;
                            currentByte <<= 1;
                            if (bitMatrix.get_Renamed(j - col, i))
                            {
                                currentByte |= 1;
                            }

                            // If we've made a whole byte, save it off
                            if (bitsRead == 8 || (bitsRead == 4 && has4mblock && (_4mblockIndex - 1) == resultOffset))
                            {
                                result[resultOffset++] = (sbyte)currentByte;
                                bitsRead    = 0;
                                currentByte = 0;
                            }
                        }
                    }
                }
                readingUp ^= true; // readingUp = !readingUp; // switch directions
            }
            if (resultOffset != version.TotalCodewords)
            {
                throw ReaderException.Instance;
            }
            return(result);
        }
        /// <summary> <p>Reads format information from one of its two locations within the QR Code.</p>
        /// 
        /// </summary>
        /// <returns> {@link FormatInformation} encapsulating the QR Code's format info
        /// </returns>
        /// <throws>  ReaderException if both format information locations cannot be parsed as </throws>
        /// <summary> the valid encoding of format information
        /// </summary>
        internal FormatInformation readFormatInformation()
        {
            if (parsedFormatInfo != null)
            {
                return parsedFormatInfo;
            }

            // Read top-left format info bits
            int formatInfoBits = 0;
            for (int j = 1; j <= 8; j++)
            {
                formatInfoBits = copyBit(j, 8, formatInfoBits);
            }
            for (int i = 7; i > 0; i--)
            {
                formatInfoBits = copyBit(8, i, formatInfoBits);
            }

            parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits);
            if (parsedFormatInfo != null)
            {
                return parsedFormatInfo;
            }

            throw ReaderException.Instance;
        }