#!/usr/bin/env python3
"""将指定 Windows BLE 配对注册表文件的全部字段转换为 BlueZ 片段。"""

from pathlib import Path
import argparse
import os
import re
import sys


def read_reg_file(path):
  raw = path.read_bytes()

  if raw.startswith((b"\xff\xfe", b"\xfe\xff")):
    return raw.decode("utf-16")

  if b"\x00" in raw[:128]:
    return raw.decode("utf-16le")

  return raw.decode("utf-8-sig")


def logical_lines(text):
  pending = ""

  for line in text.splitlines():
    line = line.strip()

    if not line:
      continue

    pending += line

    if pending.endswith("\\"):
      pending = pending[:-1]
      continue

    yield pending
    pending = ""

  if pending:
    yield pending


def parse_value(raw):
  match = re.fullmatch(
    r"dword:([0-9a-fA-F]{8})",
    raw,
    re.I
  )

  if match:
    return "dword", int(match.group(1), 16)

  match = re.fullmatch(
    r"hex(?:\(([0-9a-fA-F]+)\))?:(.*)",
    raw,
    re.I
  )

  if match:
    kind = (match.group(1) or "").lower()

    tokens = [
      item.strip()
      for item in match.group(2).split(",")
      if item.strip()
    ]

    if not all(
      re.fullmatch(r"[0-9a-fA-F]{2}", item)
      for item in tokens
    ):
      raise ValueError("Invalid hexadecimal registry value")

    return (
      f"hex({kind})",
      bytes(int(item, 16) for item in tokens)
    )

  raise ValueError("Unsupported registry value type or format")


def extract_device(text):
  section = None
  current_target = False
  values = {}

  for line in logical_lines(text):
    if line.startswith("[") and line.endswith("]"):
      name = line[1:-1]
      current_target = re.search(
        r"\\BTHPORT\\Parameters\\Keys\\[0-9a-f]{12}\\[0-9a-f]{12}$", name, re.I
      ) is not None
      if current_target:
        if section is not None:
          raise ValueError("文件包含多个设备，请导出单个 BLE 设备区段")
        section = name
      continue

    if current_target:
      match = re.fullmatch(r'"([^"]+)"=(.*)', line)
      if match:
        name = match.group(1)
        if any(existing.casefold() == name.casefold() for existing in values):
          raise ValueError(f"重复字段：{name}")
        values[name] = parse_value(match.group(2).strip())
      elif not line.startswith((";", "#")):
        raise ValueError("设备区段中存在无法转换的行")

  if section is None:
    raise ValueError("未找到 BTHPORT BLE 设备区段")
  return section, values


def get_bytes(values, name, size):
  kind, value = values[name.casefold()]

  if not kind.startswith("hex"):
    raise ValueError(f"{name} is not binary data")

  if len(value) != size:
    raise ValueError(
      f"{name}: expected {size} bytes, got {len(value)}"
    )

  return value


def get_dword(values, name):
  kind, value = values[name.casefold()]

  if kind != "dword":
    raise ValueError(f"{name} is not REG_DWORD")

  return value


def convert(text):
  section, source = extract_device(text)
  values = {name.casefold(): value for name, value in source.items()}
  adapter, device = section.rsplit("\\", 2)[-2:]
  adapter_address = ":".join(adapter[i:i + 2] for i in range(0, 12, 2)).upper()
  device_address = ":".join(device[i:i + 2] for i in range(0, 12, 2)).upper()

  ltk = get_bytes(values, "LTK", 16).hex().upper()
  irk = get_bytes(values, "IRK", 16).hex().upper()
  enc_size = get_dword(values, "KeyLength")
  ediv = get_dword(values, "EDIV")
  rand = int.from_bytes(get_bytes(values, "ERand", 8), "little")
  if not 7 <= enc_size <= 16 or not 0 <= ediv <= 65535:
    raise ValueError("KeyLength 或 EDIV 超出有效范围")

  address_type = None
  if "address" in values:
    address = int.from_bytes(get_bytes(values, "Address", 8), "little")
    if address != int(device, 16):
      raise ValueError("Address 与注册表设备区段地址不一致")
  if "addresstype" in values:
    address_type = get_dword(values, "AddressType")
    if address_type not in (0, 1):
      raise ValueError("AddressType 必须为 0 或 1")
    if address_type == 1 and int(device[:2], 16) & 0xC0 != 0xC0:
      raise ValueError("随机身份地址不是 static 地址")

  lines = [
    f"# Adapter={adapter_address}",
    f"# Device={device_address}",
    "# All source fields, converted:",
  ]
  for name, (kind, value) in source.items():
    if name.casefold() == "address":
      value = device_address
    elif kind == "hex(b)":
      if len(value) != 8:
        raise ValueError(f"{name}: REG_QWORD 必须为 8 字节")
      value = int.from_bytes(value, "little")
    elif isinstance(value, bytes):
      value = value.hex().upper()
    lines.append(f"# {name}={value}")

  lines.append("")
  if address_type is not None:
    lines.extend(["[General]", f"AddressType={'static' if address_type else 'public'}", ""])
  lines.extend([
    "[IdentityResolvingKey]",
    f"Key={irk}",
    "",
    "[LongTermKey]",
    "# Authenticated is not in this export; preserve its existing BlueZ value.",
    f"Key={ltk}",
    f"EncSize={enc_size}",
    f"EDiv={ediv}",
    f"Rand={rand}",
    "",
  ])
  return "\n".join(lines), len(source)


def main():
  parser = argparse.ArgumentParser(description=__doc__)
  parser.add_argument("reg_file", type=Path, help="要转换的 .reg 文件")
  args = parser.parse_args()
  try:
    output, count = convert(read_reg_file(args.reg_file))
    output_path = args.reg_file.with_name(f"{args.reg_file.stem}-bluez-snippet.ini")
    old_umask = os.umask(0o077)
    try:
      output_path.write_text(output, encoding="utf-8")
      output_path.chmod(0o600)
    finally:
      os.umask(old_umask)
    print(f"已转换全部 {count} 个源字段：{args.reg_file} -> {output_path}")
    return 0
  except (OSError, ValueError, KeyError) as error:
    print(f"转换失败：{error}", file=sys.stderr)
    return 1


if __name__ == "__main__":
  sys.exit(main())
