"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.CHECKSUM_LENGTH = void 0;
exports.createChecksum = createChecksum;
exports.decode = decode;
exports.decodeWithPrefix = decodeWithPrefix;
exports.detectCase = detectCase;
exports.encode = encode;
exports.expandPrefix = expandPrefix;
exports.verifyChecksum = verifyChecksum;

var _bitConverter = require("./bit-converter");

// Alphabet for Bech32
var CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'; // Checksum constant for Bech32m.

var BECH32M_CHECKSUM = 0x2bc830a3; // Minimum char code that could be present in the encoded message

var MIN_CHAR_CODE = 33; // Maximum char code that could be present in the encoded message

var MAX_CHAR_CODE = 126;
var CHECKSUM_LENGTH = 6; // Reverse lookup for characters

exports.CHECKSUM_LENGTH = CHECKSUM_LENGTH;

var CHAR_LOOKUP = function () {
  var lookup = new Map();

  for (var i = 0; i < CHARSET.length; i += 1) {
    lookup.set(CHARSET[i], i);
  }

  return lookup;
}(); // Poly generators


var GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];

function polymod(values) {
  return values.reduce(function (checksum, value) {
    var bits = checksum >> 25;
    var newChecksum = (checksum & 0x1ffffff) << 5 ^ value;
    return GEN.reduce(function (chk, gen, i) {
      return (bits >> i & 1) === 0 ? chk : chk ^ gen;
    }, newChecksum);
  },
  /* initial checksum */
  1);
}
/**
 * Expands a prefix into the specified output buffer.
 */


function expandPrefix(prefix, outBuffer) {
  for (var i = 0; i < prefix.length; i += 1) {
    var code = prefix.charCodeAt(i);
    outBuffer[i] = code >> 5;
    outBuffer[i + prefix.length + 1] = code & 31;
  }

  outBuffer[prefix.length] = 0;
}
/**
 * Verifies the checksum for a particular buffer.
 */


function verifyChecksum(buffer) {
  switch (polymod(buffer)) {
    case 1:
      return 'bech32';

    case BECH32M_CHECKSUM:
      return 'bech32m';

    default:
      return undefined;
  }
}
/**
 * Creates a checksum for a buffer and writes it to the last 6 5-bit groups
 * of the buffer.
 */


function createChecksum(buffer, encoding) {
  var checksumConstant;

  switch (encoding) {
    case 'bech32':
      checksumConstant = 1;
      break;

    case 'bech32m':
      checksumConstant = BECH32M_CHECKSUM;
      break;

    default:
      throw Error("Invalid encoding value: ".concat(encoding, "; expected bech32 or bech32m"));
  }

  var mod = polymod(buffer) ^ checksumConstant;

  for (var i = 0; i < CHECKSUM_LENGTH; i += 1) {
    var shift = 5 * (5 - i);
    buffer[buffer.length - CHECKSUM_LENGTH + i] = mod >> shift & 31;
  }
}
/**
 * Encodes an array of 5-bit groups into a string.
 *
 * @param {Uint8Array} buffer
 * @returns {string}
 *
 * @api private
 */


function encode(buffer) {
  return buffer.reduce(function (acc, bits) {
    return acc + CHARSET[bits];
  }, '');
}
/**
 * Decodes a string into an array of 5-bit groups.
 *
 * @param {string} message
 * @param {Uint8Array} [dst]
 *   Optional array to write the output to. If not specified, the array is created.
 * @returns {Uint8Array}
 *   Array with the result of decoding
 *
 * @throws {Error}
 *   if there are characters in `message` not present in the encoding alphabet
 *
 * @api private
 */


function decode(message, dst) {
  var realDst = dst || (0, _bitConverter.createBitArray)(message.length);

  for (var i = 0; i < message.length; i += 1) {
    var idx = CHAR_LOOKUP.get(message[i]);

    if (idx === undefined) {
      throw new Error("Invalid char in message: ".concat(message[i]));
    }

    realDst[i] = idx;
  }

  return realDst;
}
/**
 * Decodes a string and a human-readable prefix into an array of 5-bit groups.
 * The prefix is expanded as specified by Bech32.
 *
 * @param {string} prefix
 * @param {string} message
 * @returns {Uint8Array}
 *   Array with the result of decoding
 *
 * @api private
 */


function decodeWithPrefix(prefix, message) {
  var len = message.length + 2 * prefix.length + 1;
  var dst = (0, _bitConverter.createBitArray)(len);
  expandPrefix(prefix, dst.subarray(0, 2 * prefix.length + 1));
  decode(message, dst.subarray(2 * prefix.length + 1));
  return dst;
}
/**
 * Detects the character case used in `message`. If the message doesn't
 * contain either lower-case or upper-case chars, returns `null`.
 *
 * @param {string} message
 * @param {string} messageDescription
 *   Human-readable description of the message to put into an error message, should any occur
 * @returns {'lower'|'upper'|null}
 * @throws if the message contains both lowercase and uppercase chars,
 *   or contains chars not valid for Bech32(m) encoding
 */


function detectCase(message) {
  var messageDescription = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'message';
  var hasLowerCase = false;
  var hasUpperCase = false;

  for (var i = 0; i < message.length; i += 1) {
    var ord = message.charCodeAt(i); // 3. Allowed chars in the encoding

    if (ord < MIN_CHAR_CODE || ord > MAX_CHAR_CODE) {
      throw new TypeError("Invalid char in ".concat(messageDescription, ": ").concat(ord, "; ") + "should be in ASCII range ".concat(MIN_CHAR_CODE, "-").concat(MAX_CHAR_CODE));
    }

    hasUpperCase = hasUpperCase || ord >= 65 && ord <= 90;
    hasLowerCase = hasLowerCase || ord >= 97 && ord <= 122;
  }

  if (hasLowerCase && hasUpperCase) {
    throw new TypeError("Mixed-case ".concat(messageDescription));
  } else if (hasUpperCase) {
    return 'upper';
  } else if (hasLowerCase) {
    return 'lower';
  } else {
    return null;
  }
}