"""
PTP (IEEE 1588-2019) Precision Time Protocol dissector
"""

import struct
from typing import Dict, Optional, Any

try:
    from scapy.all import Packet, UDP, Raw
except ImportError:
    print("Error: scapy library required. Install with: pip install scapy")
    import sys
    sys.exit(1)

from .base import ProtocolDissector, DissectionResult, ProtocolType


class PTPDissector(ProtocolDissector):
    """IEEE 1588-2019 Precision Time Protocol dissector"""
    
    PTP_MESSAGE_TYPES = {
        0x0: "Sync",
        0x1: "Delay_Req", 
        0x2: "Pdelay_Req",
        0x3: "Pdelay_Resp",
        0x8: "Follow_Up",
        0x9: "Delay_Resp",
        0xA: "Pdelay_Resp_Follow_Up",
        0xB: "Announce",
        0xC: "Signaling",
        0xD: "Management"
    }
    
    def __init__(self):
        self.ptp_ports = {319, 320}  # PTP event and general ports
    
    def can_dissect(self, packet: Packet) -> bool:
        """Check if packet is PTP"""
        if not packet.haslayer(UDP):
            return False
            
        udp_layer = packet[UDP]
        if udp_layer.dport not in self.ptp_ports and udp_layer.sport not in self.ptp_ports:
            return False
            
        if not packet.haslayer(Raw):
            return False
            
        raw_data = bytes(packet[Raw])
        return len(raw_data) >= 34  # Minimum PTP header size
    
    def get_protocol_type(self) -> ProtocolType:
        return ProtocolType.PTP
    
    def dissect(self, packet: Packet) -> Optional[DissectionResult]:
        """Dissect PTP packet"""
        if not self.can_dissect(packet):
            return None
            
        raw_data = bytes(packet[Raw])
            
        try:
            header = self._parse_ptp_header(raw_data[:34])
            
            result = DissectionResult(
                protocol=ProtocolType.PTP,
                fields=header
            )
            
            # Parse message-specific fields
            msg_type = header.get('message_type', 0)
            if len(raw_data) > 34:
                msg_fields = self._parse_message_fields(msg_type, raw_data[34:])
                if msg_fields:
                    result.fields.update(msg_fields)
            
            return result
            
        except Exception as e:
            return DissectionResult(
                protocol=ProtocolType.PTP,
                fields={},
                errors=[f"PTP parsing error: {str(e)}"]
            )
    
    def _parse_ptp_header(self, header_data: bytes) -> Dict[str, Any]:
        """Parse PTP common header"""
        if len(header_data) < 34:
            raise ValueError("PTP header too short")
        
        # Parse first 4 bytes
        first_word = struct.unpack('>I', header_data[:4])[0]
        
        message_type = first_word & 0xF
        transport_specific = (first_word >> 4) & 0xF
        ptp_version = (first_word >> 8) & 0xFF
        domain_number = (first_word >> 24) & 0xFF
        
        # Parse remaining header fields
        message_length = struct.unpack('>H', header_data[4:6])[0]
        flags = struct.unpack('>H', header_data[6:8])[0]
        correction = struct.unpack('>Q', header_data[8:16])[0]
        source_port_id = header_data[20:28]
        sequence_id = struct.unpack('>H', header_data[30:32])[0]
        control = header_data[32]
        log_mean_message_interval = struct.unpack('b', header_data[33:34])[0]
        
        return {
            'message_type': message_type,
            'message_type_name': self.PTP_MESSAGE_TYPES.get(message_type, f"Unknown (0x{message_type:x})"),
            'transport_specific': transport_specific,
            'ptp_version': ptp_version,
            'domain_number': domain_number,
            'message_length': message_length,
            'flags': flags,
            'correction_field': correction,
            'source_port_identity': source_port_id.hex(),
            'sequence_id': sequence_id,
            'control_field': control,
            'log_mean_message_interval': log_mean_message_interval
        }
    
    def _parse_message_fields(self, msg_type: int, payload: bytes) -> Optional[Dict[str, Any]]:
        """Parse message-specific fields"""
        if msg_type in [0x0, 0x1, 0x2, 0x3]:  # Sync, Delay_Req, Pdelay_Req, Pdelay_Resp
            if len(payload) >= 10:
                timestamp = struct.unpack('>HI', payload[:6])  # seconds_msb, seconds_lsb, nanoseconds
                nanoseconds = struct.unpack('>I', payload[6:10])[0]
                return {
                    'origin_timestamp_sec': (timestamp[0] << 32) | timestamp[1], 
                    'origin_timestamp_nsec': nanoseconds
                }
        elif msg_type == 0xB:  # Announce
            if len(payload) >= 20:
                return {
                    'current_utc_offset': struct.unpack('>h', payload[10:12])[0],
                    'grandmaster_priority1': payload[13],
                    'grandmaster_clock_quality': payload[14:18].hex(),
                    'grandmaster_priority2': payload[18],
                    'grandmaster_identity': payload[19:27].hex()
                }
        
        return None