'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _typeof = require('@babel/runtime/helpers/typeof');
var bluetoothBump_constants = require('./constants.js');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

var _slicedToArray__default = /*#__PURE__*/_interopDefaultLegacy(_slicedToArray);
var _typeof__default = /*#__PURE__*/_interopDefaultLegacy(_typeof);

function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
/** 生成 6 位随机大写临时 ID（写到广播特征值里） */
function genTempId() {
  return Math.random().toString(36).slice(2, 8).toUpperCase();
}
/** 把字符串编码为 ArrayBuffer，用于写入特征值 */
function str2ArrayBuffer(str) {
  var buf = new ArrayBuffer(str.length);
  var view = new Uint8Array(buf);
  for (var i = 0; i < str.length; i++) {
    view[i] = str.charCodeAt(i);
  }
  return buf;
}
/** ArrayBuffer 转十六进制大写字符串 */
function arrayBuffer2Hex(buf) {
  if (!buf) return '';
  var view = new Uint8Array(buf);
  var s = '';
  for (var i = 0; i < view.length; i++) {
    var h = view[i].toString(16);
    s += h.length < 2 ? "0".concat(h) : h;
  }
  return s.toUpperCase();
}
/** 反转十六进制字符串的字节序（小端 ↔ 大端） */
function reverseHexBytes(hex) {
  var s = '';
  for (var i = hex.length - 2; i >= 0; i -= 2) {
    s += hex.substr(i, 2);
  }
  return s;
}
/**
 * 判断扫到的设备是否携带我们的 service
 *
 * iOS 上 startBluetoothDevicesDiscovery 不传 services 时收到的设备都需要手动过滤。
 * 依次检查：advertisServiceUUIDs / serviceData / advertisData / 设备名前缀（BUMP_）。
 *
 * ⚠️ iOS↔iOS 时前 3 项几乎都拿不到，必须靠设备名前缀做最终兜底。
 */
function matchesService(dev, serviceUuid) {
  var target = serviceUuid.toUpperCase();
  if (Array.isArray(dev.advertisServiceUUIDs)) {
    var _iterator = _createForOfIteratorHelper(dev.advertisServiceUUIDs),
      _step;
    try {
      for (_iterator.s(); !(_step = _iterator.n()).done;) {
        var uuid = _step.value;
        if (typeof uuid === 'string' && uuid.toUpperCase() === target) return true;
      }
    } catch (err) {
      _iterator.e(err);
    } finally {
      _iterator.f();
    }
  }
  if (dev.serviceData && _typeof__default["default"](dev.serviceData) === 'object') {
    for (var _i = 0, _Object$keys = Object.keys(dev.serviceData); _i < _Object$keys.length; _i++) {
      var key = _Object$keys[_i];
      if (key.toUpperCase() === target) return true;
    }
  }
  if (dev.advertisData) {
    var hex = arrayBuffer2Hex(dev.advertisData);
    var uuidNoDash = serviceUuid.replace(/-/g, '').toUpperCase();
    var reversed = reverseHexBytes(uuidNoDash);
    if (hex.indexOf(reversed) >= 0 || hex.indexOf(uuidNoDash) >= 0) {
      return true;
    }
  }
  // 兜底：设备名以 BUMP_ 开头说明是同游戏的 Peripheral
  var nameStr = dev.localName || dev.name;
  if (nameStr && nameStr.indexOf(bluetoothBump_constants.BUMP_NAME_PREFIX) === 0) return true;
  return false;
}
/**
 * 判断这次扫描结果是否应该触发"碰一碰"
 *
 * 条件：
 *   1. RSSI 不低于阈值（够近）
 *   2. 距离上次同设备触发已超过冷却时间（避免重复）
 *
 * 抽成纯函数后，边界条件可以一行测一个。
 */
function shouldTriggerBump(params) {
  if (params.rssi < params.threshold) return false;
  if (params.now - params.lastBumpTime < params.cooldownMs) return false;
  return true;
}
// ==================== mutual 模式相关 ====================
/**
 * 把 myTempId / seenPeerId 组装为广播 payload 字符串。
 * 固定长度，便于解析：`${myTempId}|${seenPeerId}` → 13 字节。
 *
 * - myTempId 如果不足 TEMP_ID_LENGTH 位会自动补 '-'（防御性处理）
 * - seenPeerId 为空时填占位符 '------'
 */
function encodePayload(myTempId, seenPeerId) {
  var my = padOrTrim(myTempId || '', bluetoothBump_constants.TEMP_ID_LENGTH);
  var seen = seenPeerId ? padOrTrim(seenPeerId, bluetoothBump_constants.TEMP_ID_LENGTH) : bluetoothBump_constants.EMPTY_PEER_ID;
  return "".concat(my).concat(bluetoothBump_constants.PAYLOAD_SEPARATOR).concat(seen);
}
/**
 * 把 payload 编码进设备名（iOS Peripheral 唯一可控的广播字段）。
 *   `BUMP_<myTempId>_<seenPeerId>` → 例 `BUMP_ABC123_------`（19 字符）
 *
 * 不少机型对 localName 长度有限制（iOS Peripheral 推荐 ≤ 28 字节），这里 19 字符够用。
 */
function encodeBumpName(myTempId, seenPeerId) {
  var my = padOrTrim(myTempId || '', bluetoothBump_constants.TEMP_ID_LENGTH);
  var seen = seenPeerId ? padOrTrim(seenPeerId, bluetoothBump_constants.TEMP_ID_LENGTH) : bluetoothBump_constants.EMPTY_PEER_ID;
  return "".concat(bluetoothBump_constants.BUMP_NAME_PREFIX).concat(my, "_").concat(seen);
}
/**
 * 解析设备名形如 `BUMP_ABC123_XYZ789` → { myTempId, seenPeerId }。
 * 仅需 myTempId 时（旧格式 `BUMP_ABC123`）也兼容，seenPeerId 返回空串。
 */
function parseBumpName(name) {
  if (!name) return null;
  if (name.indexOf(bluetoothBump_constants.BUMP_NAME_PREFIX) !== 0) return null;
  var body = name.slice(bluetoothBump_constants.BUMP_NAME_PREFIX.length);
  // 可能是 "ABC123" 或 "ABC123_XYZ789"
  var parts = body.split('_');
  var my = parts[0];
  var seen = parts[1] || bluetoothBump_constants.EMPTY_PEER_ID;
  if (!my || my.length !== bluetoothBump_constants.TEMP_ID_LENGTH) return null;
  if (seen.length !== bluetoothBump_constants.TEMP_ID_LENGTH) return null;
  return {
    myTempId: my,
    seenPeerId: seen === bluetoothBump_constants.EMPTY_PEER_ID ? '' : seen
  };
}
/**
 * 解析广播 payload 字符串 → { myTempId, seenPeerId }
 * 解析失败返回 null（payload 不是本协议）。
 *
 * seenPeerId 为占位符 '------' 时返回空字符串，方便上层 `if (seenPeerId)` 判断。
 */
function parsePayloadString(raw) {
  if (!raw) return null;
  var parts = raw.split(bluetoothBump_constants.PAYLOAD_SEPARATOR);
  if (parts.length !== 2) return null;
  var _parts = _slicedToArray__default["default"](parts, 2),
    my = _parts[0],
    seen = _parts[1];
  if (!my || my.length !== bluetoothBump_constants.TEMP_ID_LENGTH) return null;
  if (!seen || seen.length !== bluetoothBump_constants.TEMP_ID_LENGTH) return null;
  return {
    myTempId: my,
    seenPeerId: seen === bluetoothBump_constants.EMPTY_PEER_ID ? '' : seen
  };
}
/**
 * 从扫描到的设备里提取 mutual 协议 payload。
 *
 * 解析顺序（按"iOS 上能拿到的可能性"由高到低）：
 *   1. 设备名 / localName 前缀 `BUMP_` —— iOS↔iOS 唯一可靠通道
 *   2. serviceData[serviceUuid] —— Android 标准方案
 *   3. advertisData 整段 —— 部分机型把 payload 塞这里
 *   4. 设备名按旧格式 `myTempId|seenPeerId` 解析（向后兼容）
 */
function parsePayload(dev, serviceUuid) {
  var target = serviceUuid.toUpperCase();
  // 1) 优先：设备名 BUMP_ 前缀（iOS↔iOS 必走这里）
  var nameStr = dev.localName || dev.name;
  if (nameStr) {
    var p = parseBumpName(nameStr);
    if (p) return p;
  }
  // 2) serviceData[serviceUuid]（Android 标准）
  if (dev.serviceData && _typeof__default["default"](dev.serviceData) === 'object') {
    for (var _i2 = 0, _Object$keys2 = Object.keys(dev.serviceData); _i2 < _Object$keys2.length; _i2++) {
      var key = _Object$keys2[_i2];
      if (key.toUpperCase() === target) {
        var str = arrayBufferToUtf8(dev.serviceData[key]);
        var _p = parsePayloadString(str);
        if (_p) return _p;
      }
    }
  }
  // 3) advertisData 整段（部分机型）
  if (dev.advertisData) {
    var _str = arrayBufferToUtf8(dev.advertisData);
    var _p2 = parsePayloadString(_str);
    if (_p2) return _p2;
  }
  // 4) 旧格式 name 兜底（向后兼容）
  if (nameStr) {
    var _p3 = parsePayloadString(nameStr);
    if (_p3) return _p3;
  }
  return null;
}
/** 从候选池里挑选"信号最强且在 TTL 内"的 peer（用于决定广播里要"点名"谁） */
function pickBestPeer(peers, now, ttlMs, rssiThreshold) {
  var best = null;
  var _iterator2 = _createForOfIteratorHelper(peers.values()),
    _step2;
  try {
    for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
      var rec = _step2.value;
      if (now - rec.lastSeen > ttlMs) continue;
      if (rec.rssi < rssiThreshold) continue;
      if (!best || rec.rssi > best.rssi) best = rec;
    }
  } catch (err) {
    _iterator2.e(err);
  } finally {
    _iterator2.f();
  }
  return best;
}
/** 清理候选池里过期的 peer（就地修改） */
function pruneExpiredPeers(peers, now, ttlMs) {
  var _iterator3 = _createForOfIteratorHelper(peers),
    _step3;
  try {
    for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
      var _step3$value = _slicedToArray__default["default"](_step3.value, 2),
        id = _step3$value[0],
        rec = _step3$value[1];
      if (now - rec.lastSeen > ttlMs) peers["delete"](id);
    }
  } catch (err) {
    _iterator3.e(err);
  } finally {
    _iterator3.f();
  }
}
// ==================== 内部辅助 ====================
/** ArrayBuffer 转 UTF-8 字符串（失败返回空字符串） */
function arrayBufferToUtf8(buf) {
  if (!buf) return '';
  try {
    var view = new Uint8Array(buf);
    var s = '';
    for (var i = 0; i < view.length; i++) {
      s += String.fromCharCode(view[i]);
    }
    return s;
  } catch (e) {
    return '';
  }
}
/** 定长裁剪/补齐（补 '-'） */
function padOrTrim(s, len) {
  if (s.length === len) return s;
  if (s.length > len) return s.slice(0, len);
  return s + '-'.repeat(len - s.length);
}

exports.arrayBuffer2Hex = arrayBuffer2Hex;
exports.encodeBumpName = encodeBumpName;
exports.encodePayload = encodePayload;
exports.genTempId = genTempId;
exports.matchesService = matchesService;
exports.parseBumpName = parseBumpName;
exports.parsePayload = parsePayload;
exports.parsePayloadString = parsePayloadString;
exports.pickBestPeer = pickBestPeer;
exports.pruneExpiredPeers = pruneExpiredPeers;
exports.reverseHexBytes = reverseHexBytes;
exports.shouldTriggerBump = shouldTriggerBump;
exports.str2ArrayBuffer = str2ArrayBuffer;
