"""
T234: Max for Live ML Devices
Integración con dispositivos M4L de osciladores ML paramétricos
"""

import json
import os
from typing import Dict, List, Any, Optional
from dataclasses import dataclass
from enum import Enum


class M4LDeviceType(Enum):
    """Tipos de dispositivos M4L."""
    OSCILLATOR = "oscillator"
    FILTER = "filter"
    ENVELOPE = "envelope"
    LFO = "lfo"
    SEQUENCER = "sequencer"
    EFFECT = "effect"
    UTILITY = "utility"


@dataclass
class M4LDevice:
    """Configuración de dispositivo M4L."""
    name: str
    device_type: M4LDeviceType
    parameters: Dict[str, Any]
    ml_enabled: bool = False
    ml_model: Optional[str] = None


class M4LMLIntegration:
    """
    Integración con Max for Live devices ML.
    
    T234: Soporte para osciladores ML paramétricos y dispositivos M4L.
    """
    
    # Dispositivos M4L conocidos con ML
    KNOWN_DEVICES = {
        'ML_Oscillator': {
            'type': M4LDeviceType.OSCILLATOR,
            'ml_capable': True,
            'default_model': 'wavetable_synth',
            'parameters': ['waveform', 'frequency', 'amplitude', 'ml_complexity', 'ml_variation']
        },
        'ML_Filter': {
            'type': M4LDeviceType.FILTER,
            'ml_capable': True,
            'default_model': 'neural_filter',
            'parameters': ['cutoff', 'resonance', 'type', 'ml_drive', 'ml_character']
        },
        'ML_Sequencer': {
            'type': M4LDeviceType.SEQUENCER,
            'ml_capable': True,
            'default_model': 'generative_rhythm',
            'parameters': ['steps', 'density', 'variation', 'ml_pattern_length', 'ml_evolution']
        },
        'ML_DrumSynth': {
            'type': M4LDeviceType.OSCILLATOR,
            'ml_capable': True,
            'default_model': 'percussion_synth',
            'parameters': ['pitch', 'decay', 'tone', 'ml_body', 'ml_noise']
        },
        # Dispositivos clásicos sin ML
        'LFO': {
            'type': M4LDeviceType.LFO,
            'ml_capable': False,
            'parameters': ['rate', 'shape', 'depth', 'offset']
        },
        'Envelope_Follower': {
            'type': M4LDeviceType.UTILITY,
            'ml_capable': False,
            'parameters': ['attack', 'release', 'gain']
        }
    }
    
    def __init__(self):
        self.devices_dir = self._get_m4l_devices_dir()
        self.active_devices: List[M4LDevice] = []
    
    def _get_m4l_devices_dir(self) -> str:
        """Obtiene directorio de dispositivos M4L."""
        # Directorio típico de M4L en Ableton
        return os.path.expanduser('~/Documents/Ableton/User Library/Presets/MIDI Effects/Max MIDI Effect')
    
    def get_m4l_device_config(self, device_name: str, 
                              enable_ml: bool = True) -> Optional[Dict[str, Any]]:
        """
        Obtiene configuración de dispositivo M4L.
        
        Args:
            device_name: Nombre del dispositivo
            enable_ml: Habilitar features ML
            
        Returns:
            Configuración del dispositivo
        """
        device_info = self.KNOWN_DEVICES.get(device_name)
        
        if not device_info:
            return None
        
        config = {
            'name': device_name,
            'type': device_info['type'].value,
            'path': f'{self.devices_dir}/{device_name}.amxd',
            'parameters': {},
            'ml': {
                'enabled': enable_ml and device_info.get('ml_capable', False),
                'model': device_info.get('default_model') if enable_ml else None,
                'features': []
            }
        }
        
        # Configurar parámetros por defecto
        for param in device_info['parameters']:
            config['parameters'][param] = self._get_default_param_value(param)
        
        # Features ML específicas
        if config['ml']['enabled']:
            config['ml']['features'] = self._get_ml_features(device_info['type'])
        
        return config
    
    def _get_default_param_value(self, param: str) -> Any:
        """Obtiene valor por defecto de parámetro."""
        defaults = {
            'waveform': 'sine',
            'frequency': 440.0,
            'amplitude': 0.8,
            'cutoff': 1000.0,
            'resonance': 0.5,
            'rate': 1.0,
            'shape': 'sine',
            'depth': 0.5,
            'attack': 0.01,
            'release': 0.5,
            'gain': 1.0,
            'steps': 16,
            'density': 0.5,
            'variation': 0.3,
            'ml_complexity': 0.5,
            'ml_variation': 0.3,
            'ml_drive': 0.4,
            'ml_character': 0.5,
            'ml_pattern_length': 16,
            'ml_evolution': 0.2,
            'ml_body': 0.6,
            'ml_noise': 0.3
        }
        return defaults.get(param, 0.5)
    
    def _get_ml_features(self, device_type: M4LDeviceType) -> List[str]:
        """Obtiene features ML según tipo."""
        features = {
            M4LDeviceType.OSCILLATOR: ['generative_waveform', 'parameter_morphing', 'timbre_evolution'],
            M4LDeviceType.FILTER: ['adaptive_resonance', 'neural_character', 'dynamic_response'],
            M4LDeviceType.SEQUENCER: ['pattern_generation', 'variation_algorithms', 'fill_generation'],
            M4LDeviceType.ENVELOPE: ['intelligent_attack', 'contextual_release'],
        }
        return features.get(device_type, [])
    
    def create_ml_layer(self, track_index: int, 
                       layer_type: str,
                       genre: str) -> Dict[str, Any]:
        """
        Crea capa con dispositivos ML.
        
        Args:
            track_index: Índice del track
            layer_type: Tipo de capa
            genre: Género musical
            
        Returns:
            Configuración de capa ML
        """
        devices = []
        
        if layer_type == 'bass':
            devices = [
                self.get_m4l_device_config('ML_Oscillator', enable_ml=True),
                self.get_m4l_device_config('ML_Filter', enable_ml=True),
                self.get_m4l_device_config('LFO', enable_ml=False)
            ]
        elif layer_type == 'drums':
            devices = [
                self.get_m4l_device_config('ML_Sequencer', enable_ml=True),
                self.get_m4l_device_config('ML_DrumSynth', enable_ml=True)
            ]
        elif layer_type == 'music':
            devices = [
                self.get_m4l_device_config('ML_Oscillator', enable_ml=True),
                self.get_m4l_device_config('ML_Filter', enable_ml=False)
            ]
        
        # Configurar según género
        self._configure_for_genre(devices, genre)
        
        return {
            'track_index': track_index,
            'layer_type': layer_type,
            'genre': genre,
            'devices': [d for d in devices if d],
            'ml_parameters': self._extract_ml_parameters(devices),
            'automation_targets': self._get_automation_targets(devices)
        }
    
    def _configure_for_genre(self, devices: List[Dict], genre: str):
        """Configura dispositivos según género."""
        genre_configs = {
            'techno': {
                'ml_complexity': 0.7,
                'ml_drive': 0.6,
                'waveform': 'saw'
            },
            'house': {
                'ml_complexity': 0.5,
                'ml_drive': 0.4,
                'waveform': 'sine'
            },
            'trance': {
                'ml_complexity': 0.6,
                'ml_drive': 0.5,
                'waveform': 'supersaw'
            }
        }
        
        config = genre_configs.get(genre, genre_configs['techno'])
        
        for device in devices:
            if device:
                for param, value in config.items():
                    if param in device['parameters']:
                        device['parameters'][param] = value
    
    def _extract_ml_parameters(self, devices: List[Dict]) -> Dict[str, Any]:
        """Extrae parámetros ML de dispositivos."""
        ml_params = {}
        
        for device in devices:
            if device and device.get('ml', {}).get('enabled'):
                device_name = device['name']
                ml_params[device_name] = {
                    k: v for k, v in device['parameters'].items()
                    if k.startswith('ml_')
                }
        
        return ml_params
    
    def _get_automation_targets(self, devices: List[Dict]) -> List[Dict]:
        """Obtiene targets para automatización."""
        targets = []
        
        for i, device in enumerate(devices):
            if device:
                for param_name in device['parameters']:
                    if 'ml_' in param_name or param_name in ['cutoff', 'resonance', 'rate']:
                        targets.append({
                            'device_index': i,
                            'device_name': device['name'],
                            'parameter': param_name,
                            'track_index': 0  # Se asigna después
                        })
        
        return targets
    
    def export_m4l_preset(self, config: Dict, filepath: str) -> Dict[str, Any]:
        """Exporta preset M4L."""
        preset = {
            'ableton_version': '12.0',
            'type': 'max_for_live',
            'config': config,
            'exported_at': datetime.now().isoformat(),
            'devices': []
        }
        
        for device in config.get('devices', []):
            if device:
                preset['devices'].append({
                    'name': device['name'],
                    'path': device['path'],
                    'parameters': device['parameters']
                })
        
        with open(filepath, 'w') as f:
            json.dump(preset, f, indent=2)
        
        return {
            'success': True,
            'filepath': filepath,
            'devices_count': len(preset['devices'])
        }
    
    def get_ml_capabilities(self) -> Dict[str, Any]:
        """Obtiene capacidades ML disponibles."""
        ml_devices = [
            name for name, info in self.KNOWN_DEVICES.items()
            if info.get('ml_capable', False)
        ]
        
        return {
            'ml_devices_available': ml_devices,
            'ml_features': {
                'generative_audio': True,
                'parameter_morphing': True,
                'adaptive_processing': True,
                'neural_models': ['wavetable_synth', 'neural_filter', 'generative_rhythm', 'percussion_synth']
            },
            'integration_level': 'native',
            'requires_max_for_live': True
        }


def configure_m4l_ml_layer(track_index: int, 
                           layer_type: str,
                           genre: str = 'techno') -> Dict[str, Any]:
    """
    T234: Configura capa con dispositivos M4L ML.
    
    Args:
        track_index: Índice del track
        layer_type: Tipo de capa
        genre: Género musical
        
    Returns:
        Configuración de capa M4L ML
    """
    integration = M4LMLIntegration()
    return integration.create_ml_layer(track_index, layer_type, genre)


def get_m4l_capabilities() -> Dict[str, Any]:
    """Obtiene capacidades M4L ML."""
    integration = M4LMLIntegration()
    return integration.get_ml_capabilities()


if __name__ == '__main__':
    # Test de integración M4L
    print("M4L ML Capabilities:")
    caps = get_m4l_capabilities()
    print(json.dumps(caps, indent=2))
    
    print("\n=== ML Bass Layer ===")
    bass_layer = configure_m4l_ml_layer(0, 'bass', 'techno')
    print(json.dumps(bass_layer, indent=2))
