#!/usr/bin/env python3
"""Parse Android btsnoop HCI log to find BLE ATT writes (LED control commands)."""
import struct
import os
import sys
from datetime import datetime

def parse_btsnoop_detailed(filepath):
    with open(filepath, 'rb') as f:
        header = f.read(16)
        magic = header[:8]
        version = struct.unpack('>I', header[8:12])[0]
        datalink = struct.unpack('>I', header[12:16])[0]
        print(f"Magic: {magic}, Version: {version}, Datalink: {datalink} (1002=Android HCI)")
        print()

        pkt_num = 0
        ble_writes = []
        interesting_pkts = []
        hci_commands = []
        hci_events = []

        while True:
            pkt_hdr = f.read(24)
            if len(pkt_hdr) < 24:
                break

            orig_len, inc_len, flags, drops, ts = struct.unpack('>IIIIq', pkt_hdr)
            data = f.read(inc_len)
            if len(data) < inc_len:
                break

            pkt_num += 1

            pkt_type = flags & 0x0F
            direction = (flags & 0x10) != 0  # 0=H->C, 1=C->H

            ts_sec = ts / 1000000.0

            if pkt_type == 0:
                hci_commands.append({'ts': ts_sec, 'data': data})
            elif pkt_type == 3:
                hci_events.append({'ts': ts_sec, 'data': data})
            elif pkt_type in (1, 17):  # ACL sent or received
                if len(data) >= 4:
                    acl_len = struct.unpack('<H', data[2:4])[0]
                    if len(data) >= 4 + acl_len:
                        actual = data[4:4+acl_len]
                        if len(actual) >= 4:
                            l2cap_len, cid = struct.unpack('<HH', actual[:4])
                            if cid == 4 and len(actual) >= 5:
                                att_opcode = actual[4]
                                if att_opcode in (0x52, 0x12):  # Write Request, Write Command
                                    att_handle = struct.unpack('<H', actual[5:7])[0] if len(actual) >= 7 else 0
                                    payload = actual[7:]
                                    ble_writes.append({
                                        'ts': ts_sec,
                                        'dir': 'SENT' if pkt_type == 1 else 'RECV',
                                        'h': att_handle,
                                        'op': att_opcode,
                                        'data': payload.hex(),
                                        'len_val': len(payload),
                                    })
                                elif att_opcode in (0x18, 0x1A):  # Notifications
                                    if len(actual) >= 7:
                                        att_handle = struct.unpack('<H', actual[5:7])[0]
                                        payload = actual[7:]
                                        interesting_pkts.append({
                                            'ts': ts_sec,
                                            'type': 'NOTIFY',
                                            'h': att_handle,
                                            'data': payload.hex()[:60]
                                        })

        print(f"Total packets: {pkt_num}")
        print(f"  HCI commands: {len(hci_commands)}")
        print(f"  HCI events: {len(hci_events)}")
        print(f"  BLE writes: {len(ble_writes)}")
        print(f"  Notifications: {len(interesting_pkts)}")
        print()

        if hci_commands:
            print("=== HCI Commands (first 30) ===")
            for c in hci_commands[:30]:
                ts_str = datetime.fromtimestamp(c['ts']).strftime('%H:%M:%S.%f')[:-3]
                opcode = struct.unpack('<H', c['data'][:2])[0] if len(c['data']) >= 2 else 0
                print(f"  {ts_str} cmd=0x{opcode:04x} data={c['data'][:20].hex()}")
            print()

        if interesting_pkts:
            print("=== Notifications (first 30) ===")
            for n in interesting_pkts[:30]:
                ts_str = datetime.fromtimestamp(n['ts']).strftime('%H:%M:%S.%f')[:-3]
                print(f"  {ts_str} h=0x{n['h']:04X} {n['data']}")
            print()

        if ble_writes:
            print("=== BLE Write Packets ===")
            for i, w in enumerate(ble_writes):
                ts_str = datetime.fromtimestamp(w['ts']).strftime('%H:%M:%S.%f')[:-3]
                print(f"[{i:3d}] {ts_str} {w['dir']} h=0x{w['h']:04X} len={w['len_val']:2d} data={w['data']}")
        else:
            print("No BLE writes found.")
            # Dump first 20 ACL packets
            print("\n=== First 20 ACL packets ===")
            with open(filepath, 'rb') as f:
                f.read(16)
                shown = 0
                for _ in range(pkt_num):
                    if shown >= 20: break
                    pkt_hdr = f.read(24)
                    if len(pkt_hdr) < 24: break
                    flags = struct.unpack('>I', pkt_hdr[8:12])[0]
                    inc_len = struct.unpack('>I', pkt_hdr[4:8])[0]
                    data = f.read(inc_len)
                    pkt_type = flags & 0x0F
                    if pkt_type in (1, 17):
                        ts = struct.unpack('>q', pkt_hdr[16:24])[0]
                        ts_str = datetime.fromtimestamp(ts/1000000.0).strftime('%H:%M:%S.%f')[:-3]
                        dir_str = '->' if pkt_type == 1 else '<-'
                        print(f"  {ts_str} {dir_str} len={inc_len:3d} {data[:40].hex()}")
                        shown += 1

if __name__ == '__main__':
    path = sys.argv[1] if len(sys.argv) > 1 else '/tmp/FS/data/misc/bluetooth/logs/btsnooz_hci.log'
    parse_btsnoop_detailed(path)
