"""
Constants for DOA estimation with the 4-element UCA antenna.

Physical Setup:
    - 4-element Uniform Circular Array (UCA)
    - Elements at corners: 315°, 45°, 135°, 225° (North-CW convention)
    - Front of antenna = 0° (North)
    - Back of antenna = 180° (cables)

Channel Mapping:
    - channel-0 = Element 1 at 315°
    - channel-1 = Element 2 at 45°
    - channel-2 = Element 3 at 135°
    - channel-3 = Element 4 at 225°

Coordinate Convention:
    - Angles in North-CW (0° = North, 90° = East, clockwise positive)
    - Steering vector uses exp(-j·k·r) for incoming waves
"""

import numpy as np

# =============================================================================
# PHYSICAL CONSTANTS
# =============================================================================
C = 3e8  # Speed of light (m/s)

# =============================================================================
# ANTENNA GEOMETRY
# =============================================================================
RADIUS = 0.030  # Array radius in meters (30 mm)
NUM_ELEMENTS = 4

# Physical element positions (North-CW convention, degrees)
# Element 1 at 315°, Element 2 at 45°, Element 3 at 135°, Element 4 at 225°
ELEMENT_POSITIONS_DEG = {
    0: 315,  # channel-0 = Element 1
    1: 45,   # channel-1 = Element 2
    2: 135,  # channel-2 = Element 3
    3: 225,  # channel-3 = Element 4
}

# =============================================================================
# CHANNEL ORDERING
# =============================================================================
# Maps array element index to file channel number.
# The physical channels don't map 1:1 to doa_py's expected element order.
# This reordering was determined by brute-force testing all 24 permutations
# to find which gives correct DOA estimates.
#
# Mapping: Array elem 0 <- ch2, elem 1 <- ch3, elem 2 <- ch0, elem 3 <- ch1
CHANNEL_ORDER = [2, 3, 0, 1]

# =============================================================================
# ARRAY ROTATION
# =============================================================================
# Rotation angle to align doa_py's UCA model with our physical array orientation.
#
# WHY THIS IS NEEDED:
#   - doa_py's UniformCircularArray places elements at 0°, 90°, 180°, 270°
#     using math convention (East=0°, counter-clockwise positive)
#   - Our physical array has a different orientation and uses North-CW convention
#   - This rotation corrects for the mismatch so MUSIC outputs correct angles
#
# HOW IT WAS DETERMINED:
#   - Tested all rotations from -180° to +180° across multiple frequencies
#     (1200MHz, 1500MHz, 2400MHz, 5700MHz)
#   - Found the rotation that minimizes mean bias across ALL frequencies
#   - Result: +155° gives mean bias of only ~2° across all frequencies
#
# HOW TO USE:
#   - Call create_rotated_uca() which applies this rotation automatically
#   - MUSIC will output correct angles directly - no post-hoc correction needed
#
# PERFORMANCE:
#   - Overall RMSE: ~50-100° depending on frequency (limited by 4-element array)
#   - Per-frequency biases of -6° to +18° remain due to hardware calibration
#     (cable phase shifts, antenna coupling effects vary with frequency)
#
ARRAY_ROTATION = 155.0

# Legacy alias for backwards compatibility
POSITION_OFFSET = ARRAY_ROTATION

# Element angles after reordering (used for steering vector calculation)
# Array elem 0 <- ch2 (135°), elem 1 <- ch3 (225°), elem 2 <- ch0 (315°), elem 3 <- ch1 (45°)
ELEMENT_ANGLES_BASE = np.array([135, 225, 315, 45])  # After channel reordering
ELEMENT_ANGLES = ELEMENT_ANGLES_BASE + POSITION_OFFSET  # With offset applied

# =============================================================================
# FREQUENCY CONFIGURATIONS
# =============================================================================
FREQUENCIES = {
    "1200MHz": {
        "freq_hz": 1.2e9,
        "wavelength_m": C / 1.2e9,
        "data_dir": "1200MHz, 0dB, 1deg increments, outside",
    },
    "5700MHz": {
        "freq_hz": 5.7e9,
        "wavelength_m": C / 5.7e9,
        "data_dir": None,  # TODO: Add when available
    },
}

# Default frequency
DEFAULT_FREQ = "1200MHz"

# =============================================================================
# DERIVED QUANTITIES (for default frequency)
# =============================================================================
FREQ = FREQUENCIES[DEFAULT_FREQ]["freq_hz"]
WAVELENGTH = FREQUENCIES[DEFAULT_FREQ]["wavelength_m"]
DATA_DIR = FREQUENCIES[DEFAULT_FREQ]["data_dir"]

# Array element positions in Cartesian coordinates (meters)
# Convert North-CW to math convention (East-CCW) for calculations
_angles_math = 90 - ELEMENT_ANGLES  # North-CW to East-CCW
POSITIONS = RADIUS * np.column_stack([
    np.cos(np.radians(_angles_math)),
    np.sin(np.radians(_angles_math))
])

# =============================================================================
# PERFORMANCE NOTES
# =============================================================================
"""
Expected performance by angular region (raw MUSIC, no calibration):

| Region          | True Angles  | Expected Error |
|-----------------|--------------|----------------|
| Front           | 0°, 330-360° | < 20°          |
| Right side      | 45-75°       | < 20°          |
| Right-back      | 90-165°      | 30-60°         |
| Back (cables)   | 180-210°     | 10-20°         |
| Left-back       | 225-240°     | > 90° (worst)  |
| Left side       | 255-315°     | 20-50°         |

Limitations:
- 4-element UCA has limited angular resolution
- Array radius (30mm) is small compared to wavelength (250mm at 1.2GHz)
- Cables on back side (180°) cause reflections and distortion
- Non-monotonic raw estimates make simple calibration ineffective
"""

# =============================================================================
# HELPER FUNCTIONS
# =============================================================================

def create_rotated_uca(rotation_deg=ARRAY_ROTATION, radius=RADIUS, num_elements=NUM_ELEMENTS):
    """Create a UCA rotated to match our physical array orientation.

    Use this instead of UniformCircularArray for correct DOA estimation.
    MUSIC will output correct angles directly, no post-hoc correction needed.

    Args:
        rotation_deg: Rotation angle in degrees (default: ARRAY_ROTATION)
        radius: Array radius in meters (default: RADIUS)
        num_elements: Number of elements (default: NUM_ELEMENTS)

    Returns:
        Array object with correctly positioned elements

    Example:
        from constants import create_rotated_uca, FREQ
        from doa_py.algorithm.music_based import music

        uca = create_rotated_uca()
        spectrum = music(received_data=snapshots, num_signal=1,
                        array=uca, signal_fre=FREQ, angle_grids=grids, unit='deg')
        estimated_angle = grids[np.argmax(spectrum)]  # Correct angle directly!
    """
    from doa_py.arrays import Array

    angles = 2 * np.pi * np.arange(num_elements) / num_elements + np.deg2rad(rotation_deg)
    element_position_x = radius * np.cos(angles)
    element_position_y = radius * np.sin(angles)
    element_position_z = np.zeros(num_elements)

    class RotatedUCA(Array):
        def __init__(self):
            super().__init__(element_position_x, element_position_y, element_position_z)

    return RotatedUCA()


def get_config(freq_key=DEFAULT_FREQ):
    """Get configuration for a specific frequency.

    Args:
        freq_key: Key from FREQUENCIES dict (e.g., "1200MHz", "5700MHz")

    Returns:
        dict with freq_hz, wavelength_m, data_dir, positions
    """
    config = FREQUENCIES[freq_key].copy()
    config["positions"] = POSITIONS  # Same geometry for all frequencies
    config["channel_order"] = CHANNEL_ORDER
    config["element_angles"] = ELEMENT_ANGLES
    return config


# =============================================================================
# QUICK REFERENCE
# =============================================================================
if __name__ == "__main__":
    print("=" * 60)
    print("DOA ANTENNA CONFIGURATION")
    print("=" * 60)
    print(f"\nArray radius: {RADIUS * 1000:.1f} mm")
    print(f"Number of elements: {NUM_ELEMENTS}")
    print(f"Channel order: {CHANNEL_ORDER}")
    print(f"Array rotation: {ARRAY_ROTATION}°")
    print(f"\nDefault frequency: {FREQ/1e9:.1f} GHz")
    print(f"Wavelength: {WAVELENGTH * 1000:.1f} mm")
    print(f"Data directory: {DATA_DIR}")
    print(f"\nArray size relative to wavelength: {RADIUS/WAVELENGTH:.2f} λ")
    print()
    print("Usage:")
    print("  from constants import create_rotated_uca, CHANNEL_ORDER, FREQ")
    print("  uca = create_rotated_uca()  # Correctly oriented array")
    print("  # Load data with CHANNEL_ORDER, run MUSIC with uca")
    print("  # Result is correct angle - no post-hoc correction needed!")
    print("=" * 60)
