"""
TSPI/CTS (Time Space Position Information/Common Test System) data decoders
Supports ACTTS, GPS NMEA-RTCM, and EAG ACMI formats
"""

import struct
from typing import Dict, Any, Optional, List
from .base import DataTypeDecoder, DecodedPayload


class TSPICTSDecoder(DataTypeDecoder):
    """Base decoder for TSPI/CTS data types (0x70-0x77)"""
    
    def __init__(self):
        super().__init__()
        self.data_type_base = 0x70
        self.data_type_name = "TSPI/CTS"
        self.supported_formats = list(range(0x70, 0x78))
    
    def can_decode(self, data_type: int) -> bool:
        return 0x70 <= data_type <= 0x77
    
    def get_data_type_name(self, data_type: int) -> str:
        format_names = {
            0x70: "GPS NMEA-RTCM",
            0x71: "EAG ACMI", 
            0x72: "ACTTS",
            0x73: "TSPI/CTS Format 3",
            0x74: "TSPI/CTS Format 4",
            0x75: "TSPI/CTS Format 5",
            0x76: "TSPI/CTS Format 6",
            0x77: "TSPI/CTS Format 7"
        }
        return format_names.get(data_type, f"TSPI/CTS Format {data_type & 0x0F}")
    
    def decode(self, payload: bytes, ch10_header: Dict[str, Any]) -> Optional[DecodedPayload]:
        """Decode TSPI/CTS payload"""
        data_type = ch10_header.get('data_type', 0)
        
        if not self.can_decode(data_type):
            return None
        
        # Parse based on specific format
        if data_type == 0x70:
            return self._decode_gps_nmea(payload, ch10_header)
        elif data_type == 0x71:
            return self._decode_eag_acmi(payload, ch10_header)
        elif data_type == 0x72:
            return self._decode_actts(payload, ch10_header)
        else:
            return self._decode_generic_tspi(payload, ch10_header)
    
    def _decode_gps_nmea(self, payload: bytes, ch10_header: Dict[str, Any]) -> DecodedPayload:
        """Decode GPS NMEA-RTCM data (Format 0)"""
        decoded_data = {}
        errors = []
        
        # Parse IPH
        iph = self._parse_intra_packet_header(payload)
        if iph:
            decoded_data.update(iph)
            data_start = iph['data_start']
        else:
            data_start = 0
            errors.append("Failed to parse intra-packet header")
        
        # NMEA messages are typically ASCII text
        if data_start < len(payload):
            nmea_data = payload[data_start:]
            
            # Try to decode as ASCII
            try:
                nmea_text = nmea_data.decode('ascii').strip()
                decoded_data['nmea_messages'] = nmea_text.split('\n')
                decoded_data['message_count'] = len(decoded_data['nmea_messages'])
                
                # Parse individual NMEA sentences
                parsed_sentences = []
                for sentence in decoded_data['nmea_messages']:
                    if sentence.startswith('$'):
                        parsed_sentences.append(self._parse_nmea_sentence(sentence))
                
                decoded_data['parsed_sentences'] = parsed_sentences
                
            except UnicodeDecodeError:
                decoded_data['raw_nmea_data'] = nmea_data.hex()
                errors.append("Failed to decode NMEA data as ASCII")
        
        return DecodedPayload(
            data_type=0x70,
            data_type_name="GPS NMEA-RTCM",
            format_version=0,
            decoded_data=decoded_data,
            raw_payload=payload,
            errors=errors,
            metadata={'decoder': 'TSPICTSDecoder'}
        )
    
    def _decode_eag_acmi(self, payload: bytes, ch10_header: Dict[str, Any]) -> DecodedPayload:
        """Decode EAG ACMI data (Format 1)"""
        decoded_data = {}
        errors = []
        
        # Parse IPH
        iph = self._parse_intra_packet_header(payload)
        if iph:
            decoded_data.update(iph)
            data_start = iph['data_start']
        else:
            data_start = 0
            errors.append("Failed to parse intra-packet header")
        
        # EAG ACMI format parsing
        if data_start + 16 <= len(payload):
            # ACMI header structure (simplified)
            acmi_header = self._safe_unpack('<IIII', payload, data_start)
            if acmi_header:
                decoded_data.update({
                    'acmi_time_tag': acmi_header[0],
                    'acmi_entity_id': acmi_header[1],
                    'acmi_message_type': acmi_header[2],
                    'acmi_data_length': acmi_header[3]
                })
                
                # Parse ACMI data payload
                acmi_data_start = data_start + 16
                acmi_data_end = min(acmi_data_start + acmi_header[3], len(payload))
                decoded_data['acmi_payload'] = payload[acmi_data_start:acmi_data_end]
            else:
                errors.append("Failed to parse ACMI header")
        else:
            errors.append("Insufficient data for ACMI header")
        
        return DecodedPayload(
            data_type=0x71,
            data_type_name="EAG ACMI",
            format_version=1,
            decoded_data=decoded_data,
            raw_payload=payload,
            errors=errors,
            metadata={'decoder': 'TSPICTSDecoder'}
        )
    
    def _decode_actts(self, payload: bytes, ch10_header: Dict[str, Any]) -> DecodedPayload:
        """Decode ACTTS (Advanced Common Time & Test System) data (Format 2)"""
        decoded_data = {}
        errors = []
        
        # Parse IPH
        iph = self._parse_intra_packet_header(payload)
        if iph:
            decoded_data.update(iph)
            data_start = iph['data_start']
        else:
            data_start = 0
            errors.append("Failed to parse intra-packet header")
        
        # ACTTS timing format
        if data_start + 24 <= len(payload):
            # ACTTS header structure
            actts_data = self._safe_unpack('<QIIIIII', payload, data_start)
            if actts_data:
                decoded_data.update({
                    'actts_time_reference': actts_data[0],  # 64-bit time reference
                    'actts_time_format': actts_data[1],     # Time format indicator
                    'actts_clock_source': actts_data[2],    # Clock source ID
                    'actts_sync_status': actts_data[3],     # Synchronization status
                    'actts_time_quality': actts_data[4],    # Time quality indicator
                    'actts_reserved': actts_data[5]         # Reserved field
                })
                
                # Decode time format
                time_format = actts_data[1]
                decoded_data['time_format_description'] = self._decode_time_format(time_format)
                
                # Decode sync status
                sync_status = actts_data[3]
                decoded_data['sync_status_description'] = self._decode_sync_status(sync_status)
                
                # Parse additional ACTTS data if present
                additional_data_start = data_start + 24
                if additional_data_start < len(payload):
                    additional_data = payload[additional_data_start:]
                    decoded_data['additional_timing_data'] = additional_data.hex()
                    decoded_data['additional_data_length'] = len(additional_data)
            else:
                errors.append("Failed to parse ACTTS header")
        else:
            errors.append("Insufficient data for ACTTS header")
        
        return DecodedPayload(
            data_type=0x72,
            data_type_name="ACTTS",
            format_version=2,
            decoded_data=decoded_data,
            raw_payload=payload,
            errors=errors,
            metadata={'decoder': 'TSPICTSDecoder'}
        )
    
    def _decode_generic_tspi(self, payload: bytes, ch10_header: Dict[str, Any]) -> DecodedPayload:
        """Decode generic TSPI/CTS data (Formats 3-7)"""
        data_type = ch10_header.get('data_type', 0)
        decoded_data = {}
        errors = []
        
        # Parse IPH
        iph = self._parse_intra_packet_header(payload)
        if iph:
            decoded_data.update(iph)
            data_start = iph['data_start']
        else:
            data_start = 0
            errors.append("Failed to parse intra-packet header")
        
        # Generic parsing for unknown formats
        if data_start < len(payload):
            remaining_data = payload[data_start:]
            decoded_data['raw_data'] = remaining_data.hex()
            decoded_data['data_length'] = len(remaining_data)
            
            # Try to identify patterns
            if len(remaining_data) >= 4:
                # Check for timing patterns
                potential_timestamps = []
                for i in range(0, min(len(remaining_data) - 4, 64), 4):
                    timestamp = struct.unpack('<I', remaining_data[i:i+4])[0]
                    potential_timestamps.append(timestamp)
                
                decoded_data['potential_timestamps'] = potential_timestamps[:16]  # Limit output
        
        return DecodedPayload(
            data_type=data_type,
            data_type_name=self.get_data_type_name(data_type),
            format_version=data_type & 0x0F,
            decoded_data=decoded_data,
            raw_payload=payload,
            errors=errors,
            metadata={'decoder': 'TSPICTSDecoder'}
        )
    
    def _parse_nmea_sentence(self, sentence: str) -> Dict[str, Any]:
        """Parse individual NMEA sentence"""
        if not sentence.startswith('$') or '*' not in sentence:
            return {'raw': sentence, 'valid': False}
        
        # Split sentence and checksum
        parts = sentence.split('*')
        if len(parts) != 2:
            return {'raw': sentence, 'valid': False}
        
        data_part = parts[0][1:]  # Remove '$'
        checksum = parts[1]
        
        # Parse data fields
        fields = data_part.split(',')
        sentence_type = fields[0] if fields else ''
        
        return {
            'raw': sentence,
            'sentence_type': sentence_type,
            'fields': fields,
            'checksum': checksum,
            'valid': True
        }
    
    def _decode_time_format(self, time_format: int) -> str:
        """Decode ACTTS time format field"""
        formats = {
            0: "UTC",
            1: "GPS Time",
            2: "Local Time",
            3: "Mission Time",
            4: "Relative Time"
        }
        return formats.get(time_format, f"Unknown ({time_format})")
    
    def _decode_sync_status(self, sync_status: int) -> str:
        """Decode ACTTS synchronization status"""
        status_bits = {
            0x01: "Time Valid",
            0x02: "Sync Locked",
            0x04: "External Reference",
            0x08: "High Accuracy",
            0x10: "Leap Second Pending"
        }
        
        active_flags = []
        for bit, description in status_bits.items():
            if sync_status & bit:
                active_flags.append(description)
        
        return ", ".join(active_flags) if active_flags else "No Status"


# Specific decoder instances
class ACTTSDecoder(TSPICTSDecoder):
    """Dedicated ACTTS decoder"""
    
    def can_decode(self, data_type: int) -> bool:
        return data_type == 0x72


class GPSNMEADecoder(TSPICTSDecoder):
    """Dedicated GPS NMEA decoder"""
    
    def can_decode(self, data_type: int) -> bool:
        return data_type == 0x70


class EAGACMIDecoder(TSPICTSDecoder):
    """Dedicated EAG ACMI decoder"""
    
    def can_decode(self, data_type: int) -> bool:
        return data_type == 0x71