// W13 BLE 协议公共组包模块
// 导出通用的组包构造与CRC16计算（支持 CCITT-FALSE 与 MODBUS）

const DEFAULT_HEAD = [0xCC, 0xC0];
const MAX_LEN = 548; // 文档声明的最大总长度（含包头与CRC）

export const COMMANDS = {
  READ_VERSION: 0x01,
  SET_CONDITION_1: 0x08,
  SET_CONDITION_2: 0x09,
  OTA_START: 0x0B,
  ENABLE_BLE_LOG: 0x0C,
  RADAR_STATUS: 0x11,
  TEST_KEYS: 0x13,
};

function toUint16LE(v) {
  const x = v & 0xFFFF;
  return [x & 0xFF, (x >>> 8) & 0xFF];
}

function ensureUint8Array(data) {
  if (!data) return new Uint8Array(0);
  if (data instanceof Uint8Array) return data;
  if (Array.isArray(data)) return Uint8Array.from(data.map(x => x & 0xFF));
  if (typeof data === 'string') {
    // 默认按ASCII编码；如需GBK请先外部转换后传入字节数组
    const arr = new Uint8Array(data.length);
    for (let i = 0; i < data.length; i++) arr[i] = data.charCodeAt(i) & 0xFF;
    return arr;
  }
  throw new TypeError('Unsupported payload type');
}

// CRC-16/CCITT-FALSE: poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000
export function crc16Ccitt(bytes, start = 0, end = bytes.length) {
  let crc = 0xFFFF;
  for (let i = start; i < end; i++) {
    crc ^= (bytes[i] << 8);
    for (let j = 0; j < 8; j++) {
      if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; else crc <<= 1;
      crc &= 0xFFFF;
    }
  }
  return crc & 0xFFFF;
}

// CRC-16/MODBUS: poly=0xA001, init=0xFFFF, refin=true, refout=true, xorout=0x0000
export function crc16Modbus(bytes, start = 0, end = bytes.length) {
  let crc = 0xFFFF;
  for (let i = start; i < end; i++) {
    crc ^= bytes[i];
    for (let j = 0; j < 8; j++) {
      const lsb = crc & 0x0001;
      crc >>= 1;
      if (lsb) crc ^= 0xA001;
    }
  }
  return crc & 0xFFFF;
}

function computeCrc(bytes, type, range) {
  const [start, end] = range || [0, bytes.length];
  if (type === 'MODBUS') return crc16Modbus(bytes, start, end);
  return crc16Ccitt(bytes, start, end);
}

/**
 * 构造W13协议数据帧
 * 字段：Head(2) + Len(2) + CRC(2) + Frame(2) + FramNum(2) + Frame_Type(1) + Parameters(N)
 * Len为整包总长度（含Head与CRC），小端；CRC16默认CCITT-FALSE，计算范围为整包除CRC字段自身。
 *
 * @param {Object} opts
 * @param {number} opts.frame 帧号(0~65535)
 * @param {number} opts.framNum 帧总数(0~65535)
 * @param {number} opts.type 命令字(0~255)
 * @param {Uint8Array|number[]|string} [opts.params] 参数字节或ASCII字符串
 * @param {('CCITT'|'MODBUS')} [opts.crcType] CRC算法选择，默认CCITT
 * @param {number[]} [opts.head] 包头，默认[0xCC,0xC0]
 * @returns {Uint8Array}
 */
export function buildPacket(opts) {
  const head = (opts && opts.head) || DEFAULT_HEAD;
  const frame = (opts && opts.frame) != null ? (opts.frame >>> 0) : 1;
  const framNum = (opts && opts.framNum) != null ? (opts.framNum >>> 0) : 1;
  const type = (opts && opts.type) != null ? (opts.type & 0xFF) : 0x00;
  const params = ensureUint8Array(opts && opts.params);
  const crcType = (opts && opts.crcType) === 'MODBUS' ? 'MODBUS' : 'CCITT';

  // 预分配：固定头(2) + 长度(2) + CRC(2) + Frame(2) + FramNum(2) + Type(1) + 参数
  const fixedLen = 2 + 2 + 2 + 2 + 2 + 1;
  const totalLen = fixedLen + params.length;
  if (totalLen > MAX_LEN) throw new RangeError(`Packet too long: ${totalLen} > ${MAX_LEN}`);

  const buf = new Uint8Array(totalLen);
  let off = 0;

  // Head
  buf[off++] = head[0] & 0xFF;
  buf[off++] = head[1] & 0xFF;

  // Len (占位后写入)
  const lenLE = toUint16LE(totalLen);
  buf[off++] = lenLE[0];
  buf[off++] = lenLE[1];

  // CRC (占位，稍后计算写入)
  const crcPos = off;
  buf[off++] = 0x00;
  buf[off++] = 0x00;

  // Frame
  const frameLE = toUint16LE(frame);
  buf[off++] = frameLE[0];
  buf[off++] = frameLE[1];

  // FramNum
  const framNumLE = toUint16LE(framNum);
  buf[off++] = framNumLE[0];
  buf[off++] = framNumLE[1];

  // Frame_Type
  buf[off++] = type;

  // Parameters
  buf.set(params, off);

  // 计算CRC：整包除CRC字段自身
  // 为避免实现差异，计算时将CRC位置的两个字节视为0
  buf[crcPos] = 0x00;
  buf[crcPos + 1] = 0x00;
  const crc = computeCrc(buf, crcType, [0, buf.length]);
  const crcLE = toUint16LE(crc);
  buf[crcPos] = crcLE[0];
  buf[crcPos + 1] = crcLE[1];

  return buf;
}

/**
 * 便捷构造：使用命令常量与简易参数数组
 * @param {number} cmd COMMANDS中的命令字
 * @param {number[]|Uint8Array|string} payload 参数
 * @param {{frame?:number, framNum?:number, crcType?:('CCITT'|'MODBUS'), head?:number[]}} [options]
 */
export function buildCommand(cmd, payload, options = {}) {
  return buildPacket({
    frame: options.frame != null ? options.frame : 1,
    framNum: options.framNum != null ? options.framNum : 1,
    type: cmd & 0xFF,
    params: payload,
    crcType: options.crcType === 'MODBUS' ? 'MODBUS' : 'CCITT',
    head: options.head || DEFAULT_HEAD,
  });
}

/**
 * 示例：构造“读版本号”命令帧 (Frame_Type=0x01, P0=0x00)
 * @returns {Uint8Array}
 */
export function buildReadVersion() {
  return buildCommand(COMMANDS.READ_VERSION, [0x00]);
}
