// Package modes provides Mode S and ADS-B message decoding capabilities.
//
// Mode S is a secondary surveillance radar system that enables aircraft to transmit
// detailed information including position, altitude, velocity, and identification.
// ADS-B (Automatic Dependent Surveillance-Broadcast) is a modernization of Mode S
// that provides more precise and frequent position reports.
//
// This package implements:
//   - Complete Mode S message decoding for all downlink formats
//   - ADS-B extended squitter message parsing (DF17/18)
//   - CPR (Compact Position Reporting) position decoding algorithm
//   - Aircraft identification, category, and status decoding
//   - Velocity and heading calculation from velocity messages
//   - Navigation accuracy and integrity decoding
//
// Key Features:
//   - CPR Global Position Decoding: Resolves ambiguous encoded positions using
//     even/odd message pairs and trigonometric calculations
//   - Multi-format Support: Handles surveillance replies, extended squitter,
//     and various ADS-B message types
//   - Real-time Processing: Maintains state for CPR decoding across messages
//   - Comprehensive Data Extraction: Extracts all available aircraft parameters
//
// CPR Algorithm:
// The Compact Position Reporting format encodes latitude and longitude using
// two alternating formats (even/odd) that create overlapping grids. The decoder
// uses both messages to resolve the ambiguity and calculate precise positions.
package modes

import (
	"fmt"
	"math"
	"sync"
)

// crcTable for Mode S CRC-24 validation
var crcTable [256]uint32

func init() {
	// Initialize CRC table for Mode S CRC-24 (polynomial 0x1FFF409)
	for i := 0; i < 256; i++ {
		crc := uint32(i) << 16
		for j := 0; j < 8; j++ {
			if crc&0x800000 != 0 {
				crc = (crc << 1) ^ 0x1FFF409
			} else {
				crc = crc << 1
			}
		}
		crcTable[i] = crc & 0xFFFFFF
	}
}

// validateModeSCRC validates the 24-bit CRC of a Mode S message
func validateModeSCRC(data []byte) bool {
	if len(data) < 4 {
		return false
	}
	
	// Calculate CRC for all bytes except the last 3 (which contain the CRC)
	crc := uint32(0)
	for i := 0; i < len(data)-3; i++ {
		crc = ((crc << 8) ^ crcTable[((crc>>16)^uint32(data[i]))&0xFF]) & 0xFFFFFF
	}
	
	// Extract transmitted CRC from last 3 bytes
	transmittedCRC := uint32(data[len(data)-3])<<16 | uint32(data[len(data)-2])<<8 | uint32(data[len(data)-1])
	
	return crc == transmittedCRC
}

// Mode S Downlink Format (DF) constants.
// The DF field (first 5 bits) determines the message type and structure.
const (
	DF0  = 0  // Short air-air surveillance (ACAS)
	DF4  = 4  // Surveillance altitude reply (interrogation response)
	DF5  = 5  // Surveillance identity reply (squawk code response)
	DF11 = 11 // All-call reply (capability and ICAO address)
	DF16 = 16 // Long air-air surveillance (ACAS with altitude)
	DF17 = 17 // Extended squitter (ADS-B from transponder)
	DF18 = 18 // Extended squitter/non-transponder (ADS-B from other sources)
	DF19 = 19 // Military extended squitter
	DF20 = 20 // Comm-B altitude reply (BDS register data)
	DF21 = 21 // Comm-B identity reply (BDS register data)
	DF24 = 24 // Comm-D (ELM - Enhanced Length Message)
)

// ADS-B Type Code (TC) constants for DF17/18 extended squitter messages.
// The type code (bits 32-36) determines the content and format of ADS-B messages.
const (
	TC_IDENT_CATEGORY   = 1  // Aircraft identification and category (callsign)
	TC_SURFACE_POS      = 5  // Surface position (airport ground movement)
	TC_AIRBORNE_POS_9   = 9  // Airborne position (barometric altitude)
	TC_AIRBORNE_POS_20  = 20 // Airborne position (GNSS height above ellipsoid)
	TC_AIRBORNE_VEL     = 19 // Airborne velocity (ground speed and track)
	TC_AIRBORNE_POS_GPS = 22 // Airborne position (GNSS altitude)
	TC_RESERVED         = 23 // Reserved for future use
	TC_SURFACE_SYSTEM   = 24 // Surface system status
	TC_OPERATIONAL      = 31 // Aircraft operational status (capabilities)
)

// Aircraft represents a complete set of decoded aircraft data from Mode S/ADS-B messages.
//
// This structure contains all possible information that can be extracted from
// various Mode S and ADS-B message types, including position, velocity, status,
// and navigation data. Not all fields will be populated for every aircraft,
// depending on the messages received and aircraft capabilities.
type Aircraft struct {
	// Core Identification
	ICAO24           uint32  // 24-bit ICAO aircraft address (unique identifier)
	Callsign         string  // 8-character flight callsign (from identification messages)
	
	// Position and Navigation
	Latitude         float64 // Position latitude in decimal degrees
	Longitude        float64 // Position longitude in decimal degrees
	Altitude         int     // Altitude in feet (barometric or geometric)
	BaroAltitude     int     // Barometric altitude in feet (QNH corrected)
	GeomAltitude     int     // Geometric altitude in feet (GNSS height)
	
	// Motion and Dynamics
	VerticalRate     int     // Vertical rate in feet per minute (climb/descent)
	GroundSpeed      int     // Ground speed in knots (integer)
	Track            int     // Track angle in degrees (0-359, integer)
	Heading          int     // Aircraft heading in degrees (magnetic, integer)
	
	// Aircraft Information
	Category         string  // Aircraft category (size, type, performance)
	Squawk           string  // 4-digit transponder squawk code (octal)
	
	// Status and Alerts
	Emergency        string  // Emergency/priority status description
	OnGround         bool    // Aircraft is on ground (surface movement)
	Alert            bool    // Alert flag (ATC attention required)
	SPI              bool    // Special Position Identification (pilot activated)
	
	// Data Quality Indicators
	NACp             uint8   // Navigation Accuracy Category - Position (0-11)
	NACv             uint8   // Navigation Accuracy Category - Velocity (0-4)
	SIL              uint8   // Surveillance Integrity Level (0-3)
	
	// Autopilot/Flight Management
	SelectedAltitude int     // MCP/FCU selected altitude in feet
	SelectedHeading  float64 // MCP/FCU selected heading in degrees
	BaroSetting      float64 // Barometric pressure setting (QNH) in millibars
}

// Decoder handles Mode S and ADS-B message decoding with CPR position resolution.
//
// The decoder maintains state for CPR (Compact Position Reporting) decoding,
// which requires pairs of even/odd messages to resolve position ambiguity.
// Each aircraft (identified by ICAO24) has separate CPR state tracking.
//
// CPR Position Decoding:
// Aircraft positions are encoded using two alternating formats that create
// overlapping latitude/longitude grids. The decoder stores both even and odd
// encoded positions and uses trigonometric calculations to resolve the
// actual aircraft position when both are available.
type Decoder struct {
	// CPR (Compact Position Reporting) state tracking per aircraft
	cprEvenLat  map[uint32]float64 // Even message latitude encoding (ICAO24 -> normalized lat)
	cprEvenLon  map[uint32]float64 // Even message longitude encoding (ICAO24 -> normalized lon)
	cprOddLat   map[uint32]float64 // Odd message latitude encoding (ICAO24 -> normalized lat)
	cprOddLon   map[uint32]float64 // Odd message longitude encoding (ICAO24 -> normalized lon)
	cprEvenTime map[uint32]int64   // Timestamp of even message (for freshness comparison)
	cprOddTime  map[uint32]int64   // Timestamp of odd message (for freshness comparison)
	
	// Reference position for CPR zone ambiguity resolution (receiver location)
	refLatitude  float64 // Receiver latitude in decimal degrees
	refLongitude float64 // Receiver longitude in decimal degrees
	
	// Mutex to protect concurrent access to CPR maps
	mu sync.RWMutex
}

// NewDecoder creates a new Mode S/ADS-B decoder with initialized CPR tracking.
//
// The reference position (typically the receiver location) is used to resolve
// CPR zone ambiguity during position decoding. Without a proper reference,
// aircraft can appear many degrees away from their actual position.
//
// Parameters:
//   - refLat: Reference latitude in decimal degrees (receiver location)
//   - refLon: Reference longitude in decimal degrees (receiver location)
//
// Returns a configured decoder ready for message processing.
func NewDecoder(refLat, refLon float64) *Decoder {
	return &Decoder{
		cprEvenLat:   make(map[uint32]float64),
		cprEvenLon:   make(map[uint32]float64),
		cprOddLat:    make(map[uint32]float64),
		cprOddLon:    make(map[uint32]float64),
		cprEvenTime:  make(map[uint32]int64),
		cprOddTime:   make(map[uint32]int64),
		refLatitude:  refLat,
		refLongitude: refLon,
	}
}

// Decode processes a Mode S message and extracts all available aircraft information.
//
// This is the main entry point for message decoding. The method:
//   1. Validates message length and extracts the Downlink Format (DF)
//   2. Extracts the ICAO24 aircraft address
//   3. Routes to appropriate decoder based on message type
//   4. Returns populated Aircraft struct with available data
//
// Different message types provide different information:
//   - DF4/20: Altitude only
//   - DF5/21: Squawk code only
//   - DF17/18: Complete ADS-B data (position, velocity, identification, etc.)
//
// Parameters:
//   - data: Raw Mode S message bytes (7 or 14 bytes depending on type)
//
// Returns decoded Aircraft struct or error for invalid/incomplete messages.
func (d *Decoder) Decode(data []byte) (*Aircraft, error) {
	if len(data) < 7 {
		return nil, fmt.Errorf("message too short: %d bytes", len(data))
	}

	// Validate CRC to reject corrupted messages that create ghost targets
	if !validateModeSCRC(data) {
		return nil, fmt.Errorf("invalid CRC - corrupted message")
	}

	df := (data[0] >> 3) & 0x1F
	icao := d.extractICAO(data, df)

	aircraft := &Aircraft{
		ICAO24: icao,
	}

	switch df {
	case DF4, DF20:
		aircraft.Altitude = d.decodeAltitude(data)
	case DF5, DF21:
		aircraft.Squawk = d.decodeSquawk(data)
	case DF17, DF18:
		return d.decodeExtendedSquitter(data, aircraft)
	}

	return aircraft, nil
}

// extractICAO extracts the ICAO 24-bit aircraft address from Mode S messages.
//
// For most downlink formats, the ICAO address is located in bytes 1-3 of the
// message. Some formats may have different layouts, but this implementation
// uses the standard position for all supported formats.
//
// Parameters:
//   - data: Mode S message bytes
//   - df: Downlink format (currently unused, but available for format-specific handling)
//
// Returns the 24-bit ICAO address as a uint32.
func (d *Decoder) extractICAO(data []byte, df uint8) uint32 {
	// For most formats, ICAO is in bytes 1-3
	return uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3])
}

// decodeExtendedSquitter processes ADS-B extended squitter messages (DF17/18).
//
// Extended squitter messages contain the richest aircraft data, including:
//   - Aircraft identification and category (TC 1-4)
//   - Surface position and movement (TC 5-8)
//   - Airborne position with various altitude sources (TC 9-18, 20-22)
//   - Velocity and heading information (TC 19)
//   - Aircraft status and emergency codes (TC 28)
//   - Target state and autopilot settings (TC 29)
//   - Operational status and navigation accuracy (TC 31)
//
// The method routes messages to specific decoders based on the Type Code (TC)
// field in bits 32-36 of the message.
//
// Parameters:
//   - data: 14-byte extended squitter message
//   - aircraft: Aircraft struct to populate with decoded data
//
// Returns the updated Aircraft struct or an error for malformed messages.
func (d *Decoder) decodeExtendedSquitter(data []byte, aircraft *Aircraft) (*Aircraft, error) {
	if len(data) < 14 {
		return nil, fmt.Errorf("extended squitter too short: %d bytes", len(data))
	}

	tc := (data[4] >> 3) & 0x1F

	switch {
	case tc >= 1 && tc <= 4:
		// Aircraft identification
		d.decodeIdentification(data, aircraft)
	case tc >= 5 && tc <= 8:
		// Surface position
		d.decodeSurfacePosition(data, aircraft)
	case tc >= 9 && tc <= 18:
		// Airborne position
		d.decodeAirbornePosition(data, aircraft)
	case tc == 19:
		// Airborne velocity
		d.decodeVelocity(data, aircraft)
	case tc >= 20 && tc <= 22:
		// Airborne position with GNSS
		d.decodeAirbornePosition(data, aircraft)
	case tc == 28:
		// Aircraft status
		d.decodeStatus(data, aircraft)
	case tc == 29:
		// Target state and status
		d.decodeTargetState(data, aircraft)
	case tc == 31:
		// Operational status
		d.decodeOperationalStatus(data, aircraft)
	}

	return aircraft, nil
}

// decodeIdentification extracts aircraft callsign and category from identification messages.
//
// Aircraft identification messages (TC 1-4) contain:
//   - 8-character callsign encoded in 6-bit characters
//   - Aircraft category indicating size, performance, and type
//
// Callsign Encoding:
// Each character is encoded in 6 bits using a custom character set:
//   - Characters: "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"
//   - Index 0-63 maps to the character at that position
//   - '#' represents space or invalid characters
//
// Parameters:
//   - data: Extended squitter message containing identification data
//   - aircraft: Aircraft struct to update with callsign and category
func (d *Decoder) decodeIdentification(data []byte, aircraft *Aircraft) {
	tc := (data[4] >> 3) & 0x1F

	// Category
	aircraft.Category = d.getAircraftCategory(tc, data[4]&0x07)

	// Callsign - 8 characters encoded in 6 bits each
	chars := "#ABCDEFGHIJKLMNOPQRSTUVWXYZ##### ###############0123456789######"
	callsign := ""

	// Extract 48 bits starting from bit 40
	for i := 0; i < 8; i++ {
		bitOffset := 40 + i*6
		byteOffset := bitOffset / 8
		bitShift := bitOffset % 8

		var charCode uint8
		if bitShift <= 2 {
			charCode = (data[byteOffset] >> (2 - bitShift)) & 0x3F
		} else {
			charCode = ((data[byteOffset] << (bitShift - 2)) & 0x3F) |
				(data[byteOffset+1] >> (10 - bitShift))
		}

		if charCode < 64 {
			callsign += string(chars[charCode])
		}
	}

	aircraft.Callsign = callsign
}

// decodeAirbornePosition extracts aircraft position from CPR-encoded position messages.
//
// Airborne position messages (TC 9-18, 20-22) contain:
//   - Altitude information (barometric or geometric)
//   - CPR-encoded latitude and longitude
//   - Even/odd flag for CPR decoding
//
// CPR (Compact Position Reporting) Process:
//   1. Extract the even/odd flag and CPR lat/lon values
//   2. Normalize CPR values to 0-1 range (divide by 2^17)
//   3. Store values for this aircraft's ICAO address
//   4. Attempt position decoding if both even and odd messages are available
//
// The actual position calculation requires both even and odd messages to
// resolve the ambiguity inherent in the compressed encoding format.
//
// Parameters:
//   - data: Extended squitter message containing position data
//   - aircraft: Aircraft struct to update with position and altitude
func (d *Decoder) decodeAirbornePosition(data []byte, aircraft *Aircraft) {
	tc := (data[4] >> 3) & 0x1F

	// Altitude
	altBits := (uint16(data[5])<<4 | uint16(data[6])>>4) & 0x0FFF
	aircraft.Altitude = d.decodeAltitudeBits(altBits, tc)

	// CPR latitude/longitude
	cprLat := uint32(data[6]&0x03)<<15 | uint32(data[7])<<7 | uint32(data[8])>>1
	cprLon := uint32(data[8]&0x01)<<16 | uint32(data[9])<<8 | uint32(data[10])
	oddFlag := (data[6] >> 2) & 0x01

	// Store CPR values for later decoding (protected by mutex)
	d.mu.Lock()
	if oddFlag == 1 {
		d.cprOddLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
		d.cprOddLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
	} else {
		d.cprEvenLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
		d.cprEvenLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
	}
	d.mu.Unlock()

	// Try to decode position if we have both even and odd messages
	d.decodeCPRPosition(aircraft)
}

// decodeCPRPosition performs CPR (Compact Position Reporting) global position decoding.
//
// CRITICAL: The CPR algorithm has zone ambiguity that requires either:
// 1. A reference position (receiver location) to resolve zones correctly, OR
// 2. Message timestamp comparison to choose the most recent valid position
//
// Without proper zone resolution, aircraft can appear 6+ degrees away from actual position.
// This implementation uses global decoding which can produce large errors without
// additional context about expected aircraft location.
//
// Parameters:
//   - aircraft: Aircraft struct to update with decoded position
func (d *Decoder) decodeCPRPosition(aircraft *Aircraft) {
	// Read CPR values with read lock
	d.mu.RLock()
	evenLat, evenExists := d.cprEvenLat[aircraft.ICAO24]
	oddLat, oddExists := d.cprOddLat[aircraft.ICAO24]

	if !evenExists || !oddExists {
		d.mu.RUnlock()
		return
	}

	evenLon := d.cprEvenLon[aircraft.ICAO24]
	oddLon := d.cprOddLon[aircraft.ICAO24]
	d.mu.RUnlock()

	// CPR input values ready for decoding

	// CPR decoding algorithm
	dLat := 360.0 / 60.0
	j := math.Floor(evenLat*59 - oddLat*60 + 0.5)

	latEven := dLat * (math.Mod(j, 60) + evenLat)
	latOdd := dLat * (math.Mod(j, 59) + oddLat)

	if latEven >= 270 {
		latEven -= 360
	}
	if latOdd >= 270 {
		latOdd -= 360
	}

	// Additional range correction to ensure valid latitude bounds (-90° to +90°)
	if latEven > 90 {
		latEven = 180 - latEven
	} else if latEven < -90 {
		latEven = -180 - latEven
	}
	
	if latOdd > 90 {
		latOdd = 180 - latOdd
	} else if latOdd < -90 {
		latOdd = -180 - latOdd
	}

	// Validate final latitude values are within acceptable range
	if math.Abs(latOdd) > 90 || math.Abs(latEven) > 90 {
		// Invalid CPR decoding - skip position update
		return
	}

	// Zone ambiguity resolution using receiver reference position
	// Calculate which decoded latitude is closer to the receiver
	distToEven := math.Abs(latEven - d.refLatitude)
	distToOdd := math.Abs(latOdd - d.refLatitude)
	
	// Choose the latitude solution that's closer to the receiver position
	if distToOdd < distToEven {
		aircraft.Latitude = latOdd
	} else {
		aircraft.Latitude = latEven
	}

	// Longitude calculation
	nl := d.nlFunction(aircraft.Latitude)
	ni := math.Max(nl-1, 1)
	dLon := 360.0 / ni
	m := math.Floor(evenLon*(nl-1) - oddLon*nl + 0.5)
	lon := dLon * (math.Mod(m, ni) + oddLon)

	if lon >= 180 {
		lon -= 360
	} else if lon <= -180 {
		lon += 360
	}

	// Validate longitude is within acceptable range
	if math.Abs(lon) > 180 {
		// Invalid longitude - skip position update
		return
	}

	aircraft.Longitude = lon
	
	// CPR decoding completed successfully
}

// nlFunction calculates the number of longitude zones (NL) for a given latitude.
//
// This function implements the NL(lat) calculation defined in the CPR specification.
// The number of longitude zones decreases as latitude approaches the poles due to
// the convergence of meridians.
//
// Mathematical Background:
// - At the equator: 60 longitude zones (6° each)
// - At higher latitudes: fewer zones as meridians converge
// - At poles (±87°): only 2 zones (180° each)
//
// Formula: NL(lat) = floor(2π / arccos(1 - (1-cos(π/(2*NZ))) / cos²(lat)))
// Where NZ = 15 (number of latitude zones)
//
// Parameters:
//   - lat: Latitude in decimal degrees
//
// Returns the number of longitude zones for this latitude.
func (d *Decoder) nlFunction(lat float64) float64 {
	if math.Abs(lat) >= 87 {
		return 2
	}

	nz := 15.0
	a := 1 - math.Cos(math.Pi/(2*nz))
	b := math.Pow(math.Cos(math.Pi/180.0*math.Abs(lat)), 2)
	nl := 2 * math.Pi / math.Acos(1-a/b)

	return math.Floor(nl)
}

// decodeVelocity extracts ground speed, track, and vertical rate from velocity messages.
//
// Velocity messages (TC 19) contain:
//   - Ground speed components (East-West and North-South)
//   - Vertical rate (climb/descent rate)
//   - Intent change flag and other status bits
//
// Ground Speed Calculation:
//   - East-West and North-South velocity components are encoded separately
//   - Each component has a direction bit and magnitude
//   - Ground speed = sqrt(EW² + NS²)
//   - Track angle = atan2(EW, NS) converted to degrees
//
// Vertical Rate:
//   - Encoded in 64 ft/min increments with sign bit
//   - Range: approximately ±32,000 ft/min
//
// Parameters:
//   - data: Extended squitter message containing velocity data
//   - aircraft: Aircraft struct to update with velocity information
func (d *Decoder) decodeVelocity(data []byte, aircraft *Aircraft) {
	subtype := (data[4]) & 0x07

	if subtype == 1 || subtype == 2 {
		// Ground speed
		ewRaw := uint16(data[5]&0x03)<<8 | uint16(data[6])
		nsRaw := uint16(data[7])<<3 | uint16(data[8])>>5

		ewVel := float64(ewRaw - 1)
		nsVel := float64(nsRaw - 1)

		if data[5]&0x04 != 0 {
			ewVel = -ewVel
		}
		if data[7]&0x80 != 0 {
			nsVel = -nsVel
		}

		// Calculate ground speed in knots (rounded to integer)
		speedKnots := math.Sqrt(ewVel*ewVel + nsVel*nsVel)
		
		// Validate speed range (0-600 knots for civilian aircraft)
		if speedKnots > 600 {
			speedKnots = 600 // Cap at reasonable maximum
		}
		aircraft.GroundSpeed = int(math.Round(speedKnots))
		
		// Calculate track in degrees (0-359)
		trackDeg := math.Atan2(ewVel, nsVel) * 180 / math.Pi
		if trackDeg < 0 {
			trackDeg += 360
		}
		aircraft.Track = int(math.Round(trackDeg))
	}

	// Vertical rate
	vrSign := (data[8] >> 3) & 0x01
	vrBits := uint16(data[8]&0x07)<<6 | uint16(data[9])>>2
	if vrBits != 0 {
		aircraft.VerticalRate = int(vrBits-1) * 64
		if vrSign != 0 {
			aircraft.VerticalRate = -aircraft.VerticalRate
		}
	}
}

// decodeAltitude extracts altitude from Mode S surveillance altitude replies.
//
// Mode S altitude replies (DF4, DF20) contain a 13-bit altitude code that
// must be converted from the transmitted encoding to actual altitude in feet.
//
// Parameters:
//   - data: Mode S altitude reply message
//
// Returns altitude in feet above sea level.
func (d *Decoder) decodeAltitude(data []byte) int {
	altCode := uint16(data[2])<<8 | uint16(data[3])
	return d.decodeAltitudeBits(altCode>>3, 0)
}

// decodeAltitudeBits converts encoded altitude bits to altitude in feet.
//
// Altitude Encoding:
//   - Uses modified Gray code for error resilience
//   - 12-bit altitude code with 25-foot increments
//   - Offset of -1000 feet (code 0 = -1000 ft)
//   - Gray code conversion prevents single-bit errors from causing large altitude jumps
//
// Different altitude sources:
//   - Standard: Barometric altitude (QNH corrected)
//   - GNSS: Geometric altitude (height above WGS84 ellipsoid)
//
// Parameters:
//   - altCode: 12-bit encoded altitude value
//   - tc: Type code (determines altitude source interpretation)
//
// Returns altitude in feet, or 0 for invalid altitude codes.
func (d *Decoder) decodeAltitudeBits(altCode uint16, tc uint8) int {
	if altCode == 0 {
		return 0
	}

	// Standard altitude encoding with 25 ft increments
	// Check Q-bit (bit 4) for encoding type
	qBit := (altCode >> 4) & 1
	
	if qBit == 1 {
		// Standard altitude with Q-bit set
		// Remove Q-bit and reassemble 11-bit altitude code
		n := ((altCode & 0x1F80) >> 2) | ((altCode & 0x0020) >> 1) | (altCode & 0x000F)
		alt := int(n)*25 - 1000
		
		// Validate altitude range
		if alt < -1000 || alt > 60000 {
			return 0
		}
		return alt
	}
	
	// Gray code altitude (100 ft increments) - legacy encoding
	// Convert from Gray code to binary
	n := altCode
	n ^= n >> 8
	n ^= n >> 4
	n ^= n >> 2
	n ^= n >> 1
	
	// Convert to altitude in feet
	alt := int(n&0x7FF) * 100
	if alt < 0 || alt > 60000 {
		return 0
	}
	return alt
}

// decodeSquawk extracts the 4-digit squawk (transponder) code from identity replies.
//
// Squawk codes are 4-digit octal numbers (0000-7777) used by air traffic control
// for aircraft identification. They are transmitted in surveillance identity
// replies (DF5, DF21) and formatted as octal strings.
//
// Parameters:
//   - data: Mode S identity reply message
//
// Returns 4-digit octal squawk code as a string (e.g., "1200", "7700").
func (d *Decoder) decodeSquawk(data []byte) string {
	code := uint16(data[2])<<8 | uint16(data[3])
	return fmt.Sprintf("%04o", code>>3)
}

// getAircraftCategory converts type code and category fields to human-readable descriptions.
//
// Aircraft categories are encoded in identification messages using:
//   - Type Code (TC): Broad category group (1-4)
//   - Category (CA): Specific category within the group (0-7)
//
// Categories include:
//   - TC 1: Reserved
//   - TC 2: Surface vehicles (emergency, service, obstacles)
//   - TC 3: Light aircraft (gliders, balloons, UAVs, etc.)
//   - TC 4: Aircraft by weight class and performance
//
// These categories help ATC and other aircraft understand the type of vehicle
// and its performance characteristics for separation and routing.
//
// Parameters:
//   - tc: Type code (1-4) from identification message
//   - ca: Category field (0-7) providing specific subtype
//
// Returns human-readable category description.
func (d *Decoder) getAircraftCategory(tc uint8, ca uint8) string {
	switch tc {
	case 1:
		return "Reserved"
	case 2:
		switch ca {
		case 1:
			return "Surface Emergency Vehicle"
		case 3:
			return "Surface Service Vehicle"
		case 4, 5, 6, 7:
			return "Ground Obstruction"
		default:
			return "Surface Vehicle"
		}
	case 3:
		switch ca {
		case 1:
			return "Glider/Sailplane"
		case 2:
			return "Lighter-than-Air"
		case 3:
			return "Parachutist/Skydiver"
		case 4:
			return "Ultralight/Hang-glider"
		case 6:
			return "UAV"
		case 7:
			return "Space Vehicle"
		default:
			return "Light Aircraft"
		}
	case 4:
		switch ca {
		case 1:
			return "Light < 7000kg"
		case 2:
			return "Medium 7000-34000kg"
		case 3:
			return "Medium 34000-136000kg"
		case 4:
			return "High Vortex Large"
		case 5:
			return "Heavy > 136000kg"
		case 6:
			return "High Performance"
		case 7:
			return "Rotorcraft"
		default:
			return "Aircraft"
		}
	default:
		return "Unknown"
	}
}

// decodeStatus extracts emergency and priority status from aircraft status messages.
//
// Aircraft status messages (TC 28) contain emergency and priority codes that
// indicate special situations requiring ATC attention:
//   - General emergency (Mayday)
//   - Medical emergency (Lifeguard)
//   - Minimum fuel
//   - Communication failure
//   - Unlawful interference (hijack)
//   - Downed aircraft
//
// These codes trigger special handling by ATC and emergency services.
//
// Parameters:
//   - data: Extended squitter message containing status information
//   - aircraft: Aircraft struct to update with emergency status
func (d *Decoder) decodeStatus(data []byte, aircraft *Aircraft) {
	subtype := data[4] & 0x07

	if subtype == 1 {
		// Emergency/priority status
		emergency := (data[5] >> 5) & 0x07
		switch emergency {
		case 0:
			aircraft.Emergency = "None"
		case 1:
			aircraft.Emergency = "General Emergency"
		case 2:
			aircraft.Emergency = "Lifeguard/Medical"
		case 3:
			aircraft.Emergency = "Minimum Fuel"
		case 4:
			aircraft.Emergency = "No Communications"
		case 5:
			aircraft.Emergency = "Unlawful Interference"
		case 6:
			aircraft.Emergency = "Downed Aircraft"
		}
	}
}

// decodeTargetState extracts autopilot and flight management system settings.
//
// Target state and status messages (TC 29) contain information about:
//   - Selected altitude (MCP/FCU setting)
//   - Barometric pressure setting (QNH)
//   - Autopilot engagement status
//   - Flight management system intentions
//
// This information helps ATC understand pilot intentions and autopilot settings,
// improving situational awareness and conflict prediction.
//
// Parameters:
//   - data: Extended squitter message containing target state data
//   - aircraft: Aircraft struct to update with autopilot settings
func (d *Decoder) decodeTargetState(data []byte, aircraft *Aircraft) {
	// Selected altitude
	altBits := uint16(data[5]&0x7F)<<4 | uint16(data[6])>>4
	if altBits != 0 {
		aircraft.SelectedAltitude = int(altBits)*32 - 32
	}

	// Barometric pressure setting
	baroBits := uint16(data[7])<<1 | uint16(data[8])>>7
	if baroBits != 0 {
		aircraft.BaroSetting = float64(baroBits)*0.8 + 800
	}
}

// decodeOperationalStatus extracts navigation accuracy and system capability information.
//
// Operational status messages (TC 31) contain:
//   - Navigation Accuracy Category for Position (NACp): Position accuracy
//   - Navigation Accuracy Category for Velocity (NACv): Velocity accuracy  
//   - Surveillance Integrity Level (SIL): System integrity confidence
//
// These parameters help receiving systems assess data quality and determine
// appropriate separation standards for the aircraft.
//
// Parameters:
//   - data: Extended squitter message containing operational status
//   - aircraft: Aircraft struct to update with accuracy indicators
func (d *Decoder) decodeOperationalStatus(data []byte, aircraft *Aircraft) {
	// Navigation accuracy categories
	aircraft.NACp = (data[7] >> 4) & 0x0F
	aircraft.NACv = data[7] & 0x0F
	aircraft.SIL = (data[8] >> 6) & 0x03
}

// decodeSurfacePosition extracts position and movement data for aircraft on the ground.
//
// Surface position messages (TC 5-8) are used for airport ground movement tracking:
//   - Ground speed and movement direction
//   - Track angle (direction of movement)
//   - CPR-encoded position (same algorithm as airborne)
//   - On-ground flag is automatically set
//
// Ground Movement Encoding:
//   - Speed ranges from stationary to 175+ knots in non-linear increments
//   - Track is encoded in 128 discrete directions (2.8125° resolution)
//   - Position uses the same CPR encoding as airborne messages
//
// Parameters:
//   - data: Extended squitter message containing surface position data
//   - aircraft: Aircraft struct to update with ground movement information
func (d *Decoder) decodeSurfacePosition(data []byte, aircraft *Aircraft) {
	aircraft.OnGround = true

	// Movement
	movement := uint8(data[4]&0x07)<<4 | uint8(data[5])>>4
	if movement > 0 && movement < 125 {
		aircraft.GroundSpeed = int(math.Round(d.decodeGroundSpeed(movement)))
	}

	// Track
	trackValid := (data[5] >> 3) & 0x01
	if trackValid != 0 {
		trackBits := uint16(data[5]&0x07)<<4 | uint16(data[6])>>4
		aircraft.Track = int(math.Round(float64(trackBits) * 360.0 / 128.0))
	}

	// CPR position (similar to airborne)
	cprLat := uint32(data[6]&0x03)<<15 | uint32(data[7])<<7 | uint32(data[8])>>1
	cprLon := uint32(data[8]&0x01)<<16 | uint32(data[9])<<8 | uint32(data[10])
	oddFlag := (data[6] >> 2) & 0x01

	// Store CPR values for later decoding (protected by mutex)
	d.mu.Lock()
	if oddFlag == 1 {
		d.cprOddLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
		d.cprOddLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
	} else {
		d.cprEvenLat[aircraft.ICAO24] = float64(cprLat) / 131072.0
		d.cprEvenLon[aircraft.ICAO24] = float64(cprLon) / 131072.0
	}
	d.mu.Unlock()

	d.decodeCPRPosition(aircraft)
}

// decodeGroundSpeed converts the surface movement field to ground speed in knots.
//
// Surface movement is encoded in non-linear ranges optimized for typical
// ground operations:
//   - 0: No movement information
//   - 1: Stationary
//   - 2-8: 0.125-1.0 kt (fine resolution for slow movement)
//   - 9-12: 1.0-2.0 kt (taxi speeds)
//   - 13-38: 2.0-15.0 kt (normal taxi)
//   - 39-93: 15.0-70.0 kt (high speed taxi/runway)
//   - 94-108: 70.0-100.0 kt (takeoff/landing roll)
//   - 109-123: 100.0-175.0 kt (high speed operations)
//   - 124: >175 kt
//
// Parameters:
//   - movement: 7-bit movement field from surface position message
//
// Returns ground speed in knots, or 0 for invalid/no movement.
func (d *Decoder) decodeGroundSpeed(movement uint8) float64 {
	if movement == 1 {
		return 0
	} else if movement >= 2 && movement <= 8 {
		return float64(movement-2)*0.125 + 0.125
	} else if movement >= 9 && movement <= 12 {
		return float64(movement-9)*0.25 + 1.0
	} else if movement >= 13 && movement <= 38 {
		return float64(movement-13)*0.5 + 2.0
	} else if movement >= 39 && movement <= 93 {
		return float64(movement-39)*1.0 + 15.0
	} else if movement >= 94 && movement <= 108 {
		return float64(movement-94)*2.0 + 70.0
	} else if movement >= 109 && movement <= 123 {
		return float64(movement-109)*5.0 + 100.0
	} else if movement == 124 {
		return 175.0
	}
	return 0
}
