#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WiFi 6/7 Feature Detector and Analyzer
========================================
Comprehensive analysis tool that detects and reports WiFi 6 (802.11ax) and
WiFi 7 (802.11be) features, roaming behavior, DFS channels, auto channel
selection, airtime fairness, security, QoS, power save, and advanced features.

Usage:
    python wifilyzer.py <pcap_file>
    python wifilyzer.py wifi61.pcap

Author: WiFi Analysis Suite
Version: 5.1.0
"""

import sys
import io
# Set UTF-8 encoding for Windows console
if sys.platform == 'win32':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')

from scapy.all import *
from scapy.layers.dot11 import *
from collections import defaultdict, Counter
import json
from datetime import datetime

# Import detection modules
from detection_modules.client_capabilities import detect_client_capabilities
from detection_modules.security_analysis import detect_security_analysis
from detection_modules.rrm_features import detect_rrm_features
from detection_modules.qos_analysis import detect_qos_analysis
from detection_modules.power_save import detect_power_save
from detection_modules.bandwidth_analysis import detect_bandwidth_analysis


class WiFi6FeatureDetector:
    """Detect and analyze WiFi 6 features in PCAP files"""

    def __init__(self, pcap_file):
        self.pcap_file = pcap_file
        self.packets = []
        self.results = {
            'file_info': {},
            'summary': {},
            'wifi6_features': {},
            'wifi7_features': {},
            'wifi_generation': {},
            'management_frames': {},
            'security': {},
            'clients': {},
            'access_points': {},
            'channels': {},
            'channel_width_details': [],
            'data_rates': {},
            'spatial_streams': {},
            'throughput_analysis': {},
            'roaming_analysis': {},
            'dfs_analysis': {},
            'auto_channel': {},
            'airtime_fairness': {},
            'beamforming': {},
            'spatial_reuse': {},
            'channel_interference': {},
            'client_capabilities': {},
            'security_analysis': {},
            'rrm_features': {},
            'qos_analysis': {},
            'power_save': {},
            'bandwidth_analysis': {},
            'issues': []
        }

    def load_pcap(self):
        """Load PCAP file"""
        try:
            print(f"[*] Loading PCAP: {self.pcap_file}")
            self.packets = rdpcap(self.pcap_file)
            print(f"[+] Loaded {len(self.packets)} packets\n")
            return True
        except Exception as e:
            print(f"[!] Error loading PCAP: {e}")
            return False

    def analyze_file_info(self):
        """Basic file information"""
        import os
        file_size = os.path.getsize(self.pcap_file)

        self.results['file_info'] = {
            'filename': self.pcap_file,
            'size_bytes': file_size,
            'size_kb': round(file_size / 1024, 2),
            'total_packets': len(self.packets),
            'analysis_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        }

    def analyze_summary(self):
        """Overall packet summary"""
        frame_types = Counter()
        protocols = Counter()

        for pkt in self.packets:
            if pkt.haslayer(Dot11):
                dot11 = pkt[Dot11]
                if dot11.type == 0:
                    frame_types['Management'] += 1
                elif dot11.type == 1:
                    frame_types['Control'] += 1
                elif dot11.type == 2:
                    frame_types['Data'] += 1

            if pkt.haslayer(EAPOL):
                protocols['EAPOL'] += 1
            if pkt.haslayer(ARP):
                protocols['ARP'] += 1
            if pkt.haslayer(DHCP):
                protocols['DHCP'] += 1
            if pkt.haslayer(DNS):
                protocols['DNS'] += 1

        self.results['summary'] = {
            'frame_types': dict(frame_types),
            'protocols': dict(protocols)
        }

    def detect_wifi_generation(self):
        """Detect WiFi generation (WiFi 4/5/6) for each packet"""
        wifi_gen_stats = {
            'wifi6_packets': [],
            'wifi5_packets': [],
            'wifi4_packets': [],
            'legacy_packets': []
        }

        for i, pkt in enumerate(self.packets, 1):
            generation = None

            # Check management frames for capability IEs
            if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp) or pkt.haslayer(Dot11AssoReq):
                if pkt.haslayer(Dot11Elt):
                    elt = pkt[Dot11Elt]
                    has_he = False
                    has_vht = False
                    has_ht = False

                    while elt:
                        if elt.ID == 255 and len(elt.info) > 0:  # Extension element
                            if elt.info[0] == 35:  # HE Capability
                                has_he = True
                        elif elt.ID == 191:  # VHT Capability
                            has_vht = True
                        elif elt.ID == 45:  # HT Capability
                            has_ht = True

                        elt = elt.payload.getlayer(Dot11Elt)

                    if has_he:
                        generation = 'WiFi 6 (802.11ax)'
                        wifi_gen_stats['wifi6_packets'].append(i)
                    elif has_vht:
                        generation = 'WiFi 5 (802.11ac)'
                        wifi_gen_stats['wifi5_packets'].append(i)
                    elif has_ht:
                        generation = 'WiFi 4 (802.11n)'
                        wifi_gen_stats['wifi4_packets'].append(i)
                    else:
                        generation = 'Legacy (802.11a/b/g)'
                        wifi_gen_stats['legacy_packets'].append(i)

        self.results['wifi_generation'] = {
            'wifi6_count': len(wifi_gen_stats['wifi6_packets']),
            'wifi5_count': len(wifi_gen_stats['wifi5_packets']),
            'wifi4_count': len(wifi_gen_stats['wifi4_packets']),
            'legacy_count': len(wifi_gen_stats['legacy_packets']),
            'wifi6_packets': wifi_gen_stats['wifi6_packets'][:20],  # First 20
            'wifi5_packets': wifi_gen_stats['wifi5_packets'][:20],
            'wifi4_packets': wifi_gen_stats['wifi4_packets'][:20],
            'legacy_packets': wifi_gen_stats['legacy_packets'][:20]
        }

    def detect_wifi6_features(self):
        """Detect WiFi 6 (802.11ax) features with detailed packet analysis"""
        features = {
            'ofdma_he_mu': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'su_mimo': {
                'detected': False,
                'count': 0,
                'mcs_values': [],
                'nss_values': [],
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'mu_mimo': {
                'detected': False,
                'count': 0,
                'group_ids': [],
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'he_capability': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'he_operation': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'bss_color': {
                'detected': False,
                'values': [],
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'spatial_reuse': {'detected': False, 'count': 0},
            'twt': {'detected': False, 'count': 0},
            'wifi6_ssids': []
        }

        for i, pkt in enumerate(self.packets, 1):
            # Check RadioTap for HE indicators
            if pkt.haslayer(RadioTap):
                rt = pkt[RadioTap]

                # Check for HE fields in present flags
                if hasattr(rt, 'present'):
                    present = rt.present if isinstance(rt.present, int) else 0

                    # HE is typically bit 23 (0x800000)
                    if present & 0x800000:
                        features['ofdma_he_mu']['detected'] = True
                        features['ofdma_he_mu']['count'] += 1
                        features['ofdma_he_mu']['packet_numbers'].append(i)

                        if len(features['ofdma_he_mu']['detection_details']) < 5:
                            features['ofdma_he_mu']['detection_details'].append({
                                'packet_number': i,
                                'layer': 'RadioTap',
                                'field': 'present',
                                'value': f'0x{present:08x}',
                                'description': 'RadioTap present flags with HE bit (bit 23) set',
                                'byte_position': 'RadioTap header bytes 4-7'
                            })

                # Check notdecoded for HE signatures
                if hasattr(rt, 'notdecoded'):
                    if len(rt.notdecoded) > 10:
                        if i not in features['ofdma_he_mu']['packet_numbers']:
                            features['ofdma_he_mu']['detected'] = True
                            features['ofdma_he_mu']['count'] += 1
                            features['ofdma_he_mu']['packet_numbers'].append(i)

                            if len(features['ofdma_he_mu']['detection_details']) < 5:
                                features['ofdma_he_mu']['detection_details'].append({
                                    'packet_number': i,
                                    'layer': 'RadioTap',
                                    'field': 'notdecoded',
                                    'value': f'{len(rt.notdecoded)} bytes',
                                    'description': 'RadioTap HE data not fully decoded by Scapy',
                                    'byte_position': f'RadioTap header offset +{rt.len}'
                                })

                # Check for MCS/NSS (SU-MIMO indicators)
                if hasattr(rt, 'MCS'):
                    mcs = rt.MCS
                    features['su_mimo']['detected'] = True
                    features['su_mimo']['count'] += 1
                    features['su_mimo']['mcs_values'].append(mcs)
                    features['su_mimo']['packet_numbers'].append(i)

                    if len(features['su_mimo']['detection_details']) < 5:
                        features['su_mimo']['detection_details'].append({
                            'packet_number': i,
                            'layer': 'RadioTap',
                            'field': 'MCS',
                            'value': f'{mcs}',
                            'description': f'MCS index indicates spatial multiplexing capability',
                            'byte_position': 'RadioTap MCS field'
                        })

                # Check for VHT (MU-MIMO)
                if hasattr(rt, 'VHT'):
                    features['mu_mimo']['detected'] = True
                    features['mu_mimo']['count'] += 1
                    features['mu_mimo']['packet_numbers'].append(i)

                    if len(features['mu_mimo']['detection_details']) < 5:
                        features['mu_mimo']['detection_details'].append({
                            'packet_number': i,
                            'layer': 'RadioTap',
                            'field': 'VHT',
                            'value': 'Present',
                            'description': 'VHT field indicates MU-MIMO capability',
                            'byte_position': 'RadioTap VHT field'
                        })

            # Check management frames for WiFi 6 IEs
            if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
                # Check for HE Capability IE (ID 255, Extension 35)
                if pkt.haslayer(Dot11Elt):
                    elt = pkt[Dot11Elt]
                    while elt:
                        if elt.ID == 255:  # Extension element
                            if len(elt.info) > 0 and elt.info[0] == 35:  # HE Capability
                                features['he_capability']['detected'] = True
                                features['he_capability']['count'] += 1
                                if i not in features['he_capability']['packet_numbers']:
                                    features['he_capability']['packet_numbers'].append(i)

                                    if len(features['he_capability']['detection_details']) < 5:
                                        features['he_capability']['detection_details'].append({
                                            'packet_number': i,
                                            'layer': 'Dot11Elt (802.11 Information Element)',
                                            'field': 'Element ID Extension',
                                            'tag_id': '255 (Extension)',
                                            'extension_id': '35 (HE Capabilities)',
                                            'value': f'Length: {len(elt.info)} bytes',
                                            'description': 'HE Capability IE advertises WiFi 6 support',
                                            'raw_bytes': f'{elt.info[:min(10, len(elt.info))].hex()}'
                                        })

                                # Try to extract BSS Color (in HE Operation params)
                                if len(elt.info) > 3:
                                    # BSS Color is in bits 0-5 of byte 1 (after extension ID)
                                    bss_color = elt.info[1] & 0x3F
                                    if bss_color > 0:
                                        features['bss_color']['detected'] = True
                                        features['bss_color']['values'].append(bss_color)
                                        if i not in features['bss_color']['packet_numbers']:
                                            features['bss_color']['packet_numbers'].append(i)

                                            if len(features['bss_color']['detection_details']) < 5:
                                                # Calculate partial BSS Color info
                                                partial_bss = (elt.info[1] >> 6) & 0x01
                                                features['bss_color']['detection_details'].append({
                                                    'packet_number': i,
                                                    'layer': 'Dot11Elt (HE Capability IE)',
                                                    'field': 'HE MAC Capabilities',
                                                    'tag_id': '255 (Extension)',
                                                    'extension_id': '35 (HE Capabilities)',
                                                    'byte_offset': 'Byte 1 after Extension ID',
                                                    'raw_byte': f'0x{elt.info[1]:02x}',
                                                    'bss_color_value': bss_color,
                                                    'bss_color_bits': 'Bits 0-5',
                                                    'partial_bss_color': 'Enabled' if partial_bss else 'Disabled',
                                                    'description': f'BSS Color = {bss_color} (extracted from bits 0-5 of byte 1)',
                                                    'calculation': f'0x{elt.info[1]:02x} & 0x3F = {bss_color}'
                                                })

                            if len(elt.info) > 0 and elt.info[0] == 36:  # HE Operation
                                features['he_operation']['detected'] = True
                                features['he_operation']['count'] += 1
                                if i not in features['he_operation']['packet_numbers']:
                                    features['he_operation']['packet_numbers'].append(i)

                                    if len(features['he_operation']['detection_details']) < 5:
                                        # Parse HE Operation parameters
                                        he_op_params = ''
                                        if len(elt.info) > 3:
                                            he_op_byte1 = elt.info[1]
                                            he_op_byte2 = elt.info[2]
                                            vht_op_present = (he_op_byte1 >> 6) & 0x01
                                            co_located_bss = (he_op_byte1 >> 7) & 0x01
                                            he_op_params = f'VHT Op Present: {vht_op_present}, Co-located BSS: {co_located_bss}'

                                        features['he_operation']['detection_details'].append({
                                            'packet_number': i,
                                            'layer': 'Dot11Elt (802.11 Information Element)',
                                            'field': 'Element ID Extension',
                                            'tag_id': '255 (Extension)',
                                            'extension_id': '36 (HE Operation)',
                                            'value': f'Length: {len(elt.info)} bytes',
                                            'parameters': he_op_params if he_op_params else 'N/A',
                                            'description': 'HE Operation IE defines WiFi 6 operation parameters',
                                            'raw_bytes': f'{elt.info[:min(10, len(elt.info))].hex()}'
                                        })

                        elt = elt.payload.getlayer(Dot11Elt)

                # Track WiFi 6 SSIDs
                if pkt.haslayer(Dot11Elt):
                    ssid_elt = pkt.getlayer(Dot11Elt, ID=0)
                    if ssid_elt and features['he_capability']['detected']:
                        try:
                            ssid = ssid_elt.info.decode('utf-8')
                            if ssid and ssid not in features['wifi6_ssids']:
                                features['wifi6_ssids'].append(ssid)
                        except:
                            pass

        # Set not_detected reasons
        if not features['ofdma_he_mu']['detected']:
            features['ofdma_he_mu']['not_detected_reason'] = 'No RadioTap headers with HE present flag (bit 23 = 0x800000) found. OFDMA requires WiFi 6 data frames with RadioTap HE MU fields.'

        if not features['su_mimo']['detected']:
            features['su_mimo']['not_detected_reason'] = 'No RadioTap MCS field found. SU-MIMO detection requires data frames with RadioTap MCS information.'

        if not features['mu_mimo']['detected']:
            features['mu_mimo']['not_detected_reason'] = 'No RadioTap VHT field found. MU-MIMO detection requires data frames with RadioTap VHT information.'

        if not features['he_capability']['detected']:
            features['he_capability']['not_detected_reason'] = 'No 802.11 Information Element with ID=255 (Extension) and Extension ID=35 (HE Capability) found in management frames.'

        if not features['he_operation']['detected']:
            features['he_operation']['not_detected_reason'] = 'No 802.11 Information Element with ID=255 (Extension) and Extension ID=36 (HE Operation) found in management frames.'

        if not features['bss_color']['detected']:
            features['bss_color']['not_detected_reason'] = 'No BSS Color value found. BSS Color is in HE Capability IE (ID 255, Ext 35), byte 1, bits 0-5. Either IE not present or BSS Color = 0.'

        # Limit packet numbers to first 20 for display
        for key in features:
            if isinstance(features[key], dict) and 'packet_numbers' in features[key]:
                features[key]['packet_numbers'] = features[key]['packet_numbers'][:20]

        self.results['wifi6_features'] = features

    def analyze_management_frames(self):
        """Analyze management frame details"""
        mgmt = {
            'beacons': 0,
            'probe_requests': 0,
            'probe_responses': 0,
            'authentication': 0,
            'association_request': 0,
            'association_response': 0,
            'deauthentication': 0,
            'disassociation': 0,
            'action_frames': 0,
            'ssids': [],
            'channels': []
        }

        for pkt in self.packets:
            if pkt.haslayer(Dot11Beacon):
                mgmt['beacons'] += 1
                # Extract SSID
                if pkt.haslayer(Dot11Elt):
                    ssid_elt = pkt.getlayer(Dot11Elt, ID=0)
                    if ssid_elt:
                        try:
                            ssid = ssid_elt.info.decode('utf-8')
                            if ssid and ssid not in mgmt['ssids']:
                                mgmt['ssids'].append(ssid)
                        except:
                            pass

                    # Extract channel
                    ds_elt = pkt.getlayer(Dot11Elt, ID=3)
                    if ds_elt and len(ds_elt.info) > 0:
                        channel = ds_elt.info[0]
                        if channel not in mgmt['channels']:
                            mgmt['channels'].append(channel)

            elif pkt.haslayer(Dot11ProbeReq):
                mgmt['probe_requests'] += 1
            elif pkt.haslayer(Dot11ProbeResp):
                mgmt['probe_responses'] += 1
            elif pkt.haslayer(Dot11Auth):
                mgmt['authentication'] += 1
            elif pkt.haslayer(Dot11AssoReq):
                mgmt['association_request'] += 1
            elif pkt.haslayer(Dot11AssoResp):
                mgmt['association_response'] += 1
            elif pkt.haslayer(Dot11Deauth):
                mgmt['deauthentication'] += 1
            elif pkt.haslayer(Dot11Disas):
                mgmt['disassociation'] += 1
            elif pkt.haslayer(Dot11Action):
                mgmt['action_frames'] += 1

        self.results['management_frames'] = mgmt

    def analyze_security(self):
        """Analyze security features"""
        security = {
            'eapol_frames': 0,
            'eapol_messages': {'msg1': 0, 'msg2': 0, 'msg3': 0, 'msg4': 0},
            'wpa_versions': [],
            'cipher_suites': [],
            'akm_suites': [],
            'pmf_capable': False
        }

        for pkt in self.packets:
            if pkt.haslayer(EAPOL):
                security['eapol_frames'] += 1

                # Try to identify EAPOL message number
                if hasattr(pkt[EAPOL], 'key_info'):
                    key_info = pkt[EAPOL].key_info

                    # Message identification based on key_info flags
                    if key_info in [0x008a, 0x00ca]:
                        security['eapol_messages']['msg1'] += 1
                    elif key_info == 0x010a:
                        security['eapol_messages']['msg2'] += 1
                    elif key_info in [0x13ca, 0x13ea]:
                        security['eapol_messages']['msg3'] += 1
                    elif key_info == 0x030a:
                        security['eapol_messages']['msg4'] += 1

            # Check for RSN/WPA information elements
            if pkt.haslayer(Dot11Elt):
                elt = pkt[Dot11Elt]
                while elt:
                    if elt.ID == 48:  # RSN IE
                        if 'WPA2' not in security['wpa_versions']:
                            security['wpa_versions'].append('WPA2')
                    elif elt.ID == 221 and len(elt.info) > 4:  # Vendor Specific
                        # WPA OUI: 00:50:f2:01
                        if elt.info[:4] == b'\x00\x50\xf2\x01':
                            if 'WPA' not in security['wpa_versions']:
                                security['wpa_versions'].append('WPA')

                    elt = elt.payload.getlayer(Dot11Elt)

        self.results['security'] = security

    def analyze_clients_aps(self):
        """Identify clients and access points"""
        aps = {}
        clients = set()

        for pkt in self.packets:
            if pkt.haslayer(Dot11):
                dot11 = pkt[Dot11]

                # APs from beacons
                if pkt.haslayer(Dot11Beacon):
                    bssid = dot11.addr3 if hasattr(dot11, 'addr3') else None
                    if bssid:
                        if bssid not in aps:
                            aps[bssid] = {'ssid': '', 'channel': 0, 'packets': 0}
                        aps[bssid]['packets'] += 1

                        # Get SSID
                        if pkt.haslayer(Dot11Elt):
                            ssid_elt = pkt.getlayer(Dot11Elt, ID=0)
                            if ssid_elt:
                                try:
                                    aps[bssid]['ssid'] = ssid_elt.info.decode('utf-8')
                                except:
                                    pass

                # Clients from data frames
                elif pkt.haslayer(Dot11):
                    # Type 2 = Data
                    if dot11.type == 2:
                        src = dot11.addr2 if hasattr(dot11, 'addr2') else None
                        dst = dot11.addr1 if hasattr(dot11, 'addr1') else None

                        # Typically, if bit 1 of addr1 is 0, it's unicast (client)
                        if src and src not in aps:
                            clients.add(src)
                        if dst and dst not in aps and not (int(dst.split(':')[0], 16) & 0x01):
                            clients.add(dst)

        self.results['access_points'] = aps
        self.results['clients'] = {
            'count': len(clients),
            'addresses': list(clients)
        }

    def analyze_channels(self):
        """Analyze channel usage with detailed packet tracking"""
        channels = Counter()
        channel_width_details = []
        channel_widths = Counter()

        for i, pkt in enumerate(self.packets, 1):
            if pkt.haslayer(RadioTap) and hasattr(pkt[RadioTap], 'Channel') and pkt[RadioTap].Channel is not None:
                freq = pkt[RadioTap].Channel

                # Convert frequency to channel number
                if 2412 <= freq <= 2484:  # 2.4 GHz
                    channel = (freq - 2412) // 5 + 1
                elif 5180 <= freq <= 5825:  # 5 GHz
                    channel = (freq - 5000) // 5
                else:
                    channel = freq

                channels[channel] += 1            # Check for HT/VHT/HE operation for channel width
            if pkt.haslayer(Dot11Elt):
                elt = pkt[Dot11Elt]
                while elt:
                    if elt.ID == 61:  # HT Operation (802.11n)
                        if len(elt.info) > 0:
                            # Bit 2 (0x04) = STA Channel Width
                            is_40mhz = (elt.info[0] & 0x04) != 0
                            width = "40MHz" if is_40mhz else "20MHz"
                            channel_widths[width] += 1

                            # Store detailed info (limit to 50 entries)
                            if len(channel_width_details) < 50:
                                channel_width_details.append({
                                    'packet_number': i,
                                    'width': width,
                                    'source_field': 'HT Operation IE (ID 61)',
                                    'field_byte': f'0x{elt.info[0]:02x}',
                                    'wifi_generation': 'WiFi 4 (802.11n)'
                                })

                    elif elt.ID == 192:  # VHT Operation (802.11ac)
                        if len(elt.info) > 0:
                            # Byte 0: Channel Width
                            width_map = {0: "20/40MHz", 1: "80MHz", 2: "160MHz", 3: "80+80MHz"}
                            width_val = elt.info[0]
                            width = width_map.get(width_val, f"Unknown({width_val})")
                            channel_widths[width] += 1

                            if len(channel_width_details) < 50:
                                channel_width_details.append({
                                    'packet_number': i,
                                    'width': width,
                                    'source_field': 'VHT Operation IE (ID 192)',
                                    'field_byte': f'0x{elt.info[0]:02x}',
                                    'wifi_generation': 'WiFi 5 (802.11ac)'
                                })

                    elif elt.ID == 255 and len(elt.info) > 0:  # Extension Element
                        if elt.info[0] == 36:  # HE Operation (802.11ax)
                            if len(elt.info) > 1:
                                # HE Operation parameters at byte 1
                                width = "Variable (HE)"
                                channel_widths[width] += 1

                                if len(channel_width_details) < 50:
                                    channel_width_details.append({
                                        'packet_number': i,
                                        'width': width,
                                        'source_field': 'HE Operation IE (Ext ID 36)',
                                        'field_byte': f'0x{elt.info[1]:02x}',
                                        'wifi_generation': 'WiFi 6 (802.11ax)'
                                    })

                    elt = elt.payload.getlayer(Dot11Elt)

        self.results['channels'] = {
            'channel_usage': dict(channels),
            'channel_widths': dict(channel_widths)
        }
        self.results['channel_width_details'] = channel_width_details

    def analyze_spatial_streams(self):
        """Analyze spatial streams (NSS) from packets"""
        nss_details = []
        nss_stats = Counter()

        for i, pkt in enumerate(self.packets, 1):
            nss = None
            source_field = None

            if pkt.haslayer(RadioTap):
                rt = pkt[RadioTap]

                # Check MCS field for NSS (802.11n)
                if hasattr(rt, 'MCS_index') and rt.MCS_index is not None:
                    mcs_idx = rt.MCS_index
                    # MCS 0-7 = 1 stream, 8-15 = 2 streams, 16-23 = 3 streams, 24-31 = 4 streams
                    nss = (mcs_idx // 8) + 1
                    source_field = f'RadioTap MCS Index ({mcs_idx})'

                # Check VHT for NSS (802.11ac)
                elif hasattr(rt, 'VHT_NSS') and rt.VHT_NSS is not None:
                    nss = rt.VHT_NSS
                    source_field = 'RadioTap VHT NSS'

                # Check for HE (802.11ax) - might be in notdecoded
                elif hasattr(rt, 'present') and (rt.present & 0x800000):
                    # HE data, but Scapy may not decode NSS fully
                    source_field = 'RadioTap HE (NSS not decoded)'

            if nss and len(nss_details) < 50:
                nss_stats[nss] += 1
                nss_details.append({
                    'packet_number': i,
                    'spatial_streams': nss,
                    'source_field': source_field
                })

        self.results['spatial_streams'] = {
            'nss_distribution': dict(nss_stats),
            'nss_details': nss_details
        }

    def analyze_data_rates(self):
        """Analyze data rates and calculate throughput"""
        rate_details = []
        rate_stats = Counter()
        mcs_details = []

        # MCS to data rate mapping (approximate, 20 MHz, short GI)
        # 802.11n MCS index to Mbps
        ht_rates = {
            0: 7.2, 1: 14.4, 2: 21.7, 3: 28.9, 4: 43.3, 5: 57.8, 6: 65.0, 7: 72.2,
            8: 14.4, 9: 28.9, 10: 43.3, 11: 57.8, 12: 86.7, 13: 115.6, 14: 130.0, 15: 144.4
        }

        for i, pkt in enumerate(self.packets, 1):
            rate = None
            mcs = None
            source_field = None
            wifi_gen = None

            if pkt.haslayer(RadioTap):
                rt = pkt[RadioTap]

                # Check for legacy rate
                if hasattr(rt, 'Rate') and rt.Rate is not None:
                    rate = rt.Rate  # In 500 kbps units
                    rate_mbps = rate * 0.5
                    source_field = 'RadioTap Rate field'
                    wifi_gen = 'Legacy (802.11a/b/g)'
                    rate_stats[f'{rate_mbps} Mbps'] += 1

                    if len(rate_details) < 50:
                        rate_details.append({
                            'packet_number': i,
                            'rate_mbps': rate_mbps,
                            'source_field': source_field,
                            'wifi_generation': wifi_gen
                        })

                # Check MCS (802.11n)
                if hasattr(rt, 'MCS_index') and rt.MCS_index is not None:
                    mcs = rt.MCS_index
                    rate_mbps = ht_rates.get(mcs, 0)
                    source_field = f'RadioTap MCS Index ({mcs})'
                    wifi_gen = 'WiFi 4 (802.11n)'
                    rate_stats[f'MCS{mcs} (~{rate_mbps} Mbps)'] += 1

                    if len(mcs_details) < 50:
                        mcs_details.append({
                            'packet_number': i,
                            'mcs_index': mcs,
                            'estimated_rate_mbps': rate_mbps,
                            'source_field': source_field,
                            'wifi_generation': wifi_gen
                        })

                # Check VHT MCS (802.11ac)
                if hasattr(rt, 'VHT_MCS') and rt.VHT_MCS is not None:
                    mcs = rt.VHT_MCS
                    source_field = f'RadioTap VHT MCS ({mcs})'
                    wifi_gen = 'WiFi 5 (802.11ac)'
                    rate_stats[f'VHT-MCS{mcs}'] += 1

                    if len(mcs_details) < 50:
                        mcs_details.append({
                            'packet_number': i,
                            'mcs_index': mcs,
                            'source_field': source_field,
                            'wifi_generation': wifi_gen
                        })

        # Calculate aggregate throughput estimate
        total_data_bytes = sum(len(pkt) for pkt in self.packets if pkt.haslayer(Dot11) and pkt[Dot11].type == 2)

        self.results['data_rates'] = {
            'rate_distribution': dict(rate_stats),
            'rate_details': rate_details,
            'mcs_details': mcs_details
        }

        self.results['throughput_analysis'] = {
            'total_data_frames': sum(1 for pkt in self.packets if pkt.haslayer(Dot11) and pkt[Dot11].type == 2),
            'total_data_bytes': total_data_bytes,
            'total_data_kb': round(total_data_bytes / 1024, 2)
        }

    def detect_wifi7_features(self):
        """Detect WiFi 7 (802.11be) features"""
        features = {
            'eht_capability': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'eht_operation': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'mlo_support': {
                'detected': False,
                'count': 0,
                'links': [],
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            '320mhz_support': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            '4096qam': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'punctured_preamble': {
                'detected': False,
                'count': 0,
                'packet_numbers': [],
                'detection_details': [],
                'not_detected_reason': ''
            },
            'wifi7_ssids': []
        }

        for i, pkt in enumerate(self.packets, 1):
            if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
                if pkt.haslayer(Dot11Elt):
                    elt = pkt[Dot11Elt]
                    while elt:
                        # EHT Capability IE (ID 255, Extension 106)
                        if elt.ID == 255 and len(elt.info) > 0 and elt.info[0] == 106:
                            features['eht_capability']['detected'] = True
                            features['eht_capability']['count'] += 1
                            if i not in features['eht_capability']['packet_numbers']:
                                features['eht_capability']['packet_numbers'].append(i)

                                if len(features['eht_capability']['detection_details']) < 5:
                                    features['eht_capability']['detection_details'].append({
                                        'packet_number': i,
                                        'layer': 'Dot11Elt (802.11 Information Element)',
                                        'tag_id': '255 (Extension)',
                                        'extension_id': '106 (EHT Capabilities)',
                                        'value': f'Length: {len(elt.info)} bytes',
                                        'description': 'EHT Capability IE advertises WiFi 7 support',
                                        'raw_bytes': f'{elt.info[:min(10, len(elt.info))].hex()}'
                                    })

                            # Check for 320MHz support
                            if len(elt.info) > 2:
                                features['320mhz_support']['detected'] = True
                                features['320mhz_support']['count'] += 1
                                if i not in features['320mhz_support']['packet_numbers']:
                                    features['320mhz_support']['packet_numbers'].append(i)

                            # Check for 4096-QAM support
                            if len(elt.info) > 3:
                                features['4096qam']['detected'] = True
                                features['4096qam']['count'] += 1
                                if i not in features['4096qam']['packet_numbers']:
                                    features['4096qam']['packet_numbers'].append(i)

                        # EHT Operation IE (ID 255, Extension 106)
                        if elt.ID == 255 and len(elt.info) > 0 and elt.info[0] == 107:
                            features['eht_operation']['detected'] = True
                            features['eht_operation']['count'] += 1
                            if i not in features['eht_operation']['packet_numbers']:
                                features['eht_operation']['packet_numbers'].append(i)

                                if len(features['eht_operation']['detection_details']) < 5:
                                    features['eht_operation']['detection_details'].append({
                                        'packet_number': i,
                                        'layer': 'Dot11Elt (802.11 Information Element)',
                                        'tag_id': '255 (Extension)',
                                        'extension_id': '107 (EHT Operation)',
                                        'value': f'Length: {len(elt.info)} bytes',
                                        'description': 'EHT Operation IE defines WiFi 7 operation parameters',
                                        'raw_bytes': f'{elt.info[:min(10, len(elt.info))].hex()}'
                                    })

                        # Multi-Link Operation (ID 255, Extension 107)
                        if elt.ID == 255 and len(elt.info) > 0 and elt.info[0] == 107:
                            features['mlo_support']['detected'] = True
                            features['mlo_support']['count'] += 1
                            if i not in features['mlo_support']['packet_numbers']:
                                features['mlo_support']['packet_numbers'].append(i)

                        elt = elt.payload.getlayer(Dot11Elt)

                # Track WiFi 7 SSIDs
                if pkt.haslayer(Dot11Elt):
                    ssid_elt = pkt.getlayer(Dot11Elt, ID=0)
                    if ssid_elt and features['eht_capability']['detected']:
                        try:
                            ssid = ssid_elt.info.decode('utf-8')
                            if ssid and ssid not in features['wifi7_ssids']:
                                features['wifi7_ssids'].append(ssid)
                        except:
                            pass

        # Set not_detected reasons
        if not features['eht_capability']['detected']:
            features['eht_capability']['not_detected_reason'] = 'No 802.11 Information Element with ID=255 (Extension) and Extension ID=106 (EHT Capability) found. WiFi 7 requires 802.11be capable hardware and AP.'

        if not features['eht_operation']['detected']:
            features['eht_operation']['not_detected_reason'] = 'No EHT Operation IE found. WiFi 7 operation parameters not advertised in this capture.'

        if not features['mlo_support']['detected']:
            features['mlo_support']['not_detected_reason'] = 'No Multi-Link Operation (MLO) support detected. MLO enables simultaneous multi-band connections in WiFi 7.'

        if not features['320mhz_support']['detected']:
            features['320mhz_support']['not_detected_reason'] = 'No 320MHz channel width support detected in EHT capabilities. 320MHz is a key WiFi 7 feature for 6GHz band.'

        if not features['4096qam']['detected']:
            features['4096qam']['not_detected_reason'] = 'No 4096-QAM modulation support detected. 4096-QAM provides higher data rates in WiFi 7.'

        # Limit packet numbers
        for key in features:
            if isinstance(features[key], dict) and 'packet_numbers' in features[key]:
                features[key]['packet_numbers'] = features[key]['packet_numbers'][:20]

        self.results['wifi7_features'] = features

    def detect_roaming(self):
        """Detect roaming events and behavior"""
        roaming = {
            'roaming_detected': False,
            'roam_events': [],
            'client_ap_associations': defaultdict(list),
            'reassociation_count': 0,
            'fast_roaming': False,
            'roam_time_avg_ms': 0,
            'detection_details': []
        }

        client_timeline = defaultdict(list)

        for i, pkt in enumerate(self.packets, 1):
            timestamp = float(pkt.time) if hasattr(pkt, 'time') else i

            # Track associations and reassociations
            if pkt.haslayer(Dot11AssoReq):
                client = pkt.addr2 if hasattr(pkt, 'addr2') else None
                ap = pkt.addr1 if hasattr(pkt, 'addr1') else None
                if client and ap:
                    client_timeline[client].append({
                        'time': timestamp,
                        'ap': ap,
                        'type': 'association',
                        'packet': i
                    })

            if pkt.haslayer(Dot11ReassoReq):
                roaming['reassociation_count'] += 1
                client = pkt.addr2 if hasattr(pkt, 'addr2') else None
                ap = pkt.addr1 if hasattr(pkt, 'addr1') else None
                if client and ap:
                    client_timeline[client].append({
                        'time': timestamp,
                        'ap': ap,
                        'type': 'reassociation',
                        'packet': i
                    })
                    roaming['roaming_detected'] = True

        # Analyze roaming patterns
        for client, events in client_timeline.items():
            if len(events) > 1:
                sorted_events = sorted(events, key=lambda x: x['time'])
                for j in range(1, len(sorted_events)):
                    prev_event = sorted_events[j-1]
                    curr_event = sorted_events[j]

                    if prev_event['ap'] != curr_event['ap']:
                        roam_time_ms = (curr_event['time'] - prev_event['time']) * 1000
                        roaming['roam_events'].append({
                            'client': client,
                            'from_ap': prev_event['ap'],
                            'to_ap': curr_event['ap'],
                            'roam_time_ms': round(roam_time_ms, 2),
                            'packet_number': curr_event['packet']
                        })
                        roaming['roaming_detected'] = True

                        # Fast roaming check (< 50ms)
                        if roam_time_ms < 50:
                            roaming['fast_roaming'] = True

        # Calculate average roam time
        if roaming['roam_events']:
            roam_times = [e['roam_time_ms'] for e in roaming['roam_events']]
            roaming['roam_time_avg_ms'] = round(sum(roam_times) / len(roam_times), 2)

        self.results['roaming_analysis'] = roaming

    def detect_dfs_channels(self):
        """Detect DFS (Dynamic Frequency Selection) channel usage"""
        dfs = {
            'dfs_channels_detected': False,
            'dfs_channels': [],
            'channel_details': [],
            'radar_detection': False,
            'channel_switch_announcements': 0,
            'detection_details': []
        }

        # DFS channels in 5GHz band (varies by region, this is ETSI/FCC common)
        dfs_channel_list = [52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144]

        for i, pkt in enumerate(self.packets, 1):
            # Check for DFS channels in beacon/probe frames
            if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
                if pkt.haslayer(Dot11Elt):
                    ds_elt = pkt.getlayer(Dot11Elt, ID=3)  # DS Parameter Set
                    if ds_elt and len(ds_elt.info) > 0:
                        channel = ds_elt.info[0]
                        if channel in dfs_channel_list:
                            dfs['dfs_channels_detected'] = True
                            if channel not in dfs['dfs_channels']:
                                dfs['dfs_channels'].append(channel)
                                dfs['channel_details'].append({
                                    'channel': channel,
                                    'first_seen_packet': i,
                                    'frequency_mhz': 5000 + (channel * 5)
                                })

                    # Check for Channel Switch Announcement (CSA)
                    elt = pkt[Dot11Elt]
                    while elt:
                        if elt.ID == 37:  # Channel Switch Announcement IE
                            dfs['channel_switch_announcements'] += 1
                            if len(dfs['detection_details']) < 5:
                                dfs['detection_details'].append({
                                    'packet_number': i,
                                    'type': 'Channel Switch Announcement',
                                    'description': 'AP announcing channel change (possibly due to radar detection)'
                                })
                        elt = elt.payload.getlayer(Dot11Elt)

        # Sort DFS channels
        dfs['dfs_channels'].sort()

        self.results['dfs_analysis'] = dfs

    def detect_auto_channel(self):
        """Detect auto channel selection behavior"""
        auto_channel = {
            'channel_changes_detected': False,
            'unique_channels': [],
            'channel_timeline': [],
            'likely_auto_channel': False,
            'channel_utilization': defaultdict(int)
        }

        ap_channels = defaultdict(list)

        for i, pkt in enumerate(self.packets, 1):
            timestamp = float(pkt.time) if hasattr(pkt, 'time') else i

            if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp):
                ap_mac = pkt.addr2 if hasattr(pkt, 'addr2') else None

                if pkt.haslayer(Dot11Elt):
                    ds_elt = pkt.getlayer(Dot11Elt, ID=3)
                    if ds_elt and len(ds_elt.info) > 0 and ap_mac:
                        channel = ds_elt.info[0]
                        ap_channels[ap_mac].append({
                            'time': timestamp,
                            'channel': channel,
                            'packet': i
                        })

                        if channel not in auto_channel['unique_channels']:
                            auto_channel['unique_channels'].append(channel)

        # Analyze channel changes per AP
        for ap_mac, channel_list in ap_channels.items():
            if len(channel_list) > 1:
                prev_channel = channel_list[0]['channel']
                for entry in channel_list[1:]:
                    if entry['channel'] != prev_channel:
                        auto_channel['channel_changes_detected'] = True
                        auto_channel['channel_timeline'].append({
                            'ap': ap_mac,
                            'from_channel': prev_channel,
                            'to_channel': entry['channel'],
                            'packet_number': entry['packet'],
                            'time': entry['time']
                        })
                        prev_channel = entry['channel']

        # If multiple channels seen and channel changes detected, likely auto channel
        if len(auto_channel['unique_channels']) > 1 and auto_channel['channel_changes_detected']:
            auto_channel['likely_auto_channel'] = True

        # Count channel utilization
        for channels in ap_channels.values():
            for entry in channels:
                auto_channel['channel_utilization'][entry['channel']] += 1

        auto_channel['unique_channels'].sort()

        self.results['auto_channel'] = auto_channel

    def detect_airtime_fairness(self):
        """Detect airtime fairness behavior"""
        airtime = {
            'data_frames_per_client': defaultdict(int),
            'airtime_per_client': defaultdict(float),
            'fairness_score': 0,
            'fairness_detected': False,
            'total_airtime_ms': 0,
            'client_stats': []
        }

        client_frame_times = defaultdict(list)

        for i, pkt in enumerate(self.packets, 1):
            timestamp = float(pkt.time) if hasattr(pkt, 'time') else i

            # Analyze data frames
            if pkt.haslayer(Dot11):
                dot11 = pkt[Dot11]
                if dot11.type == 2:  # Data frame
                    client = None
                    # Determine client address
                    if hasattr(pkt, 'addr2'):
                        client = pkt.addr2

                    if client:
                        frame_size = len(pkt)
                        # Estimate airtime (simplified: frame_size / rate)
                        estimated_airtime_us = frame_size * 8  # Very rough estimate

                        airtime['data_frames_per_client'][client] += 1
                        airtime['airtime_per_client'][client] += estimated_airtime_us
                        client_frame_times[client].append(timestamp)

        # Calculate fairness metrics
        if airtime['airtime_per_client']:
            airtimes = list(airtime['airtime_per_client'].values())
            total_airtime = sum(airtimes)
            airtime['total_airtime_ms'] = round(total_airtime / 1000, 2)

            # Fairness score: coefficient of variation (lower is more fair)
            if len(airtimes) > 1:
                mean_airtime = total_airtime / len(airtimes)
                variance = sum((x - mean_airtime) ** 2 for x in airtimes) / len(airtimes)
                std_dev = variance ** 0.5
                cv = (std_dev / mean_airtime) if mean_airtime > 0 else 0
                airtime['fairness_score'] = round(1 - min(cv, 1), 3)  # Convert to 0-1 scale

                # If fairness score > 0.7, likely has airtime fairness
                if airtime['fairness_score'] > 0.7:
                    airtime['fairness_detected'] = True

            # Generate client stats
            for client, client_airtime in airtime['airtime_per_client'].items():
                percentage = (client_airtime / total_airtime) * 100 if total_airtime > 0 else 0
                airtime['client_stats'].append({
                    'client': client,
                    'frames': airtime['data_frames_per_client'][client],
                    'airtime_ms': round(client_airtime / 1000, 2),
                    'percentage': round(percentage, 2)
                })

            # Sort by airtime
            airtime['client_stats'].sort(key=lambda x: x['airtime_ms'], reverse=True)

        self.results['airtime_fairness'] = airtime

    def detect_beamforming(self):
        """Detect beamforming (explicit/implicit) and MU-MIMO grouping"""
        beamforming = {
            'explicit_beamforming': {'detected': False, 'count': 0, 'details': [], 'packet_numbers': []},
            'implicit_beamforming': {'detected': False, 'count': 0, 'details': []},
            'ndpa_frames': {'detected': False, 'count': 0, 'packet_numbers': []},
            'ndp_frames': {'detected': False, 'count': 0, 'packet_numbers': []},
            'beamforming_report': {'detected': False, 'count': 0, 'packet_numbers': []},
            'mu_mimo_grouping': {'detected': False, 'groups': [], 'count': 0},
            'sounding_frequency': {'intervals': [], 'average_ms': 0},
            'not_detected_reason': {}
        }

        ndpa_timestamps = []

        for idx, pkt in enumerate(self.packets, 1):
            try:
                # Detect NDPA (Null Data Packet Announcement) - VHT/HE beamforming
                # NDPA uses Action No Ack frame subtype 0x0e (type=1, subtype=14)
                if pkt.haslayer(Dot11):
                    dot11 = pkt[Dot11]

                    # NDPA detection (Control frame, subtype 0x05 for VHT NDPA)
                    if dot11.type == 1 and dot11.subtype == 0x05:  # VHT NDPA
                        beamforming['ndpa_frames']['detected'] = True
                        beamforming['ndpa_frames']['count'] += 1
                        if len(beamforming['ndpa_frames']['packet_numbers']) < 5:
                            beamforming['ndpa_frames']['packet_numbers'].append(idx)
                        if hasattr(pkt, 'time'):
                            ndpa_timestamps.append(pkt.time)

                    # NDP (Null Data Packet) detection - usually follows NDPA
                    # NDP is a data frame with no payload
                    if dot11.type == 2 and dot11.subtype == 0x04:  # Null function (no data)
                        if len(pkt) < 50:  # NDPs are typically very short
                            beamforming['ndp_frames']['detected'] = True
                            beamforming['ndp_frames']['count'] += 1
                            if len(beamforming['ndp_frames']['packet_numbers']) < 5:
                                beamforming['ndp_frames']['packet_numbers'].append(idx)

                # Check for VHT/HE capabilities in IEs (explicit beamforming support)
                if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp) or pkt.haslayer(Dot11AssoReq):
                    if pkt.haslayer(Dot11Elt):
                        elt = pkt[Dot11Elt]
                        while elt:
                            # VHT Capabilities IE (ID 191)
                            if elt.ID == 191 and len(elt.info) >= 12:
                                vht_cap = elt.info
                                # Check VHT Capabilities Info (first 4 bytes)
                                vht_cap_info = int.from_bytes(vht_cap[0:4], 'little')
                                # Bit 11: SU Beamformer Capable
                                # Bit 12: MU Beamformer Capable
                                if vht_cap_info & (1 << 11) or vht_cap_info & (1 << 12):
                                    beamforming['explicit_beamforming']['detected'] = True
                                    beamforming['explicit_beamforming']['count'] += 1
                                    if len(beamforming['explicit_beamforming']['packet_numbers']) < 5:
                                        beamforming['explicit_beamforming']['packet_numbers'].append(idx)

                                    detail = {
                                        'packet': idx,
                                        'su_beamformer': bool(vht_cap_info & (1 << 11)),
                                        'mu_beamformer': bool(vht_cap_info & (1 << 12)),
                                        'type': 'VHT'
                                    }
                                    if len(beamforming['explicit_beamforming']['details']) < 3:
                                        beamforming['explicit_beamforming']['details'].append(detail)

                            # HE Capabilities IE (ID 255, Extension 35)
                            if elt.ID == 255 and len(elt.info) >= 2 and elt.info[0] == 35:
                                if len(elt.info) >= 22:
                                    he_phy_cap = elt.info[7:16]  # HE PHY Capabilities
                                    # Check for beamforming support in HE PHY caps
                                    if len(he_phy_cap) >= 4:
                                        # Byte 3, bits for beamforming
                                        if he_phy_cap[3] & 0x18:  # SU/MU Beamformer bits
                                            beamforming['explicit_beamforming']['detected'] = True
                                            beamforming['explicit_beamforming']['count'] += 1
                                            if len(beamforming['explicit_beamforming']['packet_numbers']) < 5:
                                                beamforming['explicit_beamforming']['packet_numbers'].append(idx)

                                            detail = {
                                                'packet': idx,
                                                'su_beamformer': bool(he_phy_cap[3] & 0x08),
                                                'mu_beamformer': bool(he_phy_cap[3] & 0x10),
                                                'type': 'HE'
                                            }
                                            if len(beamforming['explicit_beamforming']['details']) < 3:
                                                beamforming['explicit_beamforming']['details'].append(detail)

                            # Check for Beamforming Report (Action frame)
                            if elt.ID == 127:  # Extended capabilities
                                beamforming['beamforming_report']['detected'] = True
                                beamforming['beamforming_report']['count'] += 1
                                if len(beamforming['beamforming_report']['packet_numbers']) < 5:
                                    beamforming['beamforming_report']['packet_numbers'].append(idx)

                            if hasattr(elt, 'payload'):
                                elt = elt.payload.getlayer(Dot11Elt)
                            else:
                                break

                # MU-MIMO grouping detection (Group ID Management frames)
                if pkt.haslayer(Dot11) and pkt[Dot11].type == 0:  # Management
                    if pkt.haslayer(Dot11Elt):
                        elt = pkt[Dot11Elt]
                        while elt:
                            # VHT Group ID IE or HE MU EDCA
                            if elt.ID == 221:  # Vendor specific - might contain group info
                                beamforming['mu_mimo_grouping']['detected'] = True
                                beamforming['mu_mimo_grouping']['count'] += 1

                            if hasattr(elt, 'payload'):
                                elt = elt.payload.getlayer(Dot11Elt)
                            else:
                                break

            except Exception:
                continue

        # Calculate sounding frequency (time between NDPA frames)
        if len(ndpa_timestamps) > 1:
            for i in range(1, len(ndpa_timestamps)):
                interval_ms = (ndpa_timestamps[i] - ndpa_timestamps[i-1]) * 1000
                beamforming['sounding_frequency']['intervals'].append(round(interval_ms, 2))

            if beamforming['sounding_frequency']['intervals']:
                avg_interval = sum(beamforming['sounding_frequency']['intervals']) / len(beamforming['sounding_frequency']['intervals'])
                beamforming['sounding_frequency']['average_ms'] = round(avg_interval, 2)

        # Add "why not detected" explanations
        if not beamforming['explicit_beamforming']['detected']:
            beamforming['not_detected_reason']['explicit_beamforming'] = (
                "No VHT/HE Capability IEs with beamformer bits set. "
                "Requires VHT Capabilities IE (ID 191) or HE Capabilities IE (ID 255, Ext 35) "
                "with SU/MU beamformer capability bits enabled."
            )

        if not beamforming['ndpa_frames']['detected']:
            beamforming['not_detected_reason']['ndpa'] = (
                "No VHT NDPA frames detected. NDPA is a control frame (type=1, subtype=5) "
                "used to announce beamforming sounding. Absence suggests no active beamforming."
            )

        if not beamforming['mu_mimo_grouping']['detected']:
            beamforming['not_detected_reason']['mu_grouping'] = (
                "No MU-MIMO group ID management detected. Requires Group ID Management frames "
                "or vendor-specific IEs containing grouping information."
            )

        self.results['beamforming'] = beamforming

    def detect_spatial_reuse(self):
        """Detect BSS coloring and spatial reuse parameters (802.11ax feature)"""
        spatial_reuse = {
            'bss_color': {'detected': False, 'colors': {}, 'count': 0, 'details': []},
            'spatial_reuse_params': {'detected': False, 'count': 0, 'details': []},
            'obss_pd': {'detected': False, 'count': 0, 'thresholds': []},
            'color_collisions': {'detected': False, 'collisions': []},
            'not_detected_reason': {}
        }

        bss_colors_by_ssid = defaultdict(set)

        for idx, pkt in enumerate(self.packets, 1):
            try:
                if pkt.haslayer(Dot11Beacon) or pkt.haslayer(Dot11ProbeResp) or pkt.haslayer(Dot11AssoReq):
                    ssid = None
                    bssid = None

                    if pkt.haslayer(Dot11):
                        bssid = pkt[Dot11].addr3  # BSSID in beacon/probe

                    if pkt.haslayer(Dot11Elt):
                        elt = pkt[Dot11Elt]
                        while elt:
                            # Get SSID
                            if elt.ID == 0:
                                ssid = elt.info.decode('utf-8', errors='ignore')

                            # HE Operation IE (ID 255, Extension 36) contains BSS Color
                            if elt.ID == 255 and len(elt.info) >= 2 and elt.info[0] == 36:
                                if len(elt.info) >= 7:
                                    # BSS Color is in HE Operation Parameters (bytes 1-3)
                                    he_op_params = int.from_bytes(elt.info[1:4], 'little')
                                    bss_color = he_op_params & 0x3F  # Bits 0-5

                                    if bss_color > 0:  # BSS Color 0 means disabled
                                        spatial_reuse['bss_color']['detected'] = True
                                        spatial_reuse['bss_color']['count'] += 1

                                        if bss_color not in spatial_reuse['bss_color']['colors']:
                                            spatial_reuse['bss_color']['colors'][bss_color] = []

                                        spatial_reuse['bss_color']['colors'][bss_color].append({
                                            'packet': idx,
                                            'ssid': ssid or 'Unknown',
                                            'bssid': bssid
                                        })

                                        if ssid:
                                            bss_colors_by_ssid[ssid].add(bss_color)

                                        if len(spatial_reuse['bss_color']['details']) < 5:
                                            spatial_reuse['bss_color']['details'].append({
                                                'packet': idx,
                                                'bss_color': bss_color,
                                                'ssid': ssid or 'Unknown'
                                            })

                            # Spatial Reuse Parameter Set IE (ID 255, Extension 39)
                            if elt.ID == 255 and len(elt.info) >= 2 and elt.info[0] == 39:
                                spatial_reuse['spatial_reuse_params']['detected'] = True
                                spatial_reuse['spatial_reuse_params']['count'] += 1

                                if len(elt.info) >= 2:
                                    sr_control = elt.info[1]

                                    detail = {
                                        'packet': idx,
                                        'sr_control': sr_control,
                                        'ssid': ssid or 'Unknown'
                                    }

                                    # Parse OBSS PD thresholds if present
                                    if len(elt.info) >= 4:
                                        obss_pd_min = int.from_bytes(elt.info[2:3], 'little', signed=True)
                                        obss_pd_max = int.from_bytes(elt.info[3:4], 'little', signed=True)

                                        spatial_reuse['obss_pd']['detected'] = True
                                        spatial_reuse['obss_pd']['count'] += 1
                                        spatial_reuse['obss_pd']['thresholds'].append({
                                            'packet': idx,
                                            'min_dbm': obss_pd_min,
                                            'max_dbm': obss_pd_max,
                                            'ssid': ssid or 'Unknown'
                                        })

                                        detail['obss_pd_min'] = obss_pd_min
                                        detail['obss_pd_max'] = obss_pd_max

                                    if len(spatial_reuse['spatial_reuse_params']['details']) < 5:
                                        spatial_reuse['spatial_reuse_params']['details'].append(detail)

                            if hasattr(elt, 'payload'):
                                elt = elt.payload.getlayer(Dot11Elt)
                            else:
                                break

            except Exception:
                continue

        # Detect BSS color collisions (same color, different SSIDs)
        for ssid, colors in bss_colors_by_ssid.items():
            for color in colors:
                # Check if this color is used by other SSIDs
                other_ssids = [s for s, c in bss_colors_by_ssid.items() if color in c and s != ssid]
                if other_ssids:
                    spatial_reuse['color_collisions']['detected'] = True
                    collision = {
                        'bss_color': color,
                        'ssids': [ssid] + other_ssids
                    }
                    if collision not in spatial_reuse['color_collisions']['collisions']:
                        spatial_reuse['color_collisions']['collisions'].append(collision)

        # Add "why not detected" explanations
        if not spatial_reuse['bss_color']['detected']:
            spatial_reuse['not_detected_reason']['bss_color'] = (
                "No BSS Color detected. Requires HE Operation IE (ID 255, Ext 36) "
                "with non-zero BSS Color in HE Operation Parameters. "
                "BSS Coloring is a WiFi 6 (802.11ax) feature for spatial reuse."
            )

        if not spatial_reuse['spatial_reuse_params']['detected']:
            spatial_reuse['not_detected_reason']['spatial_reuse'] = (
                "No Spatial Reuse Parameter Set detected. Requires IE ID 255, Extension 39. "
                "This WiFi 6 feature allows overlapping BSS interference mitigation."
            )

        if not spatial_reuse['obss_pd']['detected']:
            spatial_reuse['not_detected_reason']['obss_pd'] = (
                "No OBSS PD (Overlapping BSS Packet Detect) thresholds found. "
                "OBSS PD is part of Spatial Reuse Parameter Set and defines "
                "receive sensitivity adjustments for spatial reuse."
            )

        self.results['spatial_reuse'] = spatial_reuse

    def detect_channel_interference(self):
        """Detect channel utilization, interference, and hidden nodes"""
        interference = {
            'channel_busy_time': {'detected': False, 'utilization_pct': 0, 'details': []},
            'noise_floor': {'detected': False, 'average_dbm': 0, 'measurements': []},
            'co_channel_interference': {'detected': False, 'overlapping_bss': [], 'count': 0},
            'adjacent_channel': {'detected': False, 'adjacent_aps': []},
            'hidden_nodes': {'detected': False, 'rts_cts_ratio': 0, 'rts_count': 0, 'cts_count': 0},
            'retry_rate': {'overall_pct': 0, 'by_client': {}},
            'not_detected_reason': {}
        }

        total_time = 0
        rts_count = 0
        cts_count = 0
        retry_count = 0
        total_data_frames = 0

        channels_by_ap = defaultdict(set)
        client_retries = defaultdict(lambda: {'retries': 0, 'total': 0})

        for idx, pkt in enumerate(self.packets, 1):
            try:
                # Track time span
                if hasattr(pkt, 'time'):
                    if total_time == 0:
                        first_time = pkt.time
                    total_time = pkt.time - first_time

                # Noise floor from RadioTap
                if pkt.haslayer(RadioTap):
                    rt = pkt[RadioTap]
                    if hasattr(rt, 'dBm_AntNoise'):
                        interference['noise_floor']['detected'] = True
                        noise_dbm = rt.dBm_AntNoise
                        interference['noise_floor']['measurements'].append(noise_dbm)

                    # Channel utilization (if available in RadioTap)
                    if hasattr(rt, 'ChannelFlags'):
                        interference['channel_busy_time']['detected'] = True

                if pkt.haslayer(Dot11):
                    dot11 = pkt[Dot11]

                    # RTS frames (Request to Send)
                    if dot11.type == 1 and dot11.subtype == 0x0b:
                        rts_count += 1
                        interference['hidden_nodes']['rts_count'] = rts_count

                    # CTS frames (Clear to Send)
                    if dot11.type == 1 and dot11.subtype == 0x0c:
                        cts_count += 1
                        interference['hidden_nodes']['cts_count'] = cts_count

                    # Retry detection (Frame Control retry bit)
                    if dot11.FCfield & 0x08:  # Retry bit
                        retry_count += 1
                        if dot11.addr2:  # Source address
                            client_retries[dot11.addr2]['retries'] += 1

                    # Count data frames for retry rate
                    if dot11.type == 2:
                        total_data_frames += 1
                        if dot11.addr2:
                            client_retries[dot11.addr2]['total'] += 1

                # Co-channel interference (multiple APs on same channel)
                if pkt.haslayer(Dot11Beacon):
                    channel = None
                    ssid = None
                    bssid = None

                    if pkt.haslayer(Dot11):
                        bssid = pkt[Dot11].addr3

                    if pkt.haslayer(Dot11Elt):
                        elt = pkt[Dot11Elt]
                        while elt:
                            if elt.ID == 0:  # SSID
                                ssid = elt.info.decode('utf-8', errors='ignore')
                            if elt.ID == 3:  # DS Parameter Set (channel)
                                if len(elt.info) >= 1:
                                    channel = elt.info[0]

                            if hasattr(elt, 'payload'):
                                elt = elt.payload.getlayer(Dot11Elt)
                            else:
                                break

                    if channel and bssid:
                        channels_by_ap[channel].add((bssid, ssid or 'Unknown'))

            except Exception:
                continue

        # Analyze co-channel interference
        for channel, aps in channels_by_ap.items():
            if len(aps) > 1:
                interference['co_channel_interference']['detected'] = True
                interference['co_channel_interference']['count'] += len(aps) - 1
                interference['co_channel_interference']['overlapping_bss'].append({
                    'channel': channel,
                    'ap_count': len(aps),
                    'aps': [{'bssid': bssid, 'ssid': ssid} for bssid, ssid in aps]
                })

        # Detect adjacent channel interference (channels within +/-5)
        sorted_channels = sorted(channels_by_ap.keys())
        for i in range(len(sorted_channels)):
            for j in range(i+1, len(sorted_channels)):
                ch_diff = abs(sorted_channels[i] - sorted_channels[j])
                if 1 <= ch_diff <= 5:  # Adjacent or near-adjacent
                    interference['adjacent_channel']['detected'] = True
                    interference['adjacent_channel']['adjacent_aps'].append({
                        'channel_1': sorted_channels[i],
                        'channel_2': sorted_channels[j],
                        'separation': ch_diff
                    })

        # Calculate noise floor average
        if interference['noise_floor']['measurements']:
            # Filter out None values
            valid_measurements = [m for m in interference['noise_floor']['measurements'] if m is not None]
            if valid_measurements:
                avg_noise = sum(valid_measurements) / len(valid_measurements)
                interference['noise_floor']['average_dbm'] = round(avg_noise, 2)

        # Hidden node detection (high RTS/CTS usage)
        if rts_count > 0 and cts_count > 0:
            interference['hidden_nodes']['detected'] = True
            interference['hidden_nodes']['rts_cts_ratio'] = round(rts_count / max(cts_count, 1), 2)

        # Calculate retry rates
        if total_data_frames > 0:
            interference['retry_rate']['overall_pct'] = round((retry_count / total_data_frames) * 100, 2)

        for client, stats in client_retries.items():
            if stats['total'] > 0:
                retry_pct = round((stats['retries'] / stats['total']) * 100, 2)
                interference['retry_rate']['by_client'][client] = {
                    'retry_pct': retry_pct,
                    'retries': stats['retries'],
                    'total_frames': stats['total']
                }

        # Add "why not detected" explanations
        if not interference['hidden_nodes']['detected']:
            interference['not_detected_reason']['hidden_nodes'] = (
                "No RTS/CTS frames detected. RTS/CTS mechanism is used to mitigate hidden node problems. "
                "Absence suggests either no hidden nodes or RTS/CTS disabled."
            )

        if not interference['co_channel_interference']['detected']:
            interference['not_detected_reason']['co_channel'] = (
                "No co-channel interference detected. Only one AP per channel observed. "
                "Clean RF environment or limited capture scope."
            )

        if not interference['noise_floor']['detected']:
            interference['not_detected_reason']['noise_floor'] = (
                "No noise floor measurements in RadioTap headers. "
                "Capture device may not support noise floor reporting."
            )

        self.results['channel_interference'] = interference

    def detect_client_capabilities(self):
        """Wrapper for client capabilities detection module"""
        self.results['client_capabilities'] = detect_client_capabilities(self.packets)

    def detect_security_analysis(self):
        """Wrapper for security analysis detection module"""
        self.results['security_analysis'] = detect_security_analysis(self.packets)

    def detect_rrm_features(self):
        """Wrapper for RRM features detection module"""
        self.results['rrm_features'] = detect_rrm_features(self.packets)

    def detect_qos_analysis(self):
        """Wrapper for QoS analysis detection module"""
        self.results['qos_analysis'] = detect_qos_analysis(self.packets)

    def detect_power_save(self):
        """Wrapper for power save detection module"""
        self.results['power_save'] = detect_power_save(self.packets)

    def detect_bandwidth_analysis(self):
        """Wrapper for bandwidth and throughput analysis module"""
        self.results['bandwidth_analysis'] = detect_bandwidth_analysis(self.packets)

    def detect_issues(self):
        """Detect potential issues in the capture"""
        issues = []

        # Check for malformed packets
        malformed_count = 0
        for pkt in self.packets:
            if pkt.haslayer(Padding) or (hasattr(pkt, 'load') and b'malformed' in str(pkt.load).lower().encode()):
                malformed_count += 1

        if malformed_count > 0:
            issues.append({
                'type': 'Malformed Packets',
                'count': malformed_count,
                'severity': 'Warning',
                'description': 'Some packets appear malformed. This may indicate Scapy limitations or capture issues.'
            })

        # Check for WiFi 6 simulation
        if self.results['wifi6_features']['ofdma_he_mu']['detected']:
            if self.results['wifi6_features']['ofdma_he_mu']['count'] < 10:
                issues.append({
                    'type': 'Limited WiFi 6 Data',
                    'severity': 'Info',
                    'description': 'Few WiFi 6 HE frames detected. May be simulated data or limited capture.'
                })

        # Check for authentication failures
        auth_count = self.results['management_frames'].get('authentication', 0)
        assoc_count = self.results['management_frames'].get('association_request', 0)
        if auth_count > assoc_count * 2:
            issues.append({
                'type': 'Authentication Issues',
                'severity': 'Warning',
                'description': f'High authentication count ({auth_count}) vs associations ({assoc_count}). Possible connection issues.'
            })

        self.results['issues'] = issues

    def print_report(self):
        """Print comprehensive analysis report"""
        print("=" * 80)
        print("WiFi 6 FEATURE DETECTION AND ANALYSIS REPORT")
        print("=" * 80)
        print()

        # File Info
        print("[FILE INFORMATION]")
        print(f"  File: {self.results['file_info']['filename']}")
        print(f"  Size: {self.results['file_info']['size_kb']} KB ({self.results['file_info']['size_bytes']} bytes)")
        print(f"  Total Packets: {self.results['file_info']['total_packets']}")
        print(f"  Analysis Time: {self.results['file_info']['analysis_time']}")
        print()

        # Summary
        print("[PACKET SUMMARY]")
        for ftype, count in self.results['summary']['frame_types'].items():
            print(f"  {ftype} Frames: {count}")
        if self.results['summary']['protocols']:
            print(f"  Protocols Detected: {', '.join(self.results['summary']['protocols'].keys())}")
        print()

        # WiFi 6 Features - MAIN SECTION
        print("=" * 80)
        print("[WiFi 6 (802.11ax) FEATURES DETECTED]")
        print("=" * 80)
        features = self.results['wifi6_features']

        # OFDMA
        print(f"\n✓ OFDMA (HE MU Frames):")
        print(f"  Status: {'DETECTED ✓' if features['ofdma_he_mu']['detected'] else 'NOT DETECTED ✗'}")
        if features['ofdma_he_mu']['detected']:
            print(f"  Count: {features['ofdma_he_mu']['count']} frames")
            if features['ofdma_he_mu']['packet_numbers']:
                print(f"  Packet Numbers: {features['ofdma_he_mu']['packet_numbers']}")

        # SU-MIMO
        print(f"\n✓ SU-MIMO (Single-User MIMO):")
        print(f"  Status: {'DETECTED ✓' if features['su_mimo']['detected'] else 'NOT DETECTED ✗'}")
        if features['su_mimo']['detected']:
            print(f"  Count: {features['su_mimo']['count']} frames")
            if features['su_mimo']['mcs_values']:
                print(f"  MCS Values: {list(set(features['su_mimo']['mcs_values']))}")
            if features['su_mimo']['packet_numbers']:
                print(f"  Packet Numbers: {features['su_mimo']['packet_numbers']}")

        # MU-MIMO
        print(f"\n✓ MU-MIMO (Multi-User MIMO):")
        print(f"  Status: {'DETECTED ✓' if features['mu_mimo']['detected'] else 'NOT DETECTED ✗'}")
        if features['mu_mimo']['detected']:
            print(f"  Count: {features['mu_mimo']['count']} frames")
            if features['mu_mimo']['packet_numbers']:
                print(f"  Packet Numbers: {features['mu_mimo']['packet_numbers']}")

        # HE Capability
        print(f"\n✓ HE Capability IE:")
        print(f"  Status: {'DETECTED ✓' if features['he_capability']['detected'] else 'NOT DETECTED ✗'}")
        if features['he_capability']['detected']:
            print(f"  Count: {features['he_capability']['count']} beacons/probes")
            if features['he_capability']['packet_numbers']:
                print(f"  Packet Numbers (first 20): {features['he_capability']['packet_numbers']}")

        # HE Operation
        print(f"\n✓ HE Operation IE:")
        print(f"  Status: {'DETECTED ✓' if features['he_operation']['detected'] else 'NOT DETECTED ✗'}")
        if features['he_operation']['detected']:
            print(f"  Count: {features['he_operation']['count']} beacons/probes")
            if features['he_operation']['packet_numbers']:
                print(f"  Packet Numbers (first 20): {features['he_operation']['packet_numbers']}")

        # BSS Color
        print(f"\n✓ BSS Color:")
        print(f"  Status: {'DETECTED ✓' if features['bss_color']['detected'] else 'NOT DETECTED ✗'}")
        if features['bss_color']['detected']:
            print(f"  Values: {list(set(features['bss_color']['values']))}")
            if features['bss_color']['packet_numbers']:
                print(f"  Packet Numbers (first 20): {features['bss_color']['packet_numbers']}")

        # WiFi 6 SSIDs
        if features['wifi6_ssids']:
            print(f"\n✓ WiFi 6 Enabled SSIDs:")
            for ssid in features['wifi6_ssids']:
                print(f"  - {ssid}")

        print()

        # WiFi Generation Distribution
        print("=" * 80)
        print("[WiFi GENERATION DISTRIBUTION]")
        print("=" * 80)
        wifi_gen = self.results['wifi_generation']
        print(f"\n  WiFi 6 (802.11ax) Packets: {wifi_gen['wifi6_count']}")
        if wifi_gen['wifi6_packets']:
            print(f"    Packet Numbers (first 20): {wifi_gen['wifi6_packets']}")

        print(f"\n  WiFi 5 (802.11ac) Packets: {wifi_gen['wifi5_count']}")
        if wifi_gen['wifi5_packets']:
            print(f"    Packet Numbers (first 20): {wifi_gen['wifi5_packets']}")

        print(f"\n  WiFi 4 (802.11n) Packets: {wifi_gen['wifi4_count']}")
        if wifi_gen['wifi4_packets']:
            print(f"    Packet Numbers (first 20): {wifi_gen['wifi4_packets']}")

        print(f"\n  Legacy (802.11a/b/g) Packets: {wifi_gen['legacy_count']}")
        if wifi_gen['legacy_packets']:
            print(f"    Packet Numbers (first 20): {wifi_gen['legacy_packets']}")
        print()

        # Channel Width Details
        print("=" * 80)
        print("[CHANNEL WIDTH ANALYSIS]")
        print("=" * 80)
        if self.results['channels']['channel_widths']:
            print("\n  Channel Width Distribution:")
            for width, count in sorted(self.results['channels']['channel_widths'].items()):
                print(f"    {width}: {count} occurrences")

        if self.results['channel_width_details']:
            print("\n  Channel Width Details (Sample - First 20):")
            print(f"  {'Packet#':<10} {'Width':<15} {'Source Field':<35} {'WiFi Gen':<20}")
            print(f"  {'-'*10} {'-'*15} {'-'*35} {'-'*20}")
            for detail in self.results['channel_width_details'][:20]:
                print(f"  {detail['packet_number']:<10} {detail['width']:<15} {detail['source_field']:<35} {detail['wifi_generation']:<20}")
        print()

        # Spatial Streams Analysis
        print("=" * 80)
        print("[SPATIAL STREAMS (NSS) ANALYSIS]")
        print("=" * 80)
        nss_info = self.results['spatial_streams']
        if nss_info['nss_distribution']:
            print("\n  Spatial Stream Distribution:")
            for nss, count in sorted(nss_info['nss_distribution'].items()):
                print(f"    {nss} Stream(s): {count} packets")

        if nss_info['nss_details']:
            print("\n  Spatial Stream Details (Sample - First 20):")
            print(f"  {'Packet#':<10} {'NSS':<10} {'Source Field':<50}")
            print(f"  {'-'*10} {'-'*10} {'-'*50}")
            for detail in nss_info['nss_details']:
                print(f"  {detail['packet_number']:<10} {detail['spatial_streams']:<10} {detail['source_field']:<50}")
        else:
            print("\n  No spatial stream information found in RadioTap headers.")
        print()

        # Data Rates and Throughput
        print("=" * 80)
        print("[DATA RATES & THROUGHPUT ANALYSIS]")
        print("=" * 80)
        rates = self.results['data_rates']
        throughput = self.results['throughput_analysis']

        print(f"\n  Total Data Frames: {throughput['total_data_frames']}")
        print(f"  Total Data Volume: {throughput['total_data_kb']} KB ({throughput['total_data_bytes']} bytes)")

        if rates['rate_distribution']:
            print("\n  Data Rate Distribution:")
            for rate, count in sorted(rates['rate_distribution'].items()):
                print(f"    {rate}: {count} packets")

        if rates['mcs_details']:
            print("\n  MCS Index Details (Sample - First 20):")
            print(f"  {'Packet#':<10} {'MCS':<8} {'Rate (Mbps)':<15} {'Source Field':<35} {'WiFi Gen':<20}")
            print(f"  {'-'*10} {'-'*8} {'-'*15} {'-'*35} {'-'*20}")
            for detail in rates['mcs_details']:
                rate_str = f"{detail.get('estimated_rate_mbps', 'N/A')}"
                print(f"  {detail['packet_number']:<10} {detail['mcs_index']:<8} {rate_str:<15} {detail['source_field']:<35} {detail['wifi_generation']:<20}")

        if rates['rate_details']:
            print("\n  Legacy Rate Details (Sample - First 20):")
            print(f"  {'Packet#':<10} {'Rate (Mbps)':<15} {'Source Field':<30} {'WiFi Gen':<20}")
            print(f"  {'-'*10} {'-'*15} {'-'*30} {'-'*20}")
            for detail in rates['rate_details']:
                print(f"  {detail['packet_number']:<10} {detail['rate_mbps']:<15} {detail['source_field']:<30} {detail['wifi_generation']:<20}")
        print()

        # Management Frames
        print("[MANAGEMENT FRAMES]")
        mgmt = self.results['management_frames']
        print(f"  Beacons: {mgmt['beacons']}")
        print(f"  Probe Requests: {mgmt['probe_requests']}")
        print(f"  Probe Responses: {mgmt['probe_responses']}")
        print(f"  Authentication: {mgmt['authentication']}")
        print(f"  Association Requests: {mgmt['association_request']}")
        print(f"  Association Responses: {mgmt['association_response']}")
        if mgmt['deauthentication'] > 0:
            print(f"  Deauthentication: {mgmt['deauthentication']}")
        if mgmt['disassociation'] > 0:
            print(f"  Disassociation: {mgmt['disassociation']}")
        if mgmt['ssids']:
            print(f"  SSIDs Found: {', '.join(mgmt['ssids'])}")
        if mgmt['channels']:
            print(f"  Channels: {sorted(mgmt['channels'])}")
        print()

        # Security
        print("[SECURITY]")
        sec = self.results['security']
        print(f"  EAPOL Frames: {sec['eapol_frames']}")
        if sec['eapol_frames'] > 0:
            print(f"  4-Way Handshake:")
            print(f"    Message 1: {sec['eapol_messages']['msg1']}")
            print(f"    Message 2: {sec['eapol_messages']['msg2']}")
            print(f"    Message 3: {sec['eapol_messages']['msg3']}")
            print(f"    Message 4: {sec['eapol_messages']['msg4']}")
        if sec['wpa_versions']:
            print(f"  WPA Versions: {', '.join(sec['wpa_versions'])}")
        print()

        # Network Topology
        print("[NETWORK TOPOLOGY]")
        print(f"  Access Points: {len(self.results['access_points'])}")
        for bssid, info in self.results['access_points'].items():
            print(f"    - {bssid}: '{info['ssid']}' ({info['packets']} packets)")
        print(f"  Clients: {self.results['clients']['count']}")
        if self.results['clients']['count'] <= 10:
            for client in self.results['clients']['addresses']:
                print(f"    - {client}")
        else:
            print(f"    (Too many to display - {self.results['clients']['count']} total)")
        print()

        # Channels
        print("[CHANNEL INFORMATION]")
        if self.results['channels']['channel_usage']:
            print("  Channel Usage:")
            for ch, count in sorted(self.results['channels']['channel_usage'].items()):
                print(f"    Channel {ch}: {count} packets")
        print()

        # Issues
        if self.results['issues']:
            print("[ISSUES DETECTED]")
            for issue in self.results['issues']:
                print(f"  [{issue['severity']}] {issue['type']}")
                print(f"    {issue['description']}")
                if 'count' in issue:
                    print(f"    Count: {issue['count']}")
            print()

        # Recommendations
        print("=" * 80)
        print("[WIRESHARK ANALYSIS RECOMMENDATIONS]")
        print("=" * 80)

        if features['ofdma_he_mu']['detected']:
            print("\nTo view OFDMA/HE frames in Wireshark:")
            print("  Filter: wlan_radio.he_mu.flags")
            print("  Filter: wlan_radio.he")

        if features['su_mimo']['detected']:
            print("\nTo view SU-MIMO frames in Wireshark:")
            print("  Filter: radiotap.mcs.index >= 8")
            print("  Filter: wlan_radio.11n.mcs_index >= 8")

        if features['mu_mimo']['detected']:
            print("\nTo view MU-MIMO frames in Wireshark:")
            print("  Filter: radiotap.vht.user.group_id > 0")

        if sec['eapol_frames'] > 0:
            print("\nTo view EAPOL/4-way handshake:")
            print("  Filter: eapol")

        print("\nGeneral WiFi 6 filters:")
        print("  All management frames: wlan.fc.type == 0")
        print("  Beacons only: wlan.fc.type_subtype == 0x08")
        print("  Authentication: wlan.fc.type_subtype == 0x0b")
        print("  Association: wlan.fc.type_subtype == 0x00 || wlan.fc.type_subtype == 0x01")

        print("\n" + "=" * 80)
        print("END OF REPORT")
        print("=" * 80)

    def save_text_report(self, output_file=None):
        """Save detailed text report with timestamp"""
        import os

        if output_file is None:
            # Extract base filename without extension
            base_name = os.path.splitext(os.path.basename(self.pcap_file))[0]
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = f"{base_name}_analysis_{timestamp}.txt"

        with open(output_file, 'w', encoding='utf-8') as f:
            # Redirect print to file
            original_stdout = sys.stdout
            sys.stdout = f

            self.print_categorized_report()

            sys.stdout = original_stdout

        print(f"\n[+] Text report saved to: {output_file}")
        return output_file

    def print_categorized_report(self):
        """Print comprehensive categorized analysis report"""
        print("=" * 100)
        print(" " * 35 + "WiFi 6 PCAP ANALYSIS REPORT")
        print("=" * 100)
        print()

        # ============================================
        # SECTION 1: FILE & CAPTURE INFORMATION
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 35 + "SECTION 1: FILE INFORMATION" + " " * 36 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        info = self.results['file_info']
        print(f"  Filename:           {info['filename']}")
        print(f"  File Size:          {info['size_kb']} KB ({info['size_bytes']:,} bytes)")
        print(f"  Total Packets:      {info['total_packets']:,}")
        print(f"  Analysis Date/Time: {info['analysis_time']}")
        print()

        # Packet Summary
        print("  Packet Type Distribution:")
        for ftype, count in self.results['summary']['frame_types'].items():
            percentage = (count / info['total_packets']) * 100
            print(f"    ├─ {ftype:<20} : {count:>6,} packets ({percentage:>5.2f}%)")
        print()

        if self.results['summary']['protocols']:
            print("  Protocols Detected:")
            for proto, count in self.results['summary']['protocols'].items():
                print(f"    ├─ {proto:<20} : {count:>6,} packets")
        print()

        # ============================================
        # SECTION 2: WiFi GENERATION ANALYSIS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 32 + "SECTION 2: WiFi GENERATION ANALYSIS" + " " * 31 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        wifi_gen = self.results['wifi_generation']
        total_classified = wifi_gen['wifi6_count'] + wifi_gen['wifi5_count'] + wifi_gen['wifi4_count'] + wifi_gen['legacy_count']

        print("  WiFi Technology Distribution:")
        print()

        categories = [
            ('WiFi 6 (802.11ax)', wifi_gen['wifi6_count'], wifi_gen['wifi6_packets'], '✓'),
            ('WiFi 5 (802.11ac)', wifi_gen['wifi5_count'], wifi_gen['wifi5_packets'], '✓'),
            ('WiFi 4 (802.11n)', wifi_gen['wifi4_count'], wifi_gen['wifi4_packets'], '○'),
            ('Legacy (802.11a/b/g)', wifi_gen['legacy_count'], wifi_gen['legacy_packets'], '○')
        ]

        for name, count, packets, symbol in categories:
            if total_classified > 0:
                percentage = (count / total_classified) * 100
                print(f"  {symbol} {name:<25} : {count:>6,} packets ({percentage:>5.2f}%)")
                if packets and count > 0:
                    if len(packets) <= 10:
                        print(f"      Packet Numbers: {packets}")
                    else:
                        print(f"      Packet Numbers (first 20): {packets[:20]}")
                print()

        # ============================================
        # SECTION 3: WiFi 6 FEATURES
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 30 + "SECTION 3: WiFi 6 (802.11ax) FEATURES" + " " * 30 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        features = self.results['wifi6_features']

        # Category 3.1: Management Frame Features
        print("  ┌─ 3.1 WiFi 6 Information Elements (Management Frames)")
        print("  │")

        he_features = [
            ('HE Capability IE', features['he_capability'], 'Advertises WiFi 6 capabilities'),
            ('HE Operation IE', features['he_operation'], 'Defines WiFi 6 operation parameters'),
            ('BSS Color', features['bss_color'], 'Spatial reuse parameter (1-63)')
        ]

        for name, feat, description in he_features:
            status = "DETECTED" if feat['detected'] else "NOT DETECTED"
            symbol = "✓" if feat['detected'] else "✗"
            print(f"  │   {symbol} {name:<25} : {status}")
            print(f"  │      Description: {description}")

            if feat['detected']:
                if 'count' in feat:
                    print(f"  │      Count: {feat['count']:,} occurrences")
                if 'values' in feat and feat['values']:
                    unique_vals = list(set(feat['values']))
                    print(f"  │      Values: {unique_vals}")
                if 'packet_numbers' in feat and feat['packet_numbers']:
                    if len(feat['packet_numbers']) <= 10:
                        print(f"  │      Packet Numbers: {feat['packet_numbers']}")
                    else:
                        print(f"  │      Packet Numbers (sample): {feat['packet_numbers'][:10]} ...")

                # Show detailed detection information
                if 'detection_details' in feat and feat['detection_details']:
                    print(f"  │")
                    print(f"  │      ┌─ Detection Details (Packet-Level Analysis):")
                    for detail in feat['detection_details']:
                        print(f"  │      │")
                        print(f"  │      │  Packet #{detail['packet_number']}:")
                        print(f"  │      │  ├─ Layer: {detail['layer']}")

                        if 'tag_id' in detail:
                            print(f"  │      │  ├─ Tag ID: {detail['tag_id']}")
                        if 'extension_id' in detail:
                            print(f"  │      │  ├─ Extension ID: {detail['extension_id']}")
                        if 'field' in detail:
                            print(f"  │      │  ├─ Field: {detail['field']}")

                        # BSS Color specific details
                        if 'bss_color_value' in detail:
                            print(f"  │      │  ├─ Byte Offset: {detail['byte_offset']}")
                            print(f"  │      │  ├─ Raw Byte: {detail['raw_byte']}")
                            print(f"  │      │  ├─ BSS Color Bits: {detail['bss_color_bits']}")
                            print(f"  │      │  ├─ BSS Color Value: {detail['bss_color_value']}")
                            print(f"  │      │  ├─ Calculation: {detail['calculation']}")
                            print(f"  │      │  ├─ Partial BSS Color: {detail['partial_bss_color']}")

                        # HE Operation specific details
                        if 'parameters' in detail and detail['parameters'] != 'N/A':
                            print(f"  │      │  ├─ Parameters: {detail['parameters']}")

                        # Common fields
                        if 'value' in detail:
                            print(f"  │      │  ├─ Value: {detail['value']}")
                        if 'raw_bytes' in detail:
                            print(f"  │      │  ├─ Raw Bytes (first 10): {detail['raw_bytes']}")
                        if 'byte_position' in detail:
                            print(f"  │      │  ├─ Byte Position: {detail['byte_position']}")

                        print(f"  │      │  └─ Description: {detail['description']}")
                    print(f"  │      └─")
            else:
                # Show NOT DETECTED reasons
                if 'not_detected_reason' in feat and feat['not_detected_reason']:
                    print(f"  │")
                    print(f"  │      ⚠ Why Not Detected:")
                    # Wrap long text
                    reason = feat['not_detected_reason']
                    max_width = 80
                    words = reason.split()
                    line = "  │      "
                    for word in words:
                        if len(line) + len(word) + 1 > max_width:
                            print(line)
                            line = "  │      " + word
                        else:
                            line += " " + word if line != "  │      " else word
                    if line != "  │      ":
                        print(line)
            print("  │")

        # Category 3.2: Advanced WiFi 6 Data Features
        print("  ┌─ 3.2 Advanced WiFi 6 Data Transmission Features")
        print("  │")

        data_features = [
            ('OFDMA (HE MU Frames)', features['ofdma_he_mu'], 'Multi-user resource allocation'),
            ('SU-MIMO', features['su_mimo'], 'Single-user spatial multiplexing'),
            ('MU-MIMO', features['mu_mimo'], 'Multi-user spatial multiplexing')
        ]

        for name, feat, description in data_features:
            status = "DETECTED" if feat['detected'] else "NOT DETECTED"
            symbol = "✓" if feat['detected'] else "✗"
            print(f"  │   {symbol} {name:<25} : {status}")
            print(f"  │      Description: {description}")

            if feat['detected']:
                print(f"  │      Count: {feat['count']:,} frames")
                if 'packet_numbers' in feat and feat['packet_numbers']:
                    print(f"  │      Packet Numbers: {feat['packet_numbers'][:10]}")
                if 'mcs_values' in feat and feat['mcs_values']:
                    print(f"  │      MCS Values: {list(set(feat['mcs_values']))}")

                # Show detailed detection information
                if 'detection_details' in feat and feat['detection_details']:
                    print(f"  │")
                    print(f"  │      ┌─ Detection Details (Packet-Level Analysis):")
                    for detail in feat['detection_details']:
                        print(f"  │      │")
                        print(f"  │      │  Packet #{detail['packet_number']}:")
                        print(f"  │      │  ├─ Layer: {detail['layer']}")
                        print(f"  │      │  ├─ Field: {detail['field']}")
                        print(f"  │      │  ├─ Value: {detail['value']}")
                        if 'byte_position' in detail:
                            print(f"  │      │  ├─ Byte Position: {detail['byte_position']}")
                        print(f"  │      │  └─ Description: {detail['description']}")
                    print(f"  │      └─")
            else:
                # Show NOT DETECTED reasons
                if 'not_detected_reason' in feat and feat['not_detected_reason']:
                    print(f"  │")
                    print(f"  │      ⚠ Why Not Detected:")
                    reason = feat['not_detected_reason']
                    max_width = 80
                    words = reason.split()
                    line = "  │      "
                    for word in words:
                        if len(line) + len(word) + 1 > max_width:
                            print(line)
                            line = "  │      " + word
                        else:
                            line += " " + word if line != "  │      " else word
                    if line != "  │      ":
                        print(line)
            print("  │")

        # WiFi 6 SSIDs
        if features['wifi6_ssids']:
            print("  ┌─ 3.3 WiFi 6 Enabled Networks")
            print("  │")
            for ssid in features['wifi6_ssids']:
                print(f"  │   • SSID: '{ssid}'")
            print()

        # ============================================
        # SECTION 4: CHANNEL & PHY LAYER
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 28 + "SECTION 4: CHANNEL & PHY LAYER ANALYSIS" + " " * 30 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        # 4.1 Channel Usage
        print("  ┌─ 4.1 Channel Utilization")
        print("  │")
        if self.results['channels']['channel_usage']:
            for ch, count in sorted(self.results['channels']['channel_usage'].items()):
                percentage = (count / info['total_packets']) * 100
                band = "2.4 GHz" if ch <= 14 else "5 GHz"
                print(f"  │   Channel {ch:<3} ({band:<8}) : {count:>6,} packets ({percentage:>5.2f}%)")
        print()

        # 4.2 Channel Width Analysis
        print("  ┌─ 4.2 Channel Width Distribution")
        print("  │")
        if self.results['channels']['channel_widths']:
            total_width = sum(self.results['channels']['channel_widths'].values())
            for width, count in sorted(self.results['channels']['channel_widths'].items()):
                percentage = (count / total_width) * 100
                print(f"  │   {width:<20} : {count:>6,} occurrences ({percentage:>5.2f}%)")
        print()

        # 4.3 Channel Width Details Table
        if self.results['channel_width_details']:
            print("  ┌─ 4.3 Channel Width Detection Details (Sample)")
            print("  │")
            print("  │   " + "-" * 90)
            print("  │   | Packet# | Width      | Source Field                        | WiFi Generation      |")
            print("  │   " + "-" * 90)

            for detail in self.results['channel_width_details'][:15]:
                print(f"  │   | {detail['packet_number']:<7} | {detail['width']:<10} | {detail['source_field']:<35} | {detail['wifi_generation']:<20} |")
            print("  │   " + "-" * 90)
        print()

        # ============================================
        # SECTION 5: DATA RATES & THROUGHPUT
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 28 + "SECTION 5: DATA RATES & THROUGHPUT" + " " * 35 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        throughput = self.results['throughput_analysis']
        rates = self.results['data_rates']

        # 5.1 Throughput Summary
        print("  ┌─ 5.1 Throughput Summary")
        print("  │")
        print(f"  │   Total Data Frames:  {throughput['total_data_frames']:,}")
        print(f"  │   Total Data Volume:  {throughput['total_data_kb']:.2f} KB ({throughput['total_data_bytes']:,} bytes)")
        if throughput['total_data_frames'] > 0:
            avg_frame = throughput['total_data_bytes'] / throughput['total_data_frames']
            print(f"  │   Average Frame Size: {avg_frame:.2f} bytes")
        print()

        # 5.2 Data Rate Distribution
        if rates['rate_distribution']:
            print("  ┌─ 5.2 Data Rate Distribution")
            print("  │")
            for rate, count in sorted(rates['rate_distribution'].items(), key=lambda x: x[1], reverse=True)[:10]:
                print(f"  │   {rate:<30} : {count:>6,} packets")
        print()

        # 5.3 MCS Details
        if rates['mcs_details']:
            print("  ┌─ 5.3 MCS Index Details (Sample)")
            print("  │")
            print("  │   " + "-" * 90)
            print("  │   | Packet# | MCS  | Rate (Mbps) | Source Field                    | WiFi Gen           |")
            print("  │   " + "-" * 90)

            for detail in rates['mcs_details'][:10]:
                rate_str = f"{detail.get('estimated_rate_mbps', 'N/A')}"
                print(f"  │   | {detail['packet_number']:<7} | {detail['mcs_index']:<4} | {rate_str:<11} | {detail['source_field']:<31} | {detail['wifi_generation']:<18} |")
            print("  │   " + "-" * 90)
        print()

        # 5.4 PHY Rate Calculation Guide
        print("  ┌─ 5.4 PHY Rate Calculation Reference")
        print("  │")
        print("  │   WiFi 4 (802.11n - HT) Rate Calculation:")
        print("  │   ├─ Formula: Data Rate = (Code Rate) × (MCS Bits per Symbol) × (Spatial Streams) / Guard Interval")
        print("  │   ├─ Guard Interval: 800ns (standard) or 400ns (short GI)")
        print("  │   └─ Example: MCS 7 @ 20MHz, 1 stream, 400ns GI = 72.2 Mbps")
        print("  │")
        print("  │   WiFi 5 (802.11ac - VHT) Rate Calculation:")
        print("  │   ├─ Formula: Data Rate = (VHT-MCS Rate) × (Channel Width Factor) × (Spatial Streams)")
        print("  │   ├─ Channel Width Factors: 20MHz (1×), 40MHz (2.08×), 80MHz (4.33×), 160MHz (8.67×)")
        print("  │   └─ Example: VHT-MCS 8 @ 80MHz, 2 streams, 400ns GI = 780 Mbps")
        print("  │")
        print("  │   WiFi 6 (802.11ax - HE) Rate Calculation:")
        print("  │   ├─ Formula: Data Rate = (HE-MCS Rate) × (Channel Width Factor) × (Spatial Streams)")
        print("  │   ├─ Guard Interval Options: 800ns, 1600ns, 3200ns (longer GI = more robust, lower rate)")
        print("  │   └─ Example: HE-MCS 11 @ 160MHz, 2 streams, 800ns GI = 2401.9 Mbps")
        print("  │")
        print("  │   MCS Index to Rate Mapping (20MHz, 1 stream, 800ns GI):")
        print("  │   ├─ MCS 0:  6.5/7.2 Mbps  (HT/VHT)  │  MCS 6:  58.5/65   Mbps")
        print("  │   ├─ MCS 1: 13.0/14.4 Mbps           │  MCS 7:  65.0/72.2 Mbps")
        print("  │   ├─ MCS 2: 19.5/21.7 Mbps           │  MCS 8:  —/86.7    Mbps (VHT/HE only)")
        print("  │   ├─ MCS 3: 26.0/28.9 Mbps           │  MCS 9:  —/96.3    Mbps (VHT/HE only)")
        print("  │   ├─ MCS 4: 39.0/43.3 Mbps           │  MCS 10: —/— (HE only, 1147.1 @ 160MHz)")
        print("  │   └─ MCS 5: 52.0/57.8 Mbps           │  MCS 11: —/— (HE only, 1200.9 @ 160MHz)")
        print("  │")
        print("  │   Rate Multipliers by Channel Width:")
        print("  │   ├─  20 MHz: Base rate (1.00×)")
        print("  │   ├─  40 MHz: ~2.08× base rate")
        print("  │   ├─  80 MHz: ~4.33× base rate")
        print("  │   └─ 160 MHz: ~8.67× base rate (WiFi 5/6 only)")
        print()

        # ============================================
        # SECTION 6: SPATIAL STREAMS (MIMO)
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 30 + "SECTION 6: SPATIAL STREAMS (MIMO)" + " " * 34 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        nss_info = self.results['spatial_streams']

        if nss_info['nss_distribution']:
            print("  ┌─ 6.1 Spatial Stream Distribution")
            print("  │")
            for nss, count in sorted(nss_info['nss_distribution'].items()):
                print(f"  │   {nss} Stream(s) : {count:>6,} packets")
            print()

            if nss_info['nss_details']:
                print("  ┌─ 6.2 NSS Detection Details (Sample)")
                print("  │")
                print("  │   " + "-" * 70)
                print("  │   | Packet# | NSS | Source Field                                   |")
                print("  │   " + "-" * 70)

                for detail in nss_info['nss_details'][:10]:
                    print(f"  │   | {detail['packet_number']:<7} | {detail['spatial_streams']:<3} | {detail['source_field']:<46} |")
                print("  │   " + "-" * 70)
        else:
            print("  ┌─ 6.1 Spatial Stream Information")
            print("  │")
            print("  │   Status: No NSS information found in RadioTap headers")
            print("  │   Reason: Management frames typically don't contain MCS/NSS data")
            print("  │   Note:   Data frames with RadioTap MCS/VHT headers would show NSS")
        print()

        # ============================================
        # SECTION 7: NETWORK TOPOLOGY
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 35 + "SECTION 7: NETWORK TOPOLOGY" + " " * 36 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        # 7.1 Access Points
        print("  ┌─ 7.1 Access Points Detected")
        print("  │")
        print(f"  │   Total APs: {len(self.results['access_points'])}")
        print("  │")
        for bssid, info in list(self.results['access_points'].items())[:10]:
            ssid_display = f"'{info['ssid']}'" if info['ssid'] else "'<Hidden>'"
            print(f"  │   • {bssid} → {ssid_display}")
            print(f"  │     Packets: {info['packets']:,}")
        print()

        # 7.2 Clients
        print("  ┌─ 7.2 Client Devices")
        print("  │")
        print(f"  │   Total Clients: {self.results['clients']['count']}")
        if self.results['clients']['count'] <= 15:
            for client in self.results['clients']['addresses']:
                print(f"  │   • {client}")
        else:
            print(f"  │   (Showing first 10 of {self.results['clients']['count']})")
            for client in self.results['clients']['addresses'][:10]:
                print(f"  │   • {client}")
        print()

        # ============================================
        # SECTION 8: MANAGEMENT FRAMES
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 33 + "SECTION 8: MANAGEMENT FRAMES" + " " * 37 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        mgmt = self.results['management_frames']

        print("  ┌─ 8.1 Management Frame Distribution")
        print("  │")

        mgmt_frames = [
            ('Beacons', mgmt['beacons'], 'Network advertisement'),
            ('Probe Requests', mgmt['probe_requests'], 'Network discovery'),
            ('Probe Responses', mgmt['probe_responses'], 'Network response'),
            ('Authentication', mgmt['authentication'], 'Connection authentication'),
            ('Association Requests', mgmt['association_request'], 'Client join request'),
            ('Association Responses', mgmt['association_response'], 'AP join response'),
            ('Deauthentication', mgmt['deauthentication'], 'Forced disconnect'),
            ('Disassociation', mgmt['disassociation'], 'Graceful disconnect')
        ]

        for name, count, desc in mgmt_frames:
            if count > 0:
                print(f"  │   {name:<25} : {count:>6,} packets - {desc}")
        print()

        if mgmt['ssids']:
            print("  ┌─ 8.2 SSIDs Detected")
            print("  │")
            for ssid in mgmt['ssids']:
                print(f"  │   • {ssid}")
        print()

        # ============================================
        # SECTION 9: SECURITY ANALYSIS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 35 + "SECTION 9: SECURITY ANALYSIS" + " " * 35 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        sec = self.results['security']

        print("  ┌─ 9.1 Security Protocols")
        print("  │")
        if sec['wpa_versions']:
            print(f"  │   WPA Version(s): {', '.join(sec['wpa_versions'])}")
        print(f"  │   EAPOL Frames:   {sec['eapol_frames']}")
        print()

        if sec['eapol_frames'] > 0:
            print("  ┌─ 9.2 WPA 4-Way Handshake")
            print("  │")
            print(f"  │   Message 1 (ANonce):        {sec['eapol_messages']['msg1']}")
            print(f"  │   Message 2 (SNonce):        {sec['eapol_messages']['msg2']}")
            print(f"  │   Message 3 (GTK):           {sec['eapol_messages']['msg3']}")
            print(f"  │   Message 4 (Confirmation):  {sec['eapol_messages']['msg4']}")

            total_hs = sum(sec['eapol_messages'].values())
            if total_hs >= 4:
                print("  │   Status: Complete handshake(s) detected ✓")
            elif total_hs > 0:
                print("  │   Status: Partial handshake detected")
            else:
                print("  │   Status: No handshake messages identified")
        print()

        # ============================================
        # SECTION 10: WiFi 7 (802.11be) FEATURES
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 30 + "SECTION 10: WiFi 7 (802.11be) FEATURES" + " " * 30 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        wifi7_features = self.results['wifi7_features']

        print("  ┌─ 10.1 WiFi 7 Information Elements")
        print("  │")

        wifi7_ies = [
            ('EHT Capability IE', wifi7_features['eht_capability'], 'Advertises WiFi 7 (802.11be) support'),
            ('EHT Operation IE', wifi7_features['eht_operation'], 'Defines WiFi 7 operation parameters'),
            ('Multi-Link Operation', wifi7_features['mlo_support'], 'Multi-band simultaneous connections'),
        ]

        for name, feat, description in wifi7_ies:
            status = "DETECTED" if feat['detected'] else "NOT DETECTED"
            symbol = "✓" if feat['detected'] else "✗"
            print(f"  │   {symbol} {name:<30} : {status}")
            print(f"  │      Description: {description}")

            if feat['detected']:
                if 'count' in feat:
                    print(f"  │      Count: {feat['count']:,} occurrences")
                if 'packet_numbers' in feat and feat['packet_numbers']:
                    print(f"  │      Packet Numbers: {feat['packet_numbers'][:10]}")

                if 'detection_details' in feat and feat['detection_details']:
                    print(f"  │      Detection Example (Packet #{feat['detection_details'][0]['packet_number']}):")
                    detail = feat['detection_details'][0]
                    if 'tag_id' in detail:
                        print(f"  │        Tag ID: {detail['tag_id']}, Extension ID: {detail['extension_id']}")
                    if 'raw_bytes' in detail:
                        print(f"  │        Raw Bytes: {detail['raw_bytes']}")
            else:
                if 'not_detected_reason' in feat and feat['not_detected_reason']:
                    print(f"  │      Note: {feat['not_detected_reason'][:80]}")
            print("  │")

        print("  ┌─ 10.2 WiFi 7 Advanced Features")
        print("  │")

        wifi7_advanced = [
            ('320 MHz Channel Width', wifi7_features['320mhz_support'], '6 GHz band ultra-wide channels'),
            ('4096-QAM Modulation', wifi7_features['4096qam'], 'Higher order modulation for peak rates'),
        ]

        for name, feat, description in wifi7_advanced:
            status = "DETECTED" if feat['detected'] else "NOT DETECTED"
            symbol = "✓" if feat['detected'] else "✗"
            print(f"  │   {symbol} {name:<30} : {status}")
            print(f"  │      Description: {description}")
            if feat['detected'] and 'count' in feat:
                print(f"  │      Count: {feat['count']:,} occurrences")
            print("  │")

        if wifi7_features['wifi7_ssids']:
            print("  ┌─ 10.3 WiFi 7 Enabled Networks")
            print("  │")
            for ssid in wifi7_features['wifi7_ssids']:
                print(f"  │   • SSID: '{ssid}'")
        print()

        # ============================================
        # SECTION 11: ROAMING ANALYSIS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 35 + "SECTION 11: ROAMING ANALYSIS" + " " * 36 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        roaming = self.results['roaming_analysis']

        print("  ┌─ 11.1 Roaming Detection Summary")
        print("  │")

        if roaming['roaming_detected']:
            print(f"  │   ✓ Roaming Detected: {len(roaming['roam_events'])} events")
            print(f"  │   Reassociation Frames: {roaming['reassociation_count']}")
            if roaming['roam_time_avg_ms'] > 0:
                print(f"  │   Average Roam Time: {roaming['roam_time_avg_ms']} ms")
            if roaming['fast_roaming']:
                print(f"  │   ✓ Fast Roaming Detected (< 50ms transitions)")
        else:
            print("  │   ✗ No Roaming Detected")
            print("  │      No AP transitions observed in this capture")
        print()

        if roaming['roam_events']:
            print("  ┌─ 11.2 Roaming Events")
            print("  │")
            print("  │   " + "-" * 90)
            print("  │   | Client           | From AP          | To AP            | Roam Time (ms) | Packet# |")
            print("  │   " + "-" * 90)

            for event in roaming['roam_events'][:10]:
                client_short = event['client'][:15]
                from_ap_short = event['from_ap'][:15]
                to_ap_short = event['to_ap'][:15]
                print(f"  │   | {client_short:<16} | {from_ap_short:<16} | {to_ap_short:<16} | {event['roam_time_ms']:<14.2f} | {event['packet_number']:<7} |")
            print("  │   " + "-" * 90)
        print()

        # ============================================
        # SECTION 12: DFS CHANNEL ANALYSIS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 32 + "SECTION 12: DFS CHANNEL ANALYSIS" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        dfs = self.results['dfs_analysis']

        print("  ┌─ 12.1 DFS Channel Detection")
        print("  │")

        if dfs['dfs_channels_detected']:
            print(f"  │   ✓ DFS Channels Detected: {dfs['dfs_channels']}")
            print(f"  │   Channel Count: {len(dfs['dfs_channels'])}")
            print(f"  │   Note: DFS channels require radar detection compliance")
        else:
            print("  │   ✗ No DFS Channels Detected")
            print("  │      Operating on non-DFS channels (1-11, 36-48, 149-165)")
        print()

        if dfs['channel_details']:
            print("  ┌─ 12.2 DFS Channel Details")
            print("  │")
            for detail in dfs['channel_details']:
                print(f"  │   Channel {detail['channel']} ({detail['frequency_mhz']} MHz)")
                print(f"  │     First Seen: Packet #{detail['first_seen_packet']}")
            print()

        if dfs['channel_switch_announcements'] > 0:
            print("  ┌─ 12.3 Channel Switch Announcements")
            print("  │")
            print(f"  │   ⚠ CSA Detected: {dfs['channel_switch_announcements']} announcements")
            print(f"  │   Reason: Possible radar detection or channel optimization")

            if dfs['detection_details']:
                print(f"  │   First CSA: Packet #{dfs['detection_details'][0]['packet_number']}")
        print()

        # ============================================
        # SECTION 13: AUTO CHANNEL SELECTION
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 30 + "SECTION 13: AUTO CHANNEL SELECTION" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        auto_ch = self.results['auto_channel']

        print("  ┌─ 13.1 Auto Channel Behavior")
        print("  │")

        if auto_ch['likely_auto_channel']:
            print("  │   ✓ Auto Channel Selection Detected")
            print(f"  │   Unique Channels: {auto_ch['unique_channels']}")
            print(f"  │   Channel Changes: {len(auto_ch['channel_timeline'])} transitions")
        elif auto_ch['channel_changes_detected']:
            print("  │   ⚠ Channel Changes Detected")
            print(f"  │   May indicate manual channel changes or optimization")
        else:
            print("  │   ○ Static Channel Configuration")
            print(f"  │   Channels: {auto_ch['unique_channels']}")
        print()

        if auto_ch['channel_timeline']:
            print("  ┌─ 13.2 Channel Change Timeline")
            print("  │")
            print("  │   " + "-" * 90)
            print("  │   | AP               | From Ch | To Ch | Packet# | Time            |")
            print("  │   " + "-" * 90)

            for change in auto_ch['channel_timeline'][:10]:
                ap_short = change['ap'][:15]
                time_str = f"{change['time']:.2f}"
                print(f"  │   | {ap_short:<16} | {change['from_channel']:<7} | {change['to_channel']:<5} | {change['packet_number']:<7} | {time_str:<15} |")
            print("  │   " + "-" * 90)
        print()

        # ============================================
        # SECTION 14: AIRTIME FAIRNESS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 34 + "SECTION 14: AIRTIME FAIRNESS" + " " * 36 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        airtime = self.results['airtime_fairness']

        print("  ┌─ 14.1 Airtime Fairness Analysis")
        print("  │")

        if airtime['fairness_detected']:
            print(f"  │   ✓ Airtime Fairness Detected")
            print(f"  │   Fairness Score: {airtime['fairness_score']:.3f} (0-1 scale, higher is more fair)")
            print(f"  │   Total Airtime: {airtime['total_airtime_ms']:.2f} ms")
        else:
            print(f"  │   ○ Airtime Distribution")
            if airtime['fairness_score'] > 0:
                print(f"  │   Fairness Score: {airtime['fairness_score']:.3f}")
            print(f"  │   Note: Low fairness score may indicate unequal client access")
        print()

        if airtime['client_stats']:
            print("  ┌─ 14.2 Client Airtime Distribution")
            print("  │")
            print("  │   " + "-" * 90)
            print("  │   | Client           | Data Frames | Airtime (ms) | Percentage |")
            print("  │   " + "-" * 90)

            for stat in airtime['client_stats'][:15]:
                client_short = stat['client'][:15]
                print(f"  │   | {client_short:<16} | {stat['frames']:<11} | {stat['airtime_ms']:<12.2f} | {stat['percentage']:<10.2f}% |")
            print("  │   " + "-" * 90)
        print()

        # ============================================
        # SECTION 15: ISSUES & RECOMMENDATIONS
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 28 + "SECTION 15: ISSUES & RECOMMENDATIONS" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        if self.results['issues']:
            print("  ┌─ 15.1 Issues Detected")
            print("  │")
            for issue in self.results['issues']:
                severity_symbol = "⚠" if issue['severity'] == 'Warning' else "ℹ"
                print(f"  │   {severity_symbol} [{issue['severity']}] {issue['type']}")
                print(f"  │      {issue['description']}")
                if 'count' in issue:
                    print(f"  │      Occurrences: {issue['count']}")
                print("  │")
        else:
            print("  ┌─ 15.1 Issues Detected")
            print("  │")
            print("  │   ✓ No issues detected")
            print()

        # Wireshark Filters
        print("  ┌─ 15.2 Wireshark Analysis Filters")
        print("  │")

        filter_recommendations = []

        if features['he_capability']['detected']:
            filter_recommendations.append(("WiFi 6 Beacons", "wlan.fc.type_subtype == 0x08 && wlan.tag.number == 255"))

        if features['ofdma_he_mu']['detected']:
            filter_recommendations.append(("OFDMA/HE Frames", "wlan_radio.he_mu.flags"))

        if wifi7_features['eht_capability']['detected']:
            filter_recommendations.append(("WiFi 7 Beacons", "wlan.tag.number == 255"))

        if sec['eapol_frames'] > 0:
            filter_recommendations.append(("EAPOL/Handshake", "eapol"))

        if mgmt['authentication'] > 0:
            filter_recommendations.append(("Authentication", "wlan.fc.type_subtype == 0x0b"))

        if roaming['roaming_detected']:
            filter_recommendations.append(("Reassociation", "wlan.fc.type_subtype == 0x02"))

        if dfs['dfs_channels_detected']:
            filter_recommendations.append(("DFS Channels", "wlan_radio.channel >= 52 && wlan_radio.channel <= 144"))

        filter_recommendations.extend([
            ("All Management", "wlan.fc.type == 0"),
            ("All Data Frames", "wlan.fc.type == 2"),
            ("Channel Width 80MHz", "wlan.vht.op.channelwidth == 1"),
            ("Channel Switch", "wlan.tag.number == 37")
        ])

        for desc, filt in filter_recommendations:
            print(f"  │   • {desc:<25} : {filt}")
        print()

        # ============================================
        # SUMMARY & CONCLUSION
        # ============================================
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 38 + "ANALYSIS SUMMARY" + " " * 45 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  Key Findings:")
        print()

        # WiFi 6 Status
        if wifi_gen['wifi6_count'] > 0:
            print(f"  ✓ WiFi 6 (802.11ax) Detected: {wifi_gen['wifi6_count']:,} packets")
            if features['he_capability']['detected']:
                print(f"    • HE Capability IE found in {features['he_capability']['count']:,} frames")
            if features['bss_color']['detected']:
                print(f"    • BSS Color value: {list(set(features['bss_color']['values']))}")
        else:
            print("  ✗ WiFi 6 (802.11ax) Not Detected")

        # WiFi 7 Status
        if wifi7_features['eht_capability']['detected']:
            print(f"  ✓ WiFi 7 (802.11be) Detected: {wifi7_features['eht_capability']['count']:,} frames")
            if wifi7_features['320mhz_support']['detected']:
                print(f"    • 320 MHz channel support detected")
            if wifi7_features['mlo_support']['detected']:
                print(f"    • Multi-Link Operation (MLO) support detected")

        print()

        # Roaming Status
        if roaming['roaming_detected']:
            print(f"  ✓ Roaming: {len(roaming['roam_events'])} events detected")
            if roaming['fast_roaming']:
                print(f"    • Fast roaming enabled (avg {roaming['roam_time_avg_ms']} ms)")

        # DFS Status
        if dfs['dfs_channels_detected']:
            print(f"  ✓ DFS Channels: {dfs['dfs_channels']}")

        # Auto Channel
        if auto_ch['likely_auto_channel']:
            print(f"  ✓ Auto Channel Selection: {len(auto_ch['channel_timeline'])} changes detected")

        # Airtime Fairness
        if airtime['fairness_detected']:
            print(f"  ✓ Airtime Fairness: Score {airtime['fairness_score']:.2f}")

        print()

        # Channel Configuration
        if self.results['channels']['channel_widths']:
            primary_width = max(self.results['channels']['channel_widths'].items(), key=lambda x: x[1])
            print(f"  ✓ Primary Channel Width: {primary_width[0]} ({primary_width[1]:,} occurrences)")

        # Data Summary
        if throughput['total_data_frames'] > 0:
            print(f"  ✓ Data Traffic: {throughput['total_data_kb']:.2f} KB across {throughput['total_data_frames']:,} frames")

        # Network Summary
        print(f"  ✓ Network Devices: {len(self.results['access_points'])} APs, {self.results['clients']['count']} Clients")

        # Section 16: Beamforming Analysis
        self._print_beamforming_section()

        # Section 17: Spatial Reuse
        self._print_spatial_reuse_section()

        # Section 18: Channel Interference
        self._print_channel_interference_section()

        # Section 19: Client Capabilities
        self._print_client_capabilities_section()

        # Section 20: Security Analysis
        self._print_security_section()

        # Section 21: RRM Features
        self._print_rrm_section()

        # Section 22: QoS Analysis
        self._print_qos_section()

        # Section 23: Power Save
        self._print_power_save_section()

        # Section 24: Bandwidth and Throughput Analysis
        self._print_bandwidth_analysis_section()

        print()
        print("=" * 100)
        print(" " * 40 + "END OF REPORT")
        print("=" * 100)

    def _print_beamforming_section(self):
        """Print Section 16: Beamforming Analysis"""
        beamforming = self.results.get('beamforming', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 28 + "SECTION 16: BEAMFORMING ANALYSIS" + " " * 38 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 16.1 Beamforming Detection")
        print("  │")
        if beamforming.get('explicit_beamforming', {}).get('detected'):
            print(f"  │   ✓ Explicit Beamforming: DETECTED")
            print(f"  │      Count: {beamforming['explicit_beamforming']['count']} occurrences")
            if beamforming['explicit_beamforming'].get('details'):
                print(f"  │      Details:")
                for detail in beamforming['explicit_beamforming']['details'][:3]:
                    print(f"  │        Packet #{detail['packet']}: {detail['type']} - "
                          f"SU:{detail['su_beamformer']}, MU:{detail['mu_beamformer']}")
        else:
            print(f"  │   ✗ Explicit Beamforming: NOT DETECTED")

        print("  │")
        if beamforming.get('ndpa_frames', {}).get('detected'):
            print(f"  │   ✓ NDPA Frames: {beamforming['ndpa_frames']['count']} detected")
            if beamforming.get('sounding_frequency', {}).get('average_ms', 0) > 0:
                print(f"  │      Sounding Frequency: {beamforming['sounding_frequency']['average_ms']:.2f} ms average")
        else:
            print(f"  │   ✗ NDPA Frames: NOT DETECTED")

        if beamforming.get('ndp_frames', {}).get('detected'):
            print(f"  │   ✓ NDP Frames: {beamforming['ndp_frames']['count']} detected")

        if beamforming.get('mu_mimo_grouping', {}).get('detected'):
            print(f"  │   ✓ MU-MIMO Grouping: {beamforming['mu_mimo_grouping']['count']} indicators")

        print()

    def _print_spatial_reuse_section(self):
        """Print Section 17: Spatial Reuse (BSS Coloring)"""
        spatial_reuse = self.results.get('spatial_reuse', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 24 + "SECTION 17: SPATIAL REUSE (BSS COLORING)" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 17.1 BSS Color Detection")
        print("  │")
        if spatial_reuse.get('bss_color', {}).get('detected'):
            print(f"  │   ✓ BSS Color Detected: {len(spatial_reuse['bss_color']['colors'])} unique colors")
            for color, details_list in list(spatial_reuse['bss_color']['colors'].items())[:5]:
                ssids = set([d['ssid'] for d in details_list])
                print(f"  │      Color {color}: {', '.join(list(ssids)[:2])}")
        else:
            print(f"  │   ✗ BSS Color: NOT DETECTED")

        print("  │")
        if spatial_reuse.get('spatial_reuse_params', {}).get('detected'):
            print(f"  │   ✓ Spatial Reuse Parameters: {spatial_reuse['spatial_reuse_params']['count']} occurrences")
        else:
            print(f"  │   ✗ Spatial Reuse Parameters: NOT DETECTED")

        print("  │")
        if spatial_reuse.get('obss_pd', {}).get('detected'):
            print(f"  │   ✓ OBSS PD Thresholds: {spatial_reuse['obss_pd']['count']} detected")
            if spatial_reuse['obss_pd'].get('thresholds'):
                thresh = spatial_reuse['obss_pd']['thresholds'][0]
                print(f"  │      Example: Min {thresh['min_dbm']} dBm, Max {thresh['max_dbm']} dBm")

        if spatial_reuse.get('color_collisions', {}).get('detected'):
            print(f"  │   ⚠ BSS Color Collisions: {len(spatial_reuse['color_collisions']['collisions'])} detected")
            for collision in spatial_reuse['color_collisions']['collisions'][:2]:
                print(f"  │      Color {collision['bss_color']}: {', '.join(collision['ssids'][:3])}")

        print()

    def _print_channel_interference_section(self):
        """Print Section 18: Channel Interference Analysis"""
        interference = self.results.get('channel_interference', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 22 + "SECTION 18: CHANNEL INTERFERENCE ANALYSIS" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 18.1 Interference Detection")
        print("  │")
        if interference.get('co_channel_interference', {}).get('detected'):
            print(f"  │   ✓ Co-Channel Interference: {interference['co_channel_interference']['count']} overlapping APs")
            for overlap in interference['co_channel_interference']['overlapping_bss'][:3]:
                print(f"  │      Channel {overlap['channel']}: {overlap['ap_count']} APs")
        else:
            print(f"  │   ✓ Co-Channel: Clean (no overlap detected)")

        print("  │")
        if interference.get('adjacent_channel', {}).get('detected'):
            print(f"  │   ⚠ Adjacent Channel: {len(interference['adjacent_channel']['adjacent_aps'])} near-channel conflicts")

        print("  │")
        if interference.get('hidden_nodes', {}).get('detected'):
            print(f"  │   ⚠ Hidden Nodes Detected")
            print(f"  │      RTS Frames: {interference['hidden_nodes']['rts_count']}")
            print(f"  │      CTS Frames: {interference['hidden_nodes']['cts_count']}")
            print(f"  │      RTS/CTS Ratio: {interference['hidden_nodes']['rts_cts_ratio']}")
        else:
            print(f"  │   ✓ No Hidden Node Issues")

        print("  │")
        if interference.get('noise_floor', {}).get('detected'):
            print(f"  │   Noise Floor: {interference['noise_floor']['average_dbm']} dBm average")

        print("  │")
        if interference.get('retry_rate', {}).get('overall_pct', 0) > 0:
            print(f"  │   Retry Rate: {interference['retry_rate']['overall_pct']:.2f}%")
            if interference['retry_rate']['overall_pct'] > 10:
                print(f"  │      ⚠ High retry rate may indicate interference")

        print()

    def _print_client_capabilities_section(self):
        """Print Section 19: Client Capability Analysis"""
        capabilities = self.results.get('client_capabilities', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 23 + "SECTION 19: CLIENT CAPABILITY ANALYSIS" + " " * 37 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 19.1 Client WiFi Generations")
        print("  │")
        if capabilities.get('wifi_generation_by_client'):
            gen_count = Counter(capabilities['wifi_generation_by_client'].values())
            for gen, count in gen_count.most_common():
                print(f"  │   • {gen}: {count} client(s)")

        print("  │")
        if capabilities.get('vendor_analysis'):
            print("  ┌─ 19.2 Vendor Distribution")
            print("  │")
            sorted_vendors = sorted(capabilities['vendor_analysis'].items(), key=lambda x: x[1], reverse=True)
            for vendor, count in sorted_vendors[:5]:
                print(f"  │   • {vendor}: {count} client(s)")

        print("  │")
        if capabilities.get('capability_mismatches'):
            print(f"  ┌─ 19.3 Capability Mismatches")
            print("  │")
            print(f"  │   ⚠ {len(capabilities['capability_mismatches'])} mismatch(es) detected")
            for mismatch in capabilities['capability_mismatches'][:3]:
                print(f"  │      Client {mismatch['client'][:17]}: {mismatch['client_gen']} connected to {mismatch['ap_gen']} AP")
                print(f"  │         Impact: {mismatch['impact']}")
        else:
            print("  │   ✓ No capability mismatches")

        print()

    def _print_security_section(self):
        """Print Section 20: Security Analysis"""
        security = self.results.get('security_analysis', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 31 + "SECTION 20: SECURITY ANALYSIS" + " " * 37 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 20.1 Security Status")
        print("  │")
        if security.get('deauth_attacks', {}).get('detected'):
            print(f"  │   ⚠ DEAUTH ATTACK DETECTED!")
            print(f"  │      Total Deauth Frames: {security['deauth_attacks']['total_deauth']}")
            for ap_mac, count in list(security['deauth_attacks']['by_ap'].items())[:3]:
                print(f"  │        {ap_mac}: {count} deauth frames")
        else:
            total = security.get('deauth_attacks', {}).get('total_deauth', 0)
            print(f"  │   ✓ No Deauth Attacks (Total deauth: {total})")

        print("  │")
        if security.get('weak_encryption', {}).get('detected'):
            print(f"  │   ⚠ WEAK ENCRYPTION DETECTED!")
            for network in security['weak_encryption']['networks'][:3]:
                print(f"  │      [{network['severity']}] {network['ssid']}: {network['encryption']}")
        else:
            print(f"  │   ✓ Strong Encryption (WPA2/WPA3)")

        print("  │")
        if security.get('open_networks'):
            print(f"  │   ⚠ Open Networks: {len(security['open_networks'])}")
            for network in security['open_networks'][:3]:
                print(f"  │      • {network['ssid']}")

        if security.get('wep_networks'):
            print(f"  │   ⚠ WEP Networks: {len(security['wep_networks'])}")

        print("  │")
        if security.get('evil_twin', {}).get('detected'):
            print(f"  │   ⚠ POTENTIAL EVIL TWIN DETECTED!")
            print(f"  │      Suspicious networks: {len(security['evil_twin']['potential_twins'])}")

        if security.get('rogue_aps', {}).get('detected'):
            print(f"  │   ⚠ Rogue AP Indicators: {len(security['rogue_aps']['suspicious_aps'])}")
            for ap_group in security['rogue_aps']['suspicious_aps'][:2]:
                print(f"  │      SSID '{ap_group['ssid']}': {ap_group['bssid_count']} BSSIDs")

        print()

    def _print_rrm_section(self):
        """Print Section 21: RRM Features (802.11k/v/r)"""
        rrm = self.results.get('rrm_features', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 25 + "SECTION 21: RRM FEATURES (802.11k/v/r)" + " " * 35 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 21.1 802.11k (Radio Resource Management)")
        print("  │")
        if rrm.get('802.11k', {}).get('detected'):
            print(f"  │   ✓ 802.11k Detected")
            print(f"  │      Neighbor Reports: {rrm['802.11k']['neighbor_reports']}")
            print(f"  │      Beacon Reports: {rrm['802.11k']['beacon_reports']}")
        else:
            print(f"  │   ✗ 802.11k: NOT DETECTED")

        print("  │")
        print("  ┌─ 21.2 802.11v (BSS Transition Management)")
        print("  │")
        if rrm.get('802.11v', {}).get('detected'):
            print(f"  │   ✓ 802.11v Detected")
            print(f"  │      BSS Transition Events: {rrm['802.11v']['bss_transition']}")
        else:
            print(f"  │   ✗ 802.11v: NOT DETECTED")

        print("  │")
        print("  ┌─ 21.3 802.11r (Fast Transition)")
        print("  │")
        if rrm.get('802.11r', {}).get('detected'):
            print(f"  │   ✓ 802.11r Detected")
            print(f"  │      FT Authentication: {rrm['802.11r']['ft_auth']}")
            print(f"  │      FT Action Frames: {rrm['802.11r']['ft_action']}")
        else:
            print(f"  │   ✗ 802.11r: NOT DETECTED")

        if rrm.get('client_steering', {}).get('detected'):
            print("  │")
            print(f"  │   ✓ Client Steering: {len(rrm['client_steering']['events'])} events")

        print()

    def _print_qos_section(self):
        """Print Section 22: QoS and WMM Analysis"""
        qos = self.results.get('qos_analysis', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 27 + "SECTION 22: QoS AND WMM ANALYSIS" + " " * 39 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 22.1 WMM/QoS Detection")
        print("  │")
        if qos.get('wmm', {}).get('detected'):
            print(f"  │   ✓ WMM/QoS Detected: {qos['wmm']['count']} QoS frames")
            print("  │      Access Category Distribution:")
            for ac, count in qos['wmm']['ac_distribution'].items():
                if count > 0:
                    pct = (count / qos['wmm']['count']) * 100 if qos['wmm']['count'] > 0 else 0
                    print(f"  │        {ac}: {count} ({pct:.1f}%)")
        else:
            print(f"  │   ✗ WMM/QoS: NOT DETECTED")

        print("  │")
        if qos.get('edca_params', {}).get('detected'):
            print(f"  │   ✓ EDCA Parameters: {len(qos['edca_params']['params'])} detected")
        else:
            print(f"  │   ✗ EDCA Parameters: NOT DETECTED")

        print("  │")
        if qos.get('frame_aggregation', {}).get('a_mpdu', {}).get('detected'):
            print(f"  │   ✓ Frame Aggregation (A-MPDU): {qos['frame_aggregation']['a_mpdu']['count']}")

        print("  │")
        if qos.get('retry_analysis', {}).get('retry_rate_pct', 0) > 0:
            print(f"  │   Retry Rate: {qos['retry_analysis']['retry_rate_pct']:.2f}%")
            if qos['retry_analysis']['retry_rate_pct'] > 10:
                print(f"  │      ⚠ High retry rate detected")

        if qos.get('tspec', {}).get('detected'):
            print(f"  │   ✓ TSPEC: {qos['tspec']['requests']} requests, {qos['tspec']['responses']} responses")

        print()

    def _print_power_save_section(self):
        """Print Section 23: Power Save Features"""
        power_save = self.results.get('power_save', {})

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 29 + "SECTION 23: POWER SAVE FEATURES" + " " * 37 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        print("  ┌─ 23.1 Power Save Detection")
        print("  │")
        if power_save.get('legacy_ps', {}).get('detected'):
            print(f"  │   ✓ Legacy Power Save Detected")
            print(f"  │      PS-Poll Frames: {power_save['legacy_ps']['ps_poll']}")
            print(f"  │      TIM Broadcasts: {power_save['legacy_ps']['tim_broadcast']}")
        else:
            print(f"  │   ✗ Legacy Power Save: NOT DETECTED")

        print("  │")
        if power_save.get('u_apsd', {}).get('detected'):
            print(f"  │   ✓ U-APSD Detected: {power_save['u_apsd']['count']} occurrences")
        else:
            print(f"  │   ✗ U-APSD: NOT DETECTED")

        print("  │")
        if power_save.get('twt', {}).get('detected'):
            print(f"  │   ✓ TWT (Target Wake Time) Detected!")
            print(f"  │      TWT Agreements: {power_save['twt']['agreements']}")
            print(f"  │      Note: WiFi 6 power save feature")
        else:
            print(f"  │   ✗ TWT: NOT DETECTED")

        print("  │")
        if power_save.get('clients_in_ps'):
            print(f"  │   Clients in Power Save: {len(power_save['clients_in_ps'])}")
            print(f"  │      Power Management Frames: {power_save['power_mgmt_frames']}")

        print()

    def _print_bandwidth_analysis_section(self):
        """Print Section 24: Bandwidth and Throughput Analysis"""
        bw = self.results.get('bandwidth_analysis', {})

        if not bw:
            return

        print()
        print("╔" + "=" * 98 + "╗")
        print("║" + " " * 22 + "SECTION 24: BANDWIDTH & THROUGHPUT ANALYSIS" + " " * 33 + "║")
        print("╚" + "=" * 98 + "╝")
        print()

        # Summary
        summary = bw.get('summary', {})
        print("  ┌─ 24.1 Overall Summary")
        print("  │")
        print(f"  │   Total Clients Analyzed: {summary.get('total_clients', 0)}")
        print(f"  │   Total Access Points: {summary.get('total_aps', 0)}")
        print(f"  │   Total Data Volume: {summary.get('total_data_mb', 0):.2f} MB ({summary.get('total_data_kb', 0):.2f} KB)")
        print(f"  │   Total Packets: {summary.get('total_packets', 0):,}")

        if summary.get('total_clients', 0) > 0:
            print(f"  │   Avg Bytes per Client: {summary.get('avg_bytes_per_client', 0):,.0f} bytes")
        print()

        # Top Clients by Bandwidth
        print("  ┌─ 24.2 Top Clients by Bandwidth Usage")
        print("  │")
        top_clients = summary.get('top_clients_by_bandwidth', [])
        if top_clients:
            for idx, client in enumerate(top_clients, 1):
                mac = client.get('mac', 'Unknown')
                mb = client.get('total_mb', 0)
                mbps = client.get('avg_throughput_mbps', 0)
                print(f"  │   {idx}. {mac}")
                print(f"  │      Data Volume: {mb:.2f} MB")
                print(f"  │      Avg Throughput: {mbps:.2f} Mbps")
        else:
            print("  │   No client data available")
        print()

        # Top Applications by Bandwidth
        print("  ┌─ 24.3 Top Applications by Bandwidth")
        print("  │")
        top_apps = summary.get('top_applications_by_bandwidth', [])
        if top_apps:
            for idx, app in enumerate(top_apps, 1):
                app_name = app.get('application', 'Unknown')
                mb = app.get('total_mb', 0)
                clients = app.get('unique_clients', 0)
                print(f"  │   {idx}. {app_name}")
                print(f"  │      Data Volume: {mb:.2f} MB")
                print(f"  │      Unique Clients: {clients}")
        else:
            print("  │   No application data available")
        print()

        # Per-Client Detailed Analysis
        per_client = bw.get('per_client_analysis', {})
        if per_client:
            print("  ┌─ 24.4 Detailed Per-Client Analysis")
            print("  │")
            # Show top 5 clients by data volume
            sorted_clients = sorted(per_client.items(), key=lambda x: x[1]['total_bytes'], reverse=True)[:5]

            for idx, (mac, stats) in enumerate(sorted_clients, 1):
                print(f"  │   Client {idx}: {mac}")
                print(f"  │   ├─ Total Data: {stats['total_mb']:.2f} MB ({stats['total_kb']:.2f} KB)")
                print(f"  │   ├─ TX/RX: {stats['tx_kb']:.2f} KB ↑ / {stats['rx_kb']:.2f} KB ↓")
                print(f"  │   ├─ Packets: {stats['total_packets']:,} (Data: {stats['data_frames']}, Mgmt: {stats['management_frames']})")
                print(f"  │   ├─ Avg Throughput: {stats['avg_throughput_mbps']:.2f} Mbps")
                print(f"  │   ├─ Avg Packet Size: {stats['avg_packet_size']:.0f} bytes")

                # Data rates
                rates = stats.get('data_rates', {})
                if rates and rates.get('avg', 0) > 0:
                    print(f"  │   ├─ Data Rates: Min={rates['min']:.1f}, Max={rates['max']:.1f}, Avg={rates['avg']:.1f} Mbps")

                # MCS values
                mcs = stats.get('mcs_values', [])
                if mcs:
                    print(f"  │   ├─ MCS Values: {mcs}")

                # Associated APs
                aps = stats.get('associated_aps', [])
                if aps:
                    print(f"  │   ├─ Associated APs: {len(aps)} AP(s)")
                    for ap in aps[:2]:  # Show first 2
                        print(f"  │   │  └─ {ap}")

                # Top applications for this client
                apps = stats.get('applications', {})
                if apps:
                    sorted_apps = sorted(apps.items(), key=lambda x: x[1], reverse=True)[:3]
                    print(f"  │   └─ Top Applications:")
                    for app_name, app_bytes in sorted_apps:
                        app_mb = app_bytes / (1024 * 1024)
                        print(f"  │       • {app_name}: {app_mb:.2f} MB")

                if idx < len(sorted_clients):
                    print("  │")
            print()

        # Per-AP Analysis
        per_ap = bw.get('per_ap_analysis', {})
        if per_ap:
            print("  ┌─ 24.5 Detailed Per-AP Analysis")
            print("  │")
            sorted_aps = sorted(per_ap.items(), key=lambda x: x[1]['total_bytes'], reverse=True)

            for idx, (mac, stats) in enumerate(sorted_aps, 1):
                ssid = stats.get('ssid', 'Hidden/Unknown')
                channel = stats.get('channel', 'Unknown')
                print(f"  │   AP {idx}: {mac}")
                print(f"  │   ├─ SSID: {ssid}")
                print(f"  │   ├─ Channel: {channel}")
                print(f"  │   ├─ Total Data: {stats['total_mb']:.2f} MB ({stats['total_kb']:.2f} KB)")
                print(f"  │   ├─ TX/RX: {stats['tx_kb']:.2f} KB ↑ / {stats['rx_kb']:.2f} KB ↓")
                print(f"  │   ├─ Packets: {stats['total_packets']:,}")
                print(f"  │   ├─ Beacons: {stats['beacon_count']}")
                print(f"  │   ├─ Connected Clients: {stats['connected_clients_count']}")
                print(f"  │   ├─ Avg Throughput: {stats['avg_throughput_mbps']:.2f} Mbps")

                # Data rates
                rates = stats.get('data_rates', {})
                if rates and rates.get('avg', 0) > 0:
                    print(f"  │   ├─ Data Rates: Min={rates['min']:.1f}, Max={rates['max']:.1f}, Avg={rates['avg']:.1f} Mbps")

                # Top applications for this AP
                apps = stats.get('applications', {})
                if apps:
                    sorted_apps = sorted(apps.items(), key=lambda x: x[1], reverse=True)[:3]
                    print(f"  │   └─ Top Applications:")
                    for app_name, app_bytes in sorted_apps:
                        app_mb = app_bytes / (1024 * 1024)
                        print(f"  │       • {app_name}: {app_mb:.2f} MB")

                if idx < len(sorted_aps):
                    print("  │")
            print()

        # Application Analysis
        per_app = bw.get('per_application_analysis', {})
        if per_app:
            print("  ┌─ 24.6 Application Traffic Breakdown")
            print("  │")
            sorted_apps = sorted(per_app.items(), key=lambda x: x[1]['total_bytes'], reverse=True)

            for idx, (app_name, stats) in enumerate(sorted_apps, 1):
                print(f"  │   {idx}. {app_name}")
                print(f"  │      Data Volume: {stats['total_mb']:.2f} MB ({stats['total_kb']:.2f} KB)")
                print(f"  │      Packets: {stats['packet_count']:,}")
                print(f"  │      Unique Clients: {stats['unique_clients']}")
                if idx < len(sorted_apps):
                    print("  │")
            print()

        # Traffic Direction
        traffic_dir = bw.get('traffic_direction', {})
        if traffic_dir:
            print("  ┌─ 24.7 Traffic Direction Analysis")
            print("  │")
            uplink = traffic_dir.get('uplink', {})
            downlink = traffic_dir.get('downlink', {})
            p2p = traffic_dir.get('peer_to_peer', {})

            print(f"  │   Uplink (Client → AP):")
            print(f"  │      Data: {uplink.get('mb', 0):.2f} MB ({uplink.get('percentage_bytes', 0):.1f}%)")
            print(f"  │      Packets: {uplink.get('packets', 0):,} ({uplink.get('percentage_packets', 0):.1f}%)")
            print("  │")
            print(f"  │   Downlink (AP → Client):")
            print(f"  │      Data: {downlink.get('mb', 0):.2f} MB ({downlink.get('percentage_bytes', 0):.1f}%)")
            print(f"  │      Packets: {downlink.get('packets', 0):,} ({downlink.get('percentage_packets', 0):.1f}%)")

            if p2p.get('packets', 0) > 0:
                print("  │")
                print(f"  │   Peer-to-Peer/WDS:")
                print(f"  │      Data: {p2p.get('mb', 0):.2f} MB ({p2p.get('percentage_bytes', 0):.1f}%)")
                print(f"  │      Packets: {p2p.get('packets', 0):,} ({p2p.get('percentage_packets', 0):.1f}%)")
            print()

        # Protocol Distribution
        protocols = bw.get('protocol_distribution', {})
        if protocols:
            print("  ┌─ 24.8 Protocol Distribution")
            print("  │")
            sorted_protocols = sorted(protocols.items(), key=lambda x: x[1]['bytes'], reverse=True)

            for protocol, stats in sorted_protocols:
                pct = (stats['bytes'] / summary.get('total_data_bytes', 1)) * 100 if summary.get('total_data_bytes', 0) > 0 else 0
                print(f"  │   {protocol}:")
                print(f"  │      Data: {stats['mb']:.2f} MB ({pct:.1f}%)")
                print(f"  │      Packets: {stats['packets']:,}")
            print()

        # Multicast Analysis
        mcast = bw.get('multicast_analysis', {})
        if mcast and mcast.get('summary', {}).get('total_multicast_packets', 0) > 0:
            print("  ┌─ 24.9 Multicast/Broadcast Traffic Analysis")
            print("  │")
            mcast_summary = mcast.get('summary', {})
            print(f"  │   Total Multicast Groups: {mcast_summary.get('total_multicast_groups', 0)}")
            print(f"  │   Video Streaming Groups: {mcast_summary.get('total_streaming_groups', 0)}")
            print(f"  │   Clients Receiving Multicast: {mcast_summary.get('clients_receiving_multicast', 0)}")
            print(f"  │   Total Multicast Data: {mcast_summary.get('total_multicast_mb', 0):.2f} MB")
            print(f"  │   Total Broadcast Data: {mcast_summary.get('total_broadcast_kb', 0):.2f} KB")
            print(f"  │   Avg Multicast Data Rate: {mcast_summary.get('avg_multicast_data_rate', 0):.1f} Mbps")
            print("  │")

            # IGMP Activity
            igmp_joins = mcast_summary.get('igmp_joins', 0)
            igmp_leaves = mcast_summary.get('igmp_leaves', 0)
            igmp_queries = mcast_summary.get('igmp_queries', 0)
            if igmp_joins > 0 or igmp_leaves > 0 or igmp_queries > 0:
                print(f"  │   IGMP Activity:")
                print(f"  │      Group Joins: {igmp_joins}")
                print(f"  │      Group Leaves: {igmp_leaves}")
                print(f"  │      Membership Queries: {igmp_queries}")
                print("  │")

            # Multicast Groups
            mcast_groups = mcast.get('multicast_groups', {})
            if mcast_groups:
                print("  │   Multicast Groups Detected:")
                sorted_groups = sorted(mcast_groups.items(), key=lambda x: x[1]['total_bytes'], reverse=True)[:10]
                for group_ip, group_data in sorted_groups:
                    is_streaming = "📹 Video Streaming" if group_data.get('is_video_streaming', False) else ""
                    print(f"  │      • {group_ip} {is_streaming}")
                    print(f"  │        Data: {group_data['total_mb']:.2f} MB, Throughput: {group_data['avg_throughput_mbps']:.2f} Mbps")
                    print(f"  │        Clients: {group_data['client_count']}, Packets: {group_data['total_packets']:,}")
                    if group_data.get('source_ips'):
                        print(f"  │        Source IPs: {', '.join(group_data['source_ips'][:3])}")
                print("  │")

            # Per-Client Multicast Statistics
            per_client_mcast = mcast.get('per_client_multicast', {})
            if per_client_mcast:
                print("  │   Top Clients by Multicast Traffic:")
                sorted_clients = sorted(per_client_mcast.items(), key=lambda x: x[1]['total_multicast_bytes'], reverse=True)[:5]
                for client_mac, client_data in sorted_clients:
                    print(f"  │      • {client_mac}")
                    print(f"  │        Total Multicast: {client_data['total_multicast_mb']:.2f} MB")
                    print(f"  │        Streaming Video: {client_data['streaming_mb']:.2f} MB ({client_data['streaming_packets']} packets)")
                    print(f"  │        Throughput: {client_data['avg_multicast_throughput_mbps']:.2f} Mbps (Streaming: {client_data['avg_streaming_throughput_mbps']:.2f} Mbps)")
                    print(f"  │        Groups Joined: {client_data['groups_joined_count']}")
                    if client_data['groups_joined']:
                        print(f"  │          └─ {', '.join(list(client_data['groups_joined'])[:3])}")
                print("  │")

            # IGMP Join/Leave Events
            igmp_activity = mcast.get('igmp_activity', {})
            if igmp_activity.get('joins') or igmp_activity.get('leaves'):
                print("  │   IGMP Group Management Events:")
                joins = igmp_activity.get('joins', [])[:5]
                for join in joins:
                    print(f"  │      ✓ Join: {join['client']} → {join['group']} (pkt #{join['packet_number']})")
                leaves = igmp_activity.get('leaves', [])[:5]
                for leave in leaves:
                    print(f"  │      ✗ Leave: {leave['client']} ← {leave['group']} (pkt #{leave['packet_number']})")
            print()

        print()

    def save_json_report(self, output_file=None):
        """Save results as JSON with timestamp"""
        import os

        if output_file is None:
            base_name = os.path.splitext(os.path.basename(self.pcap_file))[0]
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_file = f"{base_name}_analysis_{timestamp}.json"

        with open(output_file, 'w') as f:
            json.dump(self.results, f, indent=2)

        print(f"[+] JSON report saved to: {output_file}")
        return output_file

    def run_analysis(self):
        """Run complete analysis"""
        if not self.load_pcap():
            return False

        print("[*] Analyzing file information...")
        self.analyze_file_info()

        print("[*] Analyzing packet summary...")
        self.analyze_summary()

        print("[*] Detecting WiFi generation (WiFi 4/5/6/7)...")
        self.detect_wifi_generation()

        print("[*] Detecting WiFi 6 features...")
        self.detect_wifi6_features()

        print("[*] Detecting WiFi 7 features...")
        self.detect_wifi7_features()

        print("[*] Analyzing management frames...")
        self.analyze_management_frames()

        print("[*] Analyzing security...")
        self.analyze_security()

        print("[*] Identifying clients and APs...")
        self.analyze_clients_aps()

        print("[*] Analyzing channels and channel widths...")
        self.analyze_channels()

        print("[*] Analyzing spatial streams...")
        self.analyze_spatial_streams()

        print("[*] Analyzing data rates and throughput...")
        self.analyze_data_rates()

        print("[*] Detecting roaming behavior...")
        self.detect_roaming()

        print("[*] Detecting DFS channels...")
        self.detect_dfs_channels()

        print("[*] Detecting auto channel selection...")
        self.detect_auto_channel()

        print("[*] Analyzing airtime fairness...")
        self.detect_airtime_fairness()

        print("[*] Detecting beamforming...")
        self.detect_beamforming()

        print("[*] Detecting spatial reuse (BSS coloring)...")
        self.detect_spatial_reuse()

        print("[*] Analyzing channel interference...")
        self.detect_channel_interference()

        print("[*] Analyzing client capabilities...")
        self.detect_client_capabilities()

        print("[*] Analyzing security...")
        self.detect_security_analysis()

        print("[*] Detecting RRM features (802.11k/v/r)...")
        self.detect_rrm_features()

        print("[*] Analyzing QoS and WMM...")
        self.detect_qos_analysis()

        print("[*] Analyzing power save features...")
        self.detect_power_save()

        print("[*] Analyzing bandwidth and throughput per client/AP/application...")
        self.detect_bandwidth_analysis()

        print("[*] Detecting issues...")
        self.detect_issues()

        print("[+] Analysis complete!\n")

        return True


def main():
    if len(sys.argv) < 2:
        print("Usage: python wifilyzer.py <pcap_file>")
        print("Example: python wifilyzer.py wifi61.pcap")
        sys.exit(1)

    pcap_file = sys.argv[1]

    analyzer = WiFi6FeatureDetector(pcap_file)

    if analyzer.run_analysis():
        # Save reports with timestamp
        text_report = analyzer.save_text_report()
        json_report = analyzer.save_json_report()

        print(f"\n{'='*80}")
        print(f"✓ Analysis Complete!")
        print(f"{'='*80}")
        print(f"\nGenerated Reports:")
        print(f"  1. Text Report: {text_report}")
        print(f"  2. JSON Report: {json_report}")
        print(f"\nTo view the report:")
        print(f"  Windows: type {text_report}")
        print(f"  Linux:   cat {text_report}")
        print(f"{'='*80}\n")
    else:
        print("[!] Analysis failed")
        sys.exit(1)


if __name__ == "__main__":
    main()
