using System;
using System.Collections.Generic;

namespace Quik.CommandMachine
{
    public class CommandQueue : Queue<Frame>
    {
        public void Invoke(QuikCommandHandler handler)
        {
            Enqueue(Command.Invoke);
            Enqueue(new Frame(handler));
        }

        public void ConditionalBegin(bool value)
        {
            Enqueue(Command.ConditionalBegin);
            Enqueue((Frame)(value ? 1 : 0));
        }

        public void ConditionalBegin(Func<bool> condition)
        {
            Enqueue(Command.ConditionalBegin);
            Enqueue(new Frame(condition));
        }

        public void ConditionalEnd()
        {
            Enqueue(Command.ConditionalEnd);
        }

        public void PushViewport()
        {
            Enqueue(Command.PushViewport);
        }

        public void IntersectViewport(in QRectangle viewport)
        {
            Enqueue(Command.IntersectViewport);
            Enqueue(viewport);
        }

        public void StoreViewport(in QRectangle viewport)
        {
            Enqueue(Command.StoreViewport);
            Enqueue(viewport);
        }

        public void PopViewport()
        {
            Enqueue(Command.PopViewport);
        }

        public void PushZ()
        {
            Enqueue(Command.PushZ);
        }

        public void IncrementZ()
        {
            Enqueue(Command.IncrementZ);
        }

        public void AddZ(int value)
        {
            if (value == 1)
            {
                IncrementZ();
            }
            else if (value == -1)
            {
                DecrementZ();
            }
            else
            {
                Enqueue(Command.AddZ);
                Enqueue((Frame)value);
            }
        }

        public void StoreZ(int value)
        {
            Enqueue(Command.StoreZ);
            Enqueue((Frame)value);
        }

        public void DecrementZ()
        {
            Enqueue(Command.DecrementZ);
        }

        public void PopZ()
        {
            Enqueue(Command.PopZ);
        }

        public void Line(in QLine line)
        {
            Enqueue(Command.Line);
            Enqueue(line);
        }

        public void Line(params QLine[] lines)
        {
            Enqueue(Command.Line);
            Enqueue((Frame)lines.Length);
            foreach (QLine line in lines)
                Enqueue(line);
        }

        public void Bezier(in QBezier bezier)
        {
            Frame a, b;
            Frame.Create(bezier, out a, out b);

            Enqueue(Command.Bezier);
            Enqueue(a);
            Enqueue(b);
        }

        public void Bezier(params QBezier[] beziers)
        {
            Frame a, b;

            Enqueue(Command.Bezier);
            Enqueue((Frame)beziers.Length);

            foreach (QBezier bezier in beziers)
            {
                Frame.Create(bezier, out a, out b);
                Enqueue(a);
                Enqueue(b);
            }
        }

        public void Rectangle(in QRectangle rectangle)
        {
            Enqueue(Command.Rectangle);
            Enqueue(rectangle);
        }

        public void Rectangle(QRectangle[] rectangles)
        {
            Enqueue(Command.Rectangle);
            Enqueue((Frame)rectangles.Length);
            foreach (QRectangle rectangle in rectangles)
                Enqueue(rectangle);
        }

        public void Ellipse(in QEllipse ellipse)
        {
            Frame a, b;
            Frame.Create(ellipse, out a, out b);

            Enqueue(Command.Ellipse);
            Enqueue(a);
            Enqueue(b);
        }

        public void Ellipse(params QEllipse[] ellipses)
        {
            Frame a, b;
            Enqueue(Command.Ellipse);
            Enqueue((Frame)ellipses.Length);
            foreach (QEllipse ellipse in ellipses)
            {
                Frame.Create(ellipse, out a, out b);
                Enqueue(a);
                Enqueue(b);
            }
        }

        public void Triangle(in QTriangle triangle)
        {
            Enqueue(Command.Triangle);
            Enqueue(triangle.A);
            Enqueue(triangle.B);
            Enqueue(triangle.C);
        }

        public void Triangle(params QTriangle[] triangles)
        {
            Enqueue(Command.Triangle);
            Enqueue((Frame)triangles.Length);
            foreach (QTriangle triangle in triangles)
            {
                Enqueue(triangle.A);
                Enqueue(triangle.B);
                Enqueue(triangle.C);
            }
        }

        public void Polygon(params QVec2[] polygon)
        {
            Enqueue(Command.Polygon);
            Enqueue((Frame)polygon.Length);
            foreach (QVec2 vertex in polygon)
            {
                Enqueue(vertex);
            }
        }

        public void Image(QuikTexture texture, in QRectangle rectangle)
        {
            Enqueue(Command.Image);
            Enqueue((Frame)(int)ImageCommandFlags.Single);
            Enqueue(new Frame(texture));
            Enqueue(rectangle);
        }

        public void Image(QuikTexture texture, in QRectangle rectangle, in QRectangle uv)
        {
            Enqueue(Command.Image);
            Enqueue((Frame)(int)(ImageCommandFlags.Single | ImageCommandFlags.UVs));
            Enqueue(new Frame(texture));
            Enqueue(rectangle);
            Enqueue(uv);
        }

        public void Image(QuikTexture texture, QRectangle[] rectangles, bool interleavedUV = false)
        {
            ImageCommandFlags flags = interleavedUV ? ImageCommandFlags.UVs : ImageCommandFlags.None;

            Enqueue(Command.Image);
            Enqueue(new Frame((int)flags, rectangles.Length / 2));
            Enqueue(new Frame(texture));

            foreach (QRectangle rectangle in rectangles)
            {
                Enqueue(rectangle);
            }
        }

        public void Image(QuikTexture texture, QRectangle[] rectangles, QRectangle[] uvs)
        {
            int count = Math.Min(rectangles.Length, uvs.Length);
            Enqueue(Command.Image);
            Enqueue(new Frame((int)ImageCommandFlags.UVs, count));
            Enqueue(new Frame(texture));

            for (int i = 0; i < count; i++)
            {
                Enqueue(rectangles[i]);
                Enqueue(uvs[i]);
            }
        }
    }
}