content: package com.espressif.iot.esptouch.protocol;
  
  import com.espressif.iot.esptouch.task.ICodeData;
  import com.espressif.iot.esptouch.util.ByteUtil;
  import com.espressif.iot.esptouch.util.CRC8;
  
  import java.net.InetAddress;
  import java.util.LinkedList;
  
  public class DatumCode implements ICodeData {
  
      // define by the Esptouch protocol, all of the datum code should add 1 at last to prevent 0
      private static final int EXTRA_LEN = 40;
      private static final int EXTRA_HEAD_LEN = 5;
  
      private final LinkedList<DataCode> mDataCodes;
  
      /**
       * Constructor of DatumCode
       *
       * @param apSsid      the Ap's ssid
       * @param apBssid     the Ap's bssid
       * @param apPassword  the Ap's password
       * @param ipAddress   the ip address of the phone or pad
       * @param isSsidHiden whether the Ap's ssid is hidden
       */
      public DatumCode(byte[] apSsid, byte[] apBssid, byte[] apPassword,
                       InetAddress ipAddress, boolean isSsidHiden) {
          // Data = total len(1 byte) + apPwd len(1 byte) + SSID CRC(1 byte) +
          // BSSID CRC(1 byte) + TOTAL XOR(1 byte)+ ipAddress(4 byte) + apPwd + apSsid apPwdLen <=
          // 105 at the moment
  
          // total xor
          char totalXor = 0;
  
          char apPwdLen = (char) apPassword.length;
          CRC8 crc = new CRC8();
          crc.update(apSsid);
          char apSsidCrc = (char) crc.getValue();
  
          crc.reset();
          crc.update(apBssid);
          char apBssidCrc = (char) crc.getValue();
  
          char apSsidLen = (char) apSsid.length;
          // hostname parse
          String ipAddrStrs[] = ipAddress.getHostAddress().split("\\.");
          int ipLen = ipAddrStrs.length;
  
          char ipAddrChars[] = new char[ipLen];
          // only support ipv4 at the moment
          for (int i = 0; i < ipLen; ++i) {
              ipAddrChars[i] = (char) Integer.parseInt(ipAddrStrs[i]);
          }
  
          char _totalLen = (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen);
          char totalLen = isSsidHiden ? (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen + apSsidLen)
                  : (char) (EXTRA_HEAD_LEN + ipLen + apPwdLen);
  
          // build data codes
          mDataCodes = new LinkedList<>();
          mDataCodes.add(new DataCode(_totalLen, 0));
          totalXor ^= _totalLen;
          mDataCodes.add(new DataCode(apPwdLen, 1));
          totalXor ^= apPwdLen;
          mDataCodes.add(new DataCode(apSsidCrc, 2));
          totalXor ^= apSsidCrc;
          mDataCodes.add(new DataCode(apBssidCrc, 3));
          totalXor ^= apBssidCrc;
          // ESPDataCode 4 is null
          for (int i = 0; i < ipLen; ++i) {
              mDataCodes.add(new DataCode(ipAddrChars[i], i + EXTRA_HEAD_LEN));
              totalXor ^= ipAddrChars[i];
          }
  
          byte[] apPwdBytes = apPassword;
          char[] apPwdChars = new char[apPwdBytes.length];
          for (int i = 0; i < apPwdBytes.length; i++) {
              apPwdChars[i] = ByteUtil.convertByte2Uint8(apPwdBytes[i]);
          }
          for (int i = 0; i < apPwdChars.length; i++) {
              mDataCodes.add(new DataCode(apPwdChars[i], i + EXTRA_HEAD_LEN + ipLen));
              totalXor ^= apPwdChars[i];
          }
  
          byte[] apSsidBytes = apSsid;
          char[] apSsidChars = new char[apSsidBytes.length];
          // totalXor will xor apSsidChars no matter whether the ssid is hidden
          for (int i = 0; i < apSsidBytes.length; i++) {
              apSsidChars[i] = ByteUtil.convertByte2Uint8(apSsidBytes[i]);
              totalXor ^= apSsidChars[i];
          }
          if (isSsidHiden) {
              for (int i = 0; i < apSsidChars.length; i++) {
                  mDataCodes.add(new DataCode(apSsidChars[i], i + EXTRA_HEAD_LEN + ipLen + apPwdLen));
              }
          }
  
          // add total xor last
          mDataCodes.add(4, new DataCode(totalXor, 4));
  
          // add bssid
          int bssidInsertIndex = EXTRA_HEAD_LEN;
          for (int i = 0; i < apBssid.length; i++) {
              int index = totalLen + i;
              char c = ByteUtil.convertByte2Uint8(apBssid[i]);
              DataCode dc = new DataCode(c, index);
              if (bssidInsertIndex >= mDataCodes.size()) {
                  mDataCodes.add(dc);
              } else {
                  mDataCodes.add(bssidInsertIndex, dc);
              }
              bssidInsertIndex += 4;
          }
      }
  
      @Override
      public byte[] getBytes() {
          byte[] datumCode = new byte[mDataCodes.size() * DataCode.DATA_CODE_LEN];
          int index = 0;
          for (DataCode dc : mDataCodes) {
              for (byte b : dc.getBytes()) {
                  datumCode[index++] = b;
              }
          }
          return datumCode;
      }
  
      @Override
      public String toString() {
          StringBuilder sb = new StringBuilder();
          byte[] dataBytes = getBytes();
          for (byte dataByte : dataBytes) {
              String hexString = ByteUtil.convertByte2HexString(dataByte);
              sb.append("0x");
              if (hexString.length() == 1) {
                  sb.append("0");
              }
              sb.append(hexString).append(" ");
          }
          return sb.toString();
      }
  
      @Override
      public char[] getU8s() {
          byte[] dataBytes = getBytes();
          int len = dataBytes.length / 2;
          char[] dataU8s = new char[len];
          byte high, low;
          for (int i = 0; i < len; i++) {
              high = dataBytes[i * 2];
              low = dataBytes[i * 2 + 1];
              dataU8s[i] = (char) (ByteUtil.combine2bytesToU16(high, low) + EXTRA_LEN);
          }
          return dataU8s;
      }
  }
  