using System;
using System.Windows;
using System.Windows.Controls;
using DansoriEQ.App.Audio;

namespace DansoriEQ.App.Controls;

public partial class VolumePanel : UserControl
{
    private readonly SystemVolumeService _svc = new();
    private bool _updating;
    private bool _softMode;   // true = device has no hardware volume → use APO digital attenuation

    /// <summary>Raised with a dB attenuation (≤ 0) when software (APO) volume should be used.
    /// Raised with 0 when the device has hardware volume (no APO attenuation needed).</summary>
    public event Action<double>? SoftVolumeChanged;

    public VolumePanel()
    {
        InitializeComponent();
        Loaded   += OnLoaded;
        Unloaded += (_, __) => _svc.Dispose();
    }

    // Map slider (0..100 %) to a digital attenuation in dB. 100 % = 0 dB, taper to silence.
    private static double PercentToDb(double pct)
    {
        double s = Math.Clamp(pct / 100.0, 0, 1);
        if (s <= 0.005) return -100;              // effectively muted
        return Math.Clamp(20.0 * Math.Log10(s), -60, 0);
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        _svc.VolumeChanged += OnVolumeChanged;

        try
        {
            var devices = _svc.GetOutputDevices();
            DeviceCombo.ItemsSource = devices;

            // Find default device and select without triggering Device_Changed yet
            AudioDevice? def = null;
            foreach (var d in devices) if (d.IsDefault) { def = d; break; }
            var target = def ?? (devices.Count > 0 ? devices[0] : null);
            if (target == null) { VolLabel.Text = "장치 없음"; return; }

            // Suppress SelectionChanged while setting initial value
            _updating = true;
            DeviceCombo.SelectedItem = target;
            _updating = false;

            // Manually activate the selected device
            SelectDevice(target);
        }
        catch (Exception ex)
        {
            VolLabel.Text = "초기화 오류";
            ShowErr(ex.Message);
        }
    }

    private void SelectDevice(AudioDevice dev)
    {
        try
        {
            _svc.Select(dev.Id);

            // New device: clear any previous software attenuation and re-detect capability.
            _softMode = false;
            SoftVolumeChanged?.Invoke(0);
            HideErr();

            RefreshFromDevice();
            ProbeHardwareVolume();
        }
        catch (Exception ex)
        {
            VolLabel.Text = "오류";
            ShowErr($"장치 선택 실패: {ex.Message}");
        }
    }

    // Non-destructive-ish probe: write the current value back and see if it sticks.
    private void ProbeHardwareVolume()
    {
        try
        {
            float before = _svc.GetVolume();
            float test = before < 0.5f ? before + 0.05f : before - 0.05f;
            _svc.SetVolume(test);
            float after = _svc.GetVolume();
            _svc.SetVolume(before);   // restore

            _softMode = Math.Abs(after - test) > 0.06f;
            if (_softMode)
            {
                ModeLabel.Text = "디지털 볼륨 (APO)";
                // Drive initial software volume from the slider position.
                SoftVolumeChanged?.Invoke(PercentToDb(Vol.Value));
            }
            else
            {
                ModeLabel.Text = "하드웨어 볼륨";
            }
        }
        catch { _softMode = false; ModeLabel.Text = "하드웨어 볼륨"; }
    }

    private void Device_Changed(object sender, SelectionChangedEventArgs e)
    {
        if (_updating) return;
        if (DeviceCombo.SelectedItem is not AudioDevice dev) return;
        // NOTE: DefaultDeviceSwitcher.SetDefault uses unverified undocumented COM vtable —
        // do NOT call it here until verified. Volume control only.
        SelectDevice(dev);
    }

    private void RefreshFromDevice()
    {
        _updating = true;
        try
        {
            float vol = _svc.GetVolume();
            bool muted = _svc.GetMute();
            Vol.Value = vol * 100;
            MuteBtn.IsChecked = muted;
            VolLabel.Text = $"{vol * 100:0}%";
        }
        finally { _updating = false; }
    }

    private void Vol_Changed(object sender, RoutedPropertyChangedEventArgs<double> e)
    {
        if (_updating) return;
        try
        {
            float target = (float)(Vol.Value / 100.0);
            VolLabel.Text = $"{Vol.Value:0}%";

            if (_softMode)
            {
                // Device has no hardware volume → attenuate digitally through APO.
                if (MuteBtn.IsChecked != true)
                    SoftVolumeChanged?.Invoke(PercentToDb(Vol.Value));
            }
            else
            {
                _svc.SetVolume(target);
                // Late detection: if the hardware silently ignored us, switch to soft mode.
                float actual = _svc.GetVolume();
                if (Math.Abs(actual - target) > 0.06f)
                {
                    _softMode = true;
                    ModeLabel.Text = "디지털 볼륨 (APO)";
                    SoftVolumeChanged?.Invoke(PercentToDb(Vol.Value));
                }
            }
            HideErr();
        }
        catch (Exception ex) { ShowErr($"볼륨 설정 실패: {ex.Message}"); }
    }

    private void Mute_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            bool mute = MuteBtn.IsChecked == true;

            if (_softMode)
            {
                // Digital mute: full attenuation, or restore slider level.
                SoftVolumeChanged?.Invoke(mute ? -100 : PercentToDb(Vol.Value));
            }
            else
            {
                _svc.SetMute(mute);
                bool actual = _svc.GetMute();
                if (actual != mute)
                {
                    _softMode = true;
                    ModeLabel.Text = "디지털 볼륨 (APO)";
                    SoftVolumeChanged?.Invoke(mute ? -100 : PercentToDb(Vol.Value));
                }
            }
            HideErr();
        }
        catch (Exception ex) { ShowErr($"음소거 실패: {ex.Message}"); }
    }

    private void ShowErr(string msg)
    {
        ErrText.Text = msg;
        ErrText.Visibility = Visibility.Visible;
    }

    private void HideErr()
    {
        ErrText.Text = "";
        ErrText.Visibility = Visibility.Collapsed;
    }

    private void OnVolumeChanged(float scalar, bool muted)
        => Dispatcher.Invoke(() =>
        {
            _updating = true;
            Vol.Value = scalar * 100;
            MuteBtn.IsChecked = muted;
            VolLabel.Text = $"{scalar * 100:0}%";
            _updating = false;
        });
}
