using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;

namespace Ryujinx.Profiler.UI
{
    public partial class ProfileWindow
    {
        // Color index equal to timing flag type as int
        private Color[] _timingFlagColors = new[]
        {
            new Color(150, 25, 25, 50), // FrameSwap   = 0
            new Color(25, 25, 150, 50), // SystemFrame = 1
        };

        private TimingFlag[] _timingFlags;

        private const float GraphMoveSpeed = 40000;
        private const float GraphZoomSpeed = 50;

        private float _graphZoom      = 1;
        private float _graphPosition  = 0;

        private void DrawGraph(float xOffset, float yOffset, float width)
        {
            if (_sortedProfileData.Count != 0)
            {
                int   left, right;
                float top, bottom;

                int    verticalIndex      = 0;
                float  graphRight         = xOffset + width;
                float  barHeight          = (LineHeight - LinePadding);
                long   history            = Profile.HistoryLength;
                double timeWidthTicks     = history / (double)_graphZoom;
                long   graphPositionTicks = (long)(_graphPosition * PerformanceCounter.TicksPerMillisecond);
                long   ticksPerPixel      = (long)(timeWidthTicks / width);

                // Reset start point if out of bounds
                if (timeWidthTicks + graphPositionTicks > history)
                {
                    graphPositionTicks = history - (long)timeWidthTicks;
                    _graphPosition     = (float)graphPositionTicks / PerformanceCounter.TicksPerMillisecond;
                }

                graphPositionTicks = _captureTime - graphPositionTicks;

                GL.Enable(EnableCap.ScissorTest);

                // Draw timing flags
                if (_displayFlags)
                {
                    TimingFlagType prevType = TimingFlagType.Count;

                    GL.Enable(EnableCap.Blend);
                    GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);

                    GL.Begin(PrimitiveType.Lines);
                    foreach (TimingFlag timingFlag in _timingFlags)
                    {
                        if (prevType != timingFlag.FlagType)
                        {
                            prevType = timingFlag.FlagType;
                            GL.Color4(_timingFlagColors[(int)prevType]);
                        }

                        int x = (int)(graphRight - ((graphPositionTicks - timingFlag.Timestamp) / timeWidthTicks) * width);
                        GL.Vertex2(x, 0);
                        GL.Vertex2(x, Height);
                    }
                    GL.End();
                    GL.Disable(EnableCap.Blend);
                }

                // Draw bars
                GL.Begin(PrimitiveType.Triangles);
                foreach (var entry in _sortedProfileData)
                {
                    long furthest = 0;

                    bottom = GetLineY(yOffset, LineHeight, LinePadding, true, verticalIndex);
                    top    = bottom + barHeight;

                    // Skip rendering out of bounds bars
                    if (top < 0 || bottom > Height)
                    {
                        verticalIndex++;
                        continue;
                    }


                    GL.Color3(Color.Green);
                    foreach (Timestamp timestamp in entry.Value.GetAllTimestamps())
                    {
                        // Skip drawing multiple timestamps on same pixel
                        if (timestamp.EndTime < furthest)
                            continue;
                        furthest = timestamp.EndTime + ticksPerPixel;

                        left  = (int)(graphRight - ((graphPositionTicks - timestamp.BeginTime) / timeWidthTicks) * width);
                        right = (int)(graphRight - ((graphPositionTicks - timestamp.EndTime)   / timeWidthTicks) * width);

                        // Make sure width is at least 1px
                        right = Math.Max(left + 1, right);

                        GL.Vertex2(left,  bottom);
                        GL.Vertex2(left,  top);
                        GL.Vertex2(right, top);

                        GL.Vertex2(right, top);
                        GL.Vertex2(right, bottom);
                        GL.Vertex2(left,  bottom);
                    }

                    // Currently capturing timestamp
                    GL.Color3(Color.Red);
                    long entryBegin = entry.Value.BeginTime;
                    if (entryBegin != -1)
                    {
                        left = (int)(graphRight - ((graphPositionTicks - entryBegin) / timeWidthTicks) * width);

                        // Make sure width is at least 1px
                        left = Math.Min(left - 1, (int)graphRight);

                        GL.Vertex2(left,       bottom);
                        GL.Vertex2(left,       top);
                        GL.Vertex2(graphRight, top);

                        GL.Vertex2(graphRight, top);
                        GL.Vertex2(graphRight, bottom);
                        GL.Vertex2(left,       bottom);
                    }

                    verticalIndex++;
                }

                GL.End();
                GL.Disable(EnableCap.ScissorTest);

                string label = $"-{MathF.Round(_graphPosition, 2)} ms";

                // Dummy draw for measure
                float labelWidth = _fontService.DrawText(label, 0, 0, LineHeight, false);
                _fontService.DrawText(label, graphRight - labelWidth - LinePadding, FilterHeight + LinePadding, LineHeight);
                
                _fontService.DrawText($"-{MathF.Round((float)((timeWidthTicks / PerformanceCounter.TicksPerMillisecond) + _graphPosition), 2)} ms", xOffset + LinePadding, FilterHeight + LinePadding, LineHeight);
            }
        }
    }
}
