using HidSliders.Hid;
using Voicemeeter;
using VoiceMeeter;
using Timer = System.Windows.Forms.Timer;

namespace HidSliders.Gui
{
    public partial class Form1 : Form
    {
        private IDisposable? _vmClient;
        private bool _mixerConnected;
        private List<MixerButton> _previousButtons = [];
        private readonly HidInterface _hidInterface;
        private const int ZeroPercent = 90;
        private readonly List<ChannelControlCollection> _channels;
        private bool _suppressCheckboxUpdate = false;
        private Timer _syncTimer = new();

        public Form1()
        {
            InitializeComponent();
            _channels = new List<ChannelControlCollection>
            {
                new (numericUpDown1, trackBar1, ch1A1Checkbox, ch1A2Checkbox, ch1A3Checkbox, ch1A4Checkbox, ch1A5Checkbox, ch1B1Checkbox, ch1B2Checkbox, ch1B3Checkbox, checkBox1, checkBox2, slider1Label),
                new (numericUpDown2, trackBar2, checkBox12, checkBox11, checkBox10, checkBox9, checkBox8, checkBox7, checkBox6, checkBox5, checkBox4, checkBox3, slider2Label),
                new (numericUpDown3, trackBar3, checkBox22, checkBox21, checkBox20, checkBox19, checkBox18, checkBox17, checkBox16, checkBox15, checkBox14, checkBox13, slider3Label),
                new (numericUpDown4, trackBar4, checkBox32, checkBox31, checkBox30, checkBox29, checkBox28, checkBox27, checkBox26, checkBox25, checkBox24, checkBox23, slider4Label),
                new (numericUpDown5, trackBar5, checkBox42, checkBox41, checkBox40, checkBox39, checkBox38, checkBox37, checkBox36, checkBox35, checkBox34, checkBox33, slider5Label),

            };

            switch (Properties.Settings.Default.VMVersion)
            {
                case 1:
                    vmRadio.Checked = true;
                    break;
                case 2:
                    vmBananaRadio.Checked = true;
                    break;
                case 3:
                    vmPotatoRadio.Checked = true;
                    break;
            }

            _channels[0].ChannelSelector.Value = Properties.Settings.Default.Channel1Strip;
            _channels[1].ChannelSelector.Value = Properties.Settings.Default.Channel2Strip;
            _channels[2].ChannelSelector.Value = Properties.Settings.Default.Channel3Strip;
            _channels[3].ChannelSelector.Value = Properties.Settings.Default.Channel4Strip;
            _channels[4].ChannelSelector.Value = Properties.Settings.Default.Channel5Strip;

            _syncTimer.Tick += SyncVM;
            _syncTimer.Interval = 100;
            _syncTimer.Enabled = true;
            if (Properties.Settings.Default.AutoConnect)
            {
                autoConnectCheckBox.Checked = true;
                Task.Run(ConnectVoicemeeter).Wait();
            }

            _hidInterface = new HidInterface();
            mixerLabel.Text = "Mixer not connected";

            if (Properties.Settings.Default.MixerAutoConnect)
            {
                ConnectHidMixer();
                mixerAutoConnectCheckbox.Checked = true;
            }
        }

        private void OnSliderValues(int[] values)
        {
            this.BeginInvoke(() =>
            {
                for (int i = 0; i < 5; i++)
                {
                    var gain = HidToGain(values[i]);
                    SetStripGain(_channels[i].CurrentStrip, gain);
                    _channels[i].TrackBar.Value = (int)gain;
                    _channels[i].IndicatorLabel.Text = $"{gain} dB";
                }
            });
        }

        private void HandleButtonPress(MixerButton button)
        {
            var buttonGroup = MixerButtonHelper.GetButtonGroup(button);
            var channelButton = MixerButtonHelper.GetChannelButton(button, buttonGroup);

            ChannelControlCollection channel;
            switch (buttonGroup)
            {
                case ButtonGroups.Channel1:
                    channel = _channels[0];
                    break;
                case ButtonGroups.Channel2:
                    channel = _channels[1];
                    break;
                case ButtonGroups.Channel3:
                    channel = _channels[2];
                    break;
                case ButtonGroups.Channel4:
                    channel = _channels[3];
                    break;
                case ButtonGroups.Channel5:
                    channel = _channels[4];
                    break;
                default:
                    throw new Exception();
            }

            switch (channelButton)
            {
                case ChannelButton.ChannelA1:
                    channel.BusA1Checkbox.Checked = !channel.BusA1Checkbox.Checked;
                    break;
                case ChannelButton.ChannelA2:
                    channel.BusA2Checkbox.Checked = !channel.BusA2Checkbox.Checked;
                    break;
                case ChannelButton.ChannelA3:
                    channel.BusA3Checkbox.Checked = !channel.BusA3Checkbox.Checked;
                    break;
                case ChannelButton.ChannelA4:
                    channel.BusA4Checkbox.Checked = !channel.BusA4Checkbox.Checked;
                    break;
                case ChannelButton.ChannelA5:
                    channel.BusA5Checkbox.Checked = !channel.BusA5Checkbox.Checked;
                    break;
                case ChannelButton.ChannelB1:
                    channel.BusB1Checkbox.Checked = !channel.BusB1Checkbox.Checked;
                    break;
                case ChannelButton.ChannelB2:
                    channel.BusB2Checkbox.Checked = !channel.BusB2Checkbox.Checked;
                    break;
                case ChannelButton.ChannelB3:
                    channel.BusB3Checkbox.Checked = !channel.BusB3Checkbox.Checked;
                    break;
                case ChannelButton.ChannelMute:
                    channel.MuteCheckBox.Checked = !channel.MuteCheckBox.Checked;
                    break;
                default:
                    throw new Exception();
            }
            listBox1.Items.Add($"{buttonGroup} {channelButton} pressed");
        }

        private void OnButtons(List<MixerButton> buttons)
        {
            BeginInvoke(() =>
            {
                var newButtons = buttons.Except(_previousButtons).ToList();
                _previousButtons = buttons;

                newButtons.ForEach(HandleButtonPress);
            });
        }

        private void OnSliderComplete()
        {
            BeginInvoke(() =>
            {
                _mixerConnected = false;
                button1.Enabled = true;
                mixerLabel.Text = "Mixer disconnected";
            });
        }

        private void ConnectHidMixer()
        {
            _mixerConnected = _hidInterface.ConnectDevice(0x0359, 0x6497);
            if (_mixerConnected)
            {
                mixerLabel.Text = "Mixer connected";
                button1.Enabled = false;
                _hidInterface.SliderObservable.Subscribe(OnSliderValues, OnSliderComplete);
                _hidInterface.ButtonsObservable.Subscribe(OnButtons);
            }
        }

        private async Task ConnectVoicemeeter()
        {
            RunVoicemeeterParam version = RunVoicemeeterParam.None;
            if (vmRadio.Checked)
            {
                version = RunVoicemeeterParam.Voicemeeter;
            }
            else if (vmBananaRadio.Checked)
            {
                version = RunVoicemeeterParam.VoicemeeterBanana;
            }
            else if (vmPotatoRadio.Checked)
            {
                version = RunVoicemeeterParam.VoicemeeterPotato;
            }

            Properties.Settings.Default.VMVersion = (int)version;
            _vmClient = await Remote.Initialize(version);
            if (_vmClient != null)
            {
                EnableVMControls(version);
                voicemeeterStatusLabel.Text = "Connected";
                _syncTimer.Start();
            }
        }

        private async void connectButton_Click(object sender, EventArgs e)
        {
            await ConnectVoicemeeter();
        }

        private void SyncVM(object? sender, EventArgs eventArgs)
        {
            if (_vmClient == null || Remote.IsParametersDirty() != 1) return;

            foreach (var channelControlCollection in _channels)
            {
                SyncChannelControls(channelControlCollection);
            }
        }

        private void EnableVMControls(RunVoicemeeterParam version)
        {
            foreach (var channel in _channels)
            {
                channel.TrackBar.Enabled = true;
                channel.SoloCheckBox.Enabled = true;
                channel.MuteCheckBox.Enabled = true;
                channel.EnableBusses(version);
                SyncChannelControls(channel);
            }
        }

        private void SyncChannelControls(ChannelControlCollection channel)
        {
            _suppressCheckboxUpdate = true;
            channel.BusA1Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A1);
            channel.BusB1Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B1);
            if (vmBananaRadio.Checked || vmPotatoRadio.Checked)
            {
                channel.BusA2Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A2);
                channel.BusA3Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A3);
                channel.BusB2Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B2);
            }

            if (vmPotatoRadio.Checked)
            {
                channel.BusA4Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A4);
                channel.BusA5Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.A5);
                channel.BusB3Checkbox.Checked = GetBusEnabled(channel.CurrentStrip, VMBus.B3);
            }

            channel.TrackBar.Value = (int)GetStripGain(channel.CurrentStrip);
            channel.SoloCheckBox.Checked = GetChannelSolo(channel.CurrentStrip);
            channel.MuteCheckBox.Checked = GetChannelMute(channel.CurrentStrip);
            _suppressCheckboxUpdate = false;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _vmClient?.Dispose();
            Properties.Settings.Default.Save();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ConnectHidMixer();
        }

        private float HidToGain(int hidValue)
        {
            // adjusted regression of yamaha board is approx y = 5.8456 + 17.0486 * ln(x)
            // Truncate to first decimal (move decimal, int truncate, move decimal)
            //var realResult = 5.8456f + 17.0486f * (float)Math.Log(Math.Max(hidValue / Resolution, 0.02));
            //return ((int)(realResult * 10)) / 10.0f;
            return (int)(CalculateGain(hidValue, ZeroPercent) * 10) / 10.0f;
        }

        public static float CalculateGain(float slider, int zeroPct)
        {
            float minSlider;
            float maxSlider;
            float minGain;
            float maxGain;

            if (slider >= zeroPct)
            {
                minSlider = zeroPct;
                maxSlider = 100;
                minGain = 0;
                maxGain = 12;
            }
            else
            {
                minSlider = 0;
                maxSlider = zeroPct;
                minGain = -60;
                maxGain = 0;
            }

            // Calculate the percentage of slider position within the range
            var percentage = (slider - minSlider) / (maxSlider - minSlider);

            // Interpolate gain within the range based on the percentage
            var calculatedGain = minGain + (percentage * (maxGain - minGain));

            return calculatedGain;
        }

        private void trackBar_Scroll(object sender, EventArgs e)
        {
            if (_suppressCheckboxUpdate) return;
            if (sender is TrackBar trackBar && _vmClient != null)
            {
                var channel = _channels.First(c => c.OwnsControl(trackBar));
                SetStripGain(channel.CurrentStrip, trackBar.Value);
            }
        }

        private void busCheckbox_CheckedChanged(object sender, EventArgs e)
        {
            if (_suppressCheckboxUpdate) return;
            if (sender is CheckBox { Tag: VMBus bus } checkBox && _vmClient != null)
            {
                var channel = _channels.First(c => c.OwnsControl(checkBox));
                SetBusEnabled(channel.CurrentStrip, bus, checkBox.Checked);
            }
        }

        private void SetStripGain(int strip, float value)
        {
            Remote.SetParameter($"Strip[{strip}].Gain", value);
        }

        private float GetStripGain(int index)
        {
            return Remote.GetParameter($"Strip[{index}].Gain");
        }

        private void vmRadio_CheckedChanged(object sender, EventArgs e)
        {
            if (vmRadio.Checked)
            {
                setMaxChannel(2);
            }
        }

        private void vmBananaRadio_CheckedChanged(object sender, EventArgs e)
        {
            if (vmBananaRadio.Checked)
            {
                setMaxChannel(4);
            }
        }

        private void setMaxChannel(int max)
        {
            numericUpDown1.Maximum = max;
            numericUpDown2.Maximum = max;
            numericUpDown3.Maximum = max;
            numericUpDown4.Maximum = max;
            numericUpDown5.Maximum = max;
        }

        private void vmPotatoRadio_CheckedChanged(object sender, EventArgs e)
        {
            if (vmPotatoRadio.Checked)
            {
                setMaxChannel(7);
            }
        }

        private void SetBusEnabled(int strip, VMBus bus, bool enabled)
        {
            Remote.SetParameter($"Strip[{strip}].{bus}", enabled ? 1 : 0);
        }

        private bool GetBusEnabled(int strip, VMBus bus)
        {
            return (int)Remote.GetParameter($"Strip[{strip}].{bus}") == 1;
        }

        private void SetChannelSolo(int strip, bool enabled)
        {
            Remote.SetParameter($"Strip[{strip}].Solo", enabled ? 1 : 0);
        }

        private bool GetChannelSolo(int strip)
        {
            return (int)Remote.GetParameter($"Strip[{strip}].Solo") == 1;
        }

        private void SetChannelMute(int strip, bool enabled)
        {
            Remote.SetParameter($"Strip[{strip}].Mute", enabled ? 1 : 0);
        }

        private bool GetChannelMute(int strip)
        {
            return (int)Remote.GetParameter($"Strip[{strip}].Mute") == 1;
        }

        private void numericUpDown_ValueChanged(object sender, EventArgs e)
        {
            if (sender is NumericUpDown numericUpDown)
            {
                var channel = _channels.First(c => c.OwnsControl(numericUpDown));
                if (_vmClient != null)
                {
                    SyncChannelControls(channel);
                }
                var index = _channels.FindIndex(c => c == channel);
                var settingsType = Properties.Settings.Default.GetType();
                settingsType.GetProperty($"Channel{index + 1}Strip").SetValue(Properties.Settings.Default, (int)numericUpDown.Value);
            }
        }

        private void soloCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (_suppressCheckboxUpdate) return;
            if (sender is CheckBox checkBox && _vmClient != null)
            {
                var channel = _channels.First(c => c.OwnsControl(checkBox));
                SetChannelSolo(channel.CurrentStrip, checkBox.Checked);
            }
        }

        private void muteCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            if (_suppressCheckboxUpdate) return;
            if (sender is CheckBox checkBox && _vmClient != null)
            {
                var channel = _channels.First(c => c.OwnsControl(checkBox));
                SetChannelMute(channel.CurrentStrip, checkBox.Checked);
            }
        }

        private void autoConnectCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.AutoConnect = autoConnectCheckBox.Checked;
        }

        private void checkBox43_CheckedChanged(object sender, EventArgs e)
        {
            Properties.Settings.Default.MixerAutoConnect = mixerAutoConnectCheckbox.Checked;
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            if (FormWindowState.Minimized == this.WindowState)
            {
                notifyIcon1.Visible = true;
                this.Hide();
            }
            else if (FormWindowState.Normal == this.WindowState)
            {
                notifyIcon1.Visible = false;
            }
        }

        private void notifyIcon1_Click(object sender, EventArgs e)
        {
            this.Show();
            WindowState = FormWindowState.Normal;
        }
    }
}
