using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using NAudio.Wave;

namespace WaveformDemo
{
    public class WaveformRenderer : IDisposable
    {
        private readonly Panel _panel;
        private readonly Color _backgroundColor;
        private readonly Color _waveformColorStart;
        private readonly Color _waveformColorEnd;
        private readonly Color _borderColor;

        private float[] _fullWaveformPoints = new float[0]; // Full normalized waveform data [-1..1]

        private readonly string _audioFilePath;
        private bool _dataReady = false;

        // Mouse hover cursor position (optional)
        private int? _hoverX = null;

        // Constants
        private const int MAX_WAVEFORM_LENGTH = 10000;

        public WaveformRenderer(string audioFilePath, Panel panel, Color backgroundColor, Color waveformColorStart, Color waveformColorEnd, Color borderColor)
        {
            if (string.IsNullOrWhiteSpace(audioFilePath))
                throw new ArgumentException("audioFilePath cannot be null or empty");

            _audioFilePath = audioFilePath;
            _panel = panel ?? throw new ArgumentNullException(nameof(panel));
            _backgroundColor = backgroundColor;
            _waveformColorStart = waveformColorStart;
            _waveformColorEnd = waveformColorEnd;
            _borderColor = borderColor;

            // Attach events
            _panel.Paint += Panel_Paint;
            _panel.Resize += Panel_Resize;

            if (_panel.Width > 5)
                LoadWaveform();
            else
                _panel.SizeChanged += Panel_SizeChanged;
        }

        private void Panel_SizeChanged(object sender, EventArgs e)
        {
            if (_panel.Width > 5)
            {
                _panel.SizeChanged -= Panel_SizeChanged;
                LoadWaveform();
                _panel.Invalidate();
            }
        }

        private void LoadWaveform()
        {
            try
            {
                using (var reader = new AudioFileReader(_audioFilePath))
                {
                    int channels = reader.WaveFormat.Channels;
                    long totalSamples = reader.Length / (reader.WaveFormat.BitsPerSample / 8) / channels;

                    int samplesToRead = (int)Math.Min(totalSamples, int.MaxValue);

                    int factor = (int)(samplesToRead / (float)MAX_WAVEFORM_LENGTH);
                    if (factor < 1) factor = 1;

                    float[] buffer = new float[factor * channels];

                    _fullWaveformPoints = new float[MAX_WAVEFORM_LENGTH];

                    int pointsFilled = 0;

                    reader.Position = 0;

                    while (pointsFilled < MAX_WAVEFORM_LENGTH)
                    {
                        int read = reader.Read(buffer, 0, buffer.Length);
                        if (read == 0) break;

                        int samplesRead = read / channels;

                        float maxSample = 0f;
                        for (int i = 0; i < samplesRead; i++)
                        {
                            for (int ch = 0; ch < channels; ch++)
                            {
                                float sample = buffer[i * channels + ch];
                                if (Math.Abs(sample) > Math.Abs(maxSample)) maxSample = sample;
                            }
                        }

                        _fullWaveformPoints[pointsFilled++] = maxSample;
                    }

                    for (int i = pointsFilled; i < MAX_WAVEFORM_LENGTH; i++)
                        _fullWaveformPoints[i] = 0f;

                    SmoothWaveform(_fullWaveformPoints);

                    _dataReady = false;
                    foreach (var v in _fullWaveformPoints)
                    {
                        if (Math.Abs(v) > 0.0001f)
                        {
                            _dataReady = true;
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error loading audio file waveform:\n" + ex.Message);
                _dataReady = false;
            }
        }

        private void SmoothWaveform(float[] waveform)
        {
            int windowSize = 3;
            float[] smoothed = new float[waveform.Length];

            for (int i = 0; i < waveform.Length; i++)
            {
                float sum = 0;
                int count = 0;
                for (int j = i - windowSize; j <= i + windowSize; j++)
                {
                    if (j >= 0 && j < waveform.Length)
                    {
                        sum += waveform[j];
                        count++;
                    }
                }
                smoothed[i] = sum / count;
            }

            Array.Copy(smoothed, waveform, waveform.Length);
        }

        private void Panel_Resize(object sender, EventArgs e)
        {
            _panel.Invalidate();
        }

        private void Panel_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            using (var bgBrush = new SolidBrush(_backgroundColor))
                g.FillRectangle(bgBrush, _panel.ClientRectangle);

            if (!_dataReady || _fullWaveformPoints.Length == 0)
            {
                using (var brush = new SolidBrush(Color.Gray))
                using (var sfmt = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
                {
                    g.DrawString("Waveform unavailable", SystemFonts.DefaultFont, brush, _panel.ClientRectangle, sfmt);
                }
                return;
            }

            int width = _panel.Width;
            int height = _panel.Height;
            int midY = height / 2;

            // We'll map all samples evenly across the panel width
            int samplesCount = _fullWaveformPoints.Length;
            float samplesPerPixel = samplesCount / (float)width;

            float maxAmplitude = 0f;
            foreach (var v in _fullWaveformPoints)
            {
                if (Math.Abs(v) > maxAmplitude)
                    maxAmplitude = Math.Abs(v);
            }
            if (maxAmplitude < 0.01f) maxAmplitude = 0.01f;

            float verticalScale = (midY * 0.9f) / maxAmplitude;

            using (var path = new GraphicsPath())
            {
                path.StartFigure();

                // start bottom-left corner
                path.AddLine(0, midY, 0, midY - _fullWaveformPoints[0] * verticalScale);

                // iterate across panel horizontally,
                // pick waveform points corresponding to each pixel with interpolation
                for (int x = 1; x < width; x++)
                {
                    // Calculate sample index (with simple interpolation)
                    float sampleIndexF = x * samplesPerPixel;
                    int sampleA = (int)Math.Floor(sampleIndexF);
                    int sampleB = sampleA + 1;
                    float frac = sampleIndexF - sampleA;

                    if (sampleB >= samplesCount) sampleB = samplesCount - 1;
                    if (sampleA >= samplesCount) sampleA = samplesCount - 1;

                    float sampleValue = (1 - frac) * _fullWaveformPoints[sampleA] + frac * _fullWaveformPoints[sampleB];

                    float y = midY - sampleValue * verticalScale;

                    int prevSampleA = (int)Math.Floor((x - 1) * samplesPerPixel);
                    int prevSampleB = prevSampleA + 1;
                    if (prevSampleB >= samplesCount) prevSampleB = samplesCount - 1;
                    if (prevSampleA >= samplesCount) prevSampleA = samplesCount - 1;
                    float prevFrac = (x - 1) * samplesPerPixel - prevSampleA;
                    float prevValue = (1 - prevFrac) * _fullWaveformPoints[prevSampleA] + prevFrac * _fullWaveformPoints[prevSampleB];
                    float prevY = midY - prevValue * verticalScale;

                    path.AddLine(x - 1, prevY, x, y);
                }

                // close bottom line
                path.AddLine(width - 1, midY - _fullWaveformPoints[samplesCount - 1] * verticalScale, width - 1, midY);
                path.AddLine(width - 1, midY, 0, midY);
                path.CloseFigure();

                using (var gradientBrush = new LinearGradientBrush(
                    new Rectangle(0, 0, width, height),
                    _waveformColorStart,
                    _waveformColorEnd,
                    LinearGradientMode.Vertical))
                {
                    g.FillPath(gradientBrush, path);
                }

                // Thicker highlight line on top edge
                using (var highlightPen = new Pen(Color.FromArgb(120, Color.White), 2))
                {
                    PointF[] highlightPoints = new PointF[width];
                    for (int x = 0; x < width; x++)
                    {
                        float sampleIndexF = x * samplesPerPixel;
                        int sampleA = (int)Math.Floor(sampleIndexF);
                        int sampleB = sampleA + 1;
                        float frac = sampleIndexF - sampleA;
                        if (sampleB >= samplesCount) sampleB = samplesCount - 1;
                        if (sampleA >= samplesCount) sampleA = samplesCount - 1;
                        float sampleValue = (1 - frac) * _fullWaveformPoints[sampleA] + frac * _fullWaveformPoints[sampleB];
                        float y = midY - sampleValue * verticalScale + 1f;
                        highlightPoints[x] = new PointF(x, y);
                    }
                    g.DrawCurve(highlightPen, highlightPoints);
                }
            }

            // Optional: draw vertical cursor line on hover
            if (_hoverX.HasValue)
            {
                int mx = _hoverX.Value;
                if (mx >= 0 && mx < width)
                {
                    using (var cursorPen = new Pen(Color.FromArgb(180, Color.White), 2))
                    {
                        g.DrawLine(cursorPen, mx, 0, mx, height);
                    }
                }
            }

            // Panel border
            using (var borderPen = new Pen(Color.FromArgb(150, _borderColor), 3))
            {
                var rect = _panel.ClientRectangle;
                rect.Width -= 1;
                rect.Height -= 1;
                g.DrawRectangle(borderPen, rect);
            }
        }

        public void Dispose()
        {
            _panel.Paint -= Panel_Paint;
            _panel.Resize -= Panel_Resize;
            _panel.SizeChanged -= Panel_SizeChanged;
        }
    }
}

make it to where if an empty audio file path is present just apply the colors and clear the waveform.