content: /*
   * Copyright 2006-2007 Jeremias Maerki.
   *
   * 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.
   */
  
  package com.ailiwean.core.zxing.core.datamatrix.encoder;
  
  
  class C40Encoder implements Encoder {
  
    @Override
    public int getEncodingMode() {
      return HighLevelEncoder.C40_ENCODATION;
    }
  
    @Override
    public void encode(EncoderContext context) {
      //step C
      StringBuilder buffer = new StringBuilder();
      while (context.hasMoreCharacters()) {
        char c = context.getCurrentChar();
        context.pos++;
  
        int lastCharSize = encodeChar(c, buffer);
  
        int unwritten = (buffer.length() / 3) * 2;
  
        int curCodewordCount = context.getCodewordCount() + unwritten;
        context.updateSymbolInfo(curCodewordCount);
        int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
  
        if (!context.hasMoreCharacters()) {
          //Avoid having a single C40 value in the last triplet
          StringBuilder removed = new StringBuilder();
          if ((buffer.length() % 3) == 2 && available != 2) {
            lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
          }
          while ((buffer.length() % 3) == 1 && (lastCharSize > 3 || available != 1)) {
            lastCharSize = backtrackOneCharacter(context, buffer, removed, lastCharSize);
          }
          break;
        }
  
        int count = buffer.length();
        if ((count % 3) == 0) {
          int newMode = HighLevelEncoder.lookAheadTest(context.getMessage(), context.pos, getEncodingMode());
          if (newMode != getEncodingMode()) {
            // Return to ASCII encodation, which will actually handle latch to new mode
            context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
            break;
          }
        }
      }
      handleEOD(context, buffer);
    }
  
    private int backtrackOneCharacter(EncoderContext context,
                                      StringBuilder buffer, StringBuilder removed, int lastCharSize) {
      int count = buffer.length();
      buffer.delete(count - lastCharSize, count);
      context.pos--;
      char c = context.getCurrentChar();
      lastCharSize = encodeChar(c, removed);
      context.resetSymbolInfo(); //Deal with possible reduction in symbol size
      return lastCharSize;
    }
  
    static void writeNextTriplet(EncoderContext context, StringBuilder buffer) {
      context.writeCodewords(encodeToCodewords(buffer, 0));
      buffer.delete(0, 3);
    }
  
    /**
     * Handle "end of data" situations
     *
     * @param context the encoder context
     * @param buffer  the buffer with the remaining encoded characters
     */
    void handleEOD(EncoderContext context, StringBuilder buffer) {
      int unwritten = (buffer.length() / 3) * 2;
      int rest = buffer.length() % 3;
  
      int curCodewordCount = context.getCodewordCount() + unwritten;
      context.updateSymbolInfo(curCodewordCount);
      int available = context.getSymbolInfo().getDataCapacity() - curCodewordCount;
  
      if (rest == 2) {
        buffer.append('\0'); //Shift 1
        while (buffer.length() >= 3) {
          writeNextTriplet(context, buffer);
        }
        if (context.hasMoreCharacters()) {
          context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
        }
      } else if (available == 1 && rest == 1) {
        while (buffer.length() >= 3) {
          writeNextTriplet(context, buffer);
        }
        if (context.hasMoreCharacters()) {
          context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
        }
        // else no unlatch
        context.pos--;
      } else if (rest == 0) {
        while (buffer.length() >= 3) {
          writeNextTriplet(context, buffer);
        }
        if (available > 0 || context.hasMoreCharacters()) {
          context.writeCodeword(HighLevelEncoder.C40_UNLATCH);
        }
      } else {
        throw new IllegalStateException("Unexpected case. Please report!");
      }
      context.signalEncoderChange(HighLevelEncoder.ASCII_ENCODATION);
    }
  
    int encodeChar(char c, StringBuilder sb) {
      if (c == ' ') {
        sb.append('\3');
        return 1;
      }
      if (c >= '0' && c <= '9') {
        sb.append((char) (c - 48 + 4));
        return 1;
      }
      if (c >= 'A' && c <= 'Z') {
        sb.append((char) (c - 65 + 14));
        return 1;
      }
      if (c < ' ') {
        sb.append('\0'); //Shift 1 Set
        sb.append(c);
        return 2;
      }
      if (c <= '/') {
        sb.append('\1'); //Shift 2 Set
        sb.append((char) (c - 33));
        return 2;
      }
      if (c <= '@') {
        sb.append('\1'); //Shift 2 Set
        sb.append((char) (c - 58 + 15));
        return 2;
      }
      if (c <= '_') {
        sb.append('\1'); //Shift 2 Set
        sb.append((char) (c - 91 + 22));
        return 2;
      }
      if (c <= 127) {
        sb.append('\2'); //Shift 3 Set
        sb.append((char) (c - 96));
        return 2;
      }
      sb.append("\1\u001e"); //Shift 2, Upper Shift
      int len = 2;
      len += encodeChar((char) (c - 128), sb);
      return len;
    }
  
    private static String encodeToCodewords(CharSequence sb, int startPos) {
      char c1 = sb.charAt(startPos);
      char c2 = sb.charAt(startPos + 1);
      char c3 = sb.charAt(startPos + 2);
      int v = (1600 * c1) + (40 * c2) + c3 + 1;
      char cw1 = (char) (v / 256);
      char cw2 = (char) (v % 256);
      return new String(new char[] {cw1, cw2});
    }
  
  }
  