context: /*
   * Copyright 2013 ZXing authors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  
  using ZXing.Common;
  
  namespace ZXing.Aztec.Internal
  {
     /// <summary>
     /// This produces nearly optimal encodings of text into the first-level of
     /// encoding used by Aztec code.
     /// It uses a dynamic algorithm.  For each prefix of the string, it determines
     /// a set of encodings that could lead to this prefix.  We repeatedly add a
     /// character and generate a new set of optimal encodings until we have read
     /// through the entire input.
     /// @author Frank Yellin
     /// @author Rustam Abdullaev
     /// </summary>
     public sealed class HighLevelEncoder
     {
  
        internal static String[] MODE_NAMES = {"UPPER", "LOWER", "DIGIT", "MIXED", "PUNCT"};
  
        internal const int MODE_UPPER = 0; // 5 bits
        internal const int MODE_LOWER = 1; // 5 bits
        internal const int MODE_DIGIT = 2; // 4 bits
        internal const int MODE_MIXED = 3; // 5 bits
        internal const int MODE_PUNCT = 4; // 5 bits
  
        // The Latch Table shows, for each pair of Modes, the optimal method for
        // getting from one mode to another.  In the worst possible case, this can
        // be up to 14 bits.  In the best possible case, we are already there!
        // The high half-word of each entry gives the number of bits.
        // The low half-word of each entry are the actual bits necessary to change
        internal static readonly int[][] LATCH_TABLE = new int[][]
           {
              new[]
                 {
                    0,
                    (5 << 16) + 28, // UPPER -> LOWER
                    (5 << 16) + 30, // UPPER -> DIGIT
                    (5 << 16) + 29, // UPPER -> MIXED
                    (10 << 16) + (29 << 5) + 30, // UPPER -> MIXED -> PUNCT
                 },
              new[]
                 {
                    (9 << 16) + (30 << 4) + 14, // LOWER -> DIGIT -> UPPER
                    0,
                    (5 << 16) + 30, // LOWER -> DIGIT
                    (5 << 16) + 29, // LOWER -> MIXED
                    (10 << 16) + (29 << 5) + 30, // LOWER -> MIXED -> PUNCT
                 },
              new[]
                 {
                    (4 << 16) + 14, // DIGIT -> UPPER
                    (9 << 16) + (14 << 5) + 28, // DIGIT -> UPPER -> LOWER
                    0,
                    (9 << 16) + (14 << 5) + 29, // DIGIT -> UPPER -> MIXED
                    (14 << 16) + (14 << 10) + (29 << 5) + 30,
                    // DIGIT -> UPPER -> MIXED -> PUNCT
                 },
              new[]
                 {
                    (5 << 16) + 29, // MIXED -> UPPER
                    (5 << 16) + 28, // MIXED -> LOWER
                    (10 << 16) + (29 << 5) + 30, // MIXED -> UPPER -> DIGIT
                    0,
                    (5 << 16) + 30, // MIXED -> PUNCT
                 },
              new[]
                 {
                    (5 << 16) + 31, // PUNCT -> UPPER
                    (10 << 16) + (31 << 5) + 28, // PUNCT -> UPPER -> LOWER
                    (10 << 16) + (31 << 5) + 30, // PUNCT -> UPPER -> DIGIT
                    (10 << 16) + (31 << 5) + 29, // PUNCT -> UPPER -> MIXED
                    0,
                 },
           };
  
        // A reverse mapping from [mode][char] to the encoding for that character
        // in that mode.  An entry of 0 indicates no mapping exists.
        internal static readonly int[][] CHAR_MAP = new int[5][];
        // A map showing the available shift codes.  (The shifts to BINARY are not shown
        internal static readonly int[][] SHIFT_TABLE = new int[6][]; // mode shift codes, per table
        private readonly byte[] text;
  
        static HighLevelEncoder()
        {
           CHAR_MAP[0] = new int[256];
           CHAR_MAP[1] = new int[256];
           CHAR_MAP[2] = new int[256];
           CHAR_MAP[3] = new int[256];
           CHAR_MAP[4] = new int[256];
  
           SHIFT_TABLE[0] = new int[6];
           SHIFT_TABLE[1] = new int[6];
           SHIFT_TABLE[2] = new int[6];
           SHIFT_TABLE[3] = new int[6];
           SHIFT_TABLE[4] = new int[6];
           SHIFT_TABLE[5] = new int[6];
  
           CHAR_MAP[MODE_UPPER][' '] = 1;
           for (int c = 'A'; c <= 'Z'; c++)
           {
              CHAR_MAP[MODE_UPPER][c] = c - 'A' + 2;
           }
           CHAR_MAP[MODE_LOWER][' '] = 1;
           for (int c = 'a'; c <= 'z'; c++)
           {
              CHAR_MAP[MODE_LOWER][c] = c - 'a' + 2;
           }
           CHAR_MAP[MODE_DIGIT][' '] = 1;
           for (int c = '0'; c <= '9'; c++)
           {
              CHAR_MAP[MODE_DIGIT][c] = c - '0' + 2;
           }
           CHAR_MAP[MODE_DIGIT][','] = 12;
           CHAR_MAP[MODE_DIGIT]['.'] = 13;
           int[] mixedTable = {
                                 '\0', ' ', 1, 2, 3, 4, 5, 6, 7, '\b', '\t', '\n', 11, '\f', '\r',
                                 27, 28, 29, 30, 31, '@', '\\', '^', '_', '`', '|', '~', 127
                              };
           for (int i = 0; i < mixedTable.Length; i++)
           {
              CHAR_MAP[MODE_MIXED][mixedTable[i]] = i;
           }
           int[] punctTable =
              {
                 '\0', '\r', '\0', '\0', '\0', '\0', '!', '\'', '#', '$', '%', '&', '\'',
                 '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?',
                 '[', ']', '{', '}'
              };
           for (int i = 0; i < punctTable.Length; i++)
           {
              if (punctTable[i] > 0)
              {
                 CHAR_MAP[MODE_PUNCT][punctTable[i]] = i;
              }
           }
           foreach (int[] table in SHIFT_TABLE)
           {
              SupportClass.Fill(table, -1);
           }
           SHIFT_TABLE[MODE_UPPER][MODE_PUNCT] = 0;
  
           SHIFT_TABLE[MODE_LOWER][MODE_PUNCT] = 0;
           SHIFT_TABLE[MODE_LOWER][MODE_UPPER] = 28;
  
           SHIFT_TABLE[MODE_MIXED][MODE_PUNCT] = 0;
  
           SHIFT_TABLE[MODE_DIGIT][MODE_PUNCT] = 0;
           SHIFT_TABLE[MODE_DIGIT][MODE_UPPER] = 15;
        }
  
        public HighLevelEncoder(byte[] text)
        {
           this.text = text;
        }
  
        /// <summary>
        /// Convert the text represented by this High Level Encoder into a BitArray.
        /// </summary>
        /// <returns></returns>
        public BitArray encode()
        {
           ICollection<State> states = new Collection<State>();
           states.Add(State.INITIAL_STATE);
           for (int index = 0; index < text.Length; index++)
           {
              int pairCode;
              // don't remove the (int) type cast, mono compiler needs it
              int nextChar = (index + 1 < text.Length) ? (int)text[index + 1] : 0;
              switch (text[index])
              {
                 case (byte)'\r':
                    pairCode = nextChar == '\n' ? 2 : 0;
                    break;
                 case (byte)'.':
                    pairCode = nextChar == ' ' ? 3 : 0;
                    break;
                 case (byte)',':
                    pairCode = nextChar == ' ' ? 4 : 0;
                    break;
                 case (byte)':':
  
gt:                   pairCode = nextChar == ' ' ? 5 : 0;