import pytest
import vita
from vita.constants import ValueScale


def test_normalized_linear_parameter():
    """Test normalized control on a linear parameter"""
    synth = vita.Synth()
    
    # Get a linear parameter (most parameters are linear by default)
    info = synth.get_control_details("filter_1_cutoff")
    if info.scale != ValueScale.Linear:
        pytest.skip("filter_1_cutoff is not linear in this version")
    
    # Test boundary values
    controls = synth.get_controls()
    controls["filter_1_cutoff"].set_normalized(0.0)
    assert abs(controls["filter_1_cutoff"].get_normalized() - 0.0) < 0.001
    assert abs(controls["filter_1_cutoff"].value() - info.min) < 0.001
    
    controls["filter_1_cutoff"].set_normalized(1.0)
    assert abs(controls["filter_1_cutoff"].get_normalized() - 1.0) < 0.001
    assert abs(controls["filter_1_cutoff"].value() - info.max) < 0.001
    
    # Test middle value
    controls["filter_1_cutoff"].set_normalized(0.5)
    assert abs(controls["filter_1_cutoff"].get_normalized() - 0.5) < 0.001
    expected_mid = info.min + 0.5 * (info.max - info.min)
    assert abs(controls["filter_1_cutoff"].value() - expected_mid) < 0.001


def test_normalized_quartic_parameter():
    """Test normalized control on a quartic parameter like env_1_delay"""
    synth = vita.Synth()
    
    # Verify env_1_delay uses quartic scaling
    info = synth.get_control_details("env_1_delay")
    assert info.scale == ValueScale.Quartic
    
    # Test boundary values
    controls = synth.get_controls()
    controls["env_1_delay"].set_normalized(0.0)
    assert abs(controls["env_1_delay"].get_normalized() - 0.0) < 0.001
    
    controls["env_1_delay"].set_normalized(1.0)
    assert abs(controls["env_1_delay"].get_normalized() - 1.0) < 0.001
    
    # Test that normalized 0.5 maps to 0.25 seconds display (0.5^4 * 4.0)
    # For quartic parameters in VST, normalized maps linearly to internal range
    # but the knob DISPLAY position has a quartic relationship
    controls["env_1_delay"].set_normalized(0.5)
    expected_value = info.min + 0.5 * (info.max - info.min)  # Linear mapping
    assert abs(controls["env_1_delay"].value() - expected_value) < 0.001
    assert abs(controls["env_1_delay"].get_normalized() - 0.5) < 0.001
    
    actual_value = controls["env_1_delay"].value()
    linear_mid = info.min + 0.5 * (info.max - info.min)
    
    # With this implementation, normalized maps linearly to internal range
    assert abs(actual_value - linear_mid) < 0.001


def test_normalized_indexed_parameter():
    """Test normalized control on an indexed/discrete parameter"""
    synth = vita.Synth()
    
    # delay_style is an indexed parameter
    info = synth.get_control_details("delay_style")
    assert info.scale == ValueScale.Indexed
    assert info.is_discrete
    
    num_options = len(info.options)
    
    # Test each discrete value
    controls = synth.get_controls()
    for i in range(num_options):
        normalized = i / (num_options - 1) if num_options > 1 else 0
        controls["delay_style"].set_normalized(normalized)
        
        # Check it maps to the correct index
        actual_index = int(controls["delay_style"].value() - info.min)
        assert actual_index == i
        
        # Check text matches
        assert controls["delay_style"].get_text() == info.options[i]


def test_normalized_vst_style_automation():
    """Demonstrate VST-style parameter automation using normalized values"""
    synth = vita.Synth()
    
    # Simulate a VST automation curve for filter cutoff
    # This would be how a DAW sends parameter changes
    automation_points = [
        (0.0, 0.0),    # Start closed
        (0.25, 0.5),   # Open to middle
        (0.5, 1.0),    # Full open
        (0.75, 0.3),   # Partially close
        (1.0, 0.0),    # Close again
    ]
    
    controls = synth.get_controls()
    for time, normalized_value in automation_points:
        controls["filter_1_cutoff"].set_normalized(normalized_value)
        
        # Verify the normalized value is preserved
        retrieved = controls["filter_1_cutoff"].get_normalized()
        assert abs(retrieved - normalized_value) < 0.001
        
        # In a real scenario, you would render audio here
        # audio = synth.render(60, 0.8, 0.1, 0.1)


def test_normalized_clipping():
    """Test that normalized values are properly clipped to 0-1 range"""
    synth = vita.Synth()
    
    # Test values outside 0-1 range
    controls = synth.get_controls()
    controls["filter_1_cutoff"].set_normalized(-0.5)
    assert controls["filter_1_cutoff"].get_normalized() == 0.0
    
    controls["filter_1_cutoff"].set_normalized(1.5)
    assert controls["filter_1_cutoff"].get_normalized() == 1.0


if __name__ == "__main__":
    # Run basic tests
    test_normalized_linear_parameter()
    print("✓ Linear parameter test passed")
    
    test_normalized_quartic_parameter()
    print("✓ Quartic parameter test passed")
    
    test_normalized_indexed_parameter()
    print("✓ Indexed parameter test passed")
    
    test_normalized_vst_style_automation()
    print("✓ VST-style automation test passed")
    
    test_normalized_clipping()
    print("✓ Clipping test passed")
    
    print("\nAll normalized parameter tests passed!")
