namespace Quik
{
    /// <summary>
    /// Enumeration of QUIK commands.
    /// </summary>
    public enum QuikCommandType
    {
        /// <summary>
        /// Nothing.
        /// </summary>
        /// <seealso cref="QuikCommandNone"/>
        None,

        /// <summary>
        /// Set a mask region.
        /// </summary>
        Mask,

        /// <summary>
        /// Draw a line.
        /// </summary>
        /// <seealso cref="QuikCommandLine"/>
        Line,

        /// <summary>
        /// Draw multiple lines.
        /// </summary>
        Lines,

        /// <summary>
        /// Draw a Bezier curve.
        /// </summary>
        Bezier,

        /// <summary>
        /// Draw a rectangle.
        /// </summary>
        Rectangle,

        /// <summary>
        /// Draw multiple rectangles.
        /// </summary>
        Rectangles,

        /// <summary>
        /// Draw an ellipse.
        /// </summary>
        Ellipse,

        /// <summary>
        /// Draw multiple ellipses.
        /// </summary>
        Ellipses,

        /// <summary>
        /// Draw a triangle.
        /// </summary>
        Triangle,

        /// <summary>
        /// Draw multiple triangles.
        /// </summary>
        Triangles,

        /// <summary>
        /// Draw a polygon.
        /// </summary>
        Polygon,

        /// <summary>
        /// Put a character.
        /// </summary>
        PutChar,

        /// <summary>
        /// Put text.
        /// </summary>
        PutText,

        /// <summary>
        /// Flow text in box.
        /// </summary>
        FlowText,

        /// <summary>
        /// Clear the image mask.
        /// </summary>
        StencilMaskClear,

        /// <summary>
        ///  Create an image mask with the following commands.
        /// </summary>
        StencilMaskBegin,

        /// <summary>
        /// End the image mask commands.
        /// </summary>
        StencilMaskEnd,

        /// <summary>
        /// Draw an image.
        /// </summary>
        Image,

        /// <summary>
        /// Begin defining custom commands after this value.
        /// </summary>
        /// <seealso cref="QuikCommand"/>
        CustomCommandRange = 1024,
    }

    /// <summary>
    /// A single QUIK command.
    /// </summary>
    public abstract class QuikCommand
    {
        /// <summary>
        /// The type ID for the QUIK command.
        /// </summary>
        public abstract QuikCommandType Type { get; }
    }

    /// <summary>
    /// Does nothing.
    /// </summary>
    public sealed class QuikCommandNone : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.None;
    }

    public sealed class QuikCommandMask : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Mask;
        
        public QuikRectangle Bounds { get; }

        public QuikCommandMask(QuikRectangle bounds)
        {
            Bounds = bounds;
        }
    }

    /// <summary>
    /// Draws a line.
    /// </summary>
    public sealed class QuikCommandLine : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Line;

        /// <summary>
        /// The line to draw.
        /// </summary>
        public QuikLine Line { get; }
        
        /// <summary>
        /// Stroke style of the line.
        /// </summary>
        public QuikStrokeStyle Style { get; set; }

        /// <summary>
        /// Create a draw line command.
        /// </summary>
        /// <param name="line">The line to draw.</param>
        public QuikCommandLine(QuikLine line)
        {
            Line = line;
        }
    }

    /// <summary>
    /// Draw multiple lines.
    /// </summary>
    public sealed class QuikCommandLines : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Lines;

        /// <summary>
        /// The array of lines to draw.
        /// </summary>
        public QuikLine[] Lines { get; }
        
        /// <summary>
        /// Stroke style of the lines.
        /// </summary>
        public QuikStrokeStyle Style { get; set; }

        /// <summary>
        /// Create a draw lines command.
        /// </summary>
        /// <param name="lines">The lines to draw.</param>
        public QuikCommandLines(QuikLine[] lines)
        {
            Lines = lines;
        }
    }

    /// <summary>
    /// Draw a Bezier curve.
    /// </summary>
    public sealed class QuikCommandBezier : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Bezier;

        /// <summary>
        /// The Bezier curve segments to draw.
        /// </summary>
        public QuikBezier[] Segments;
        
        /// <summary>
        /// Stroke style of the curve.
        /// </summary>
        public QuikStrokeStyle Style { get; set; }

        /// <summary>
        /// Create a draw Bezier curve command.
        /// </summary>
        /// <param name="segments">The Bezier curve segments to draw.</param>
        public QuikCommandBezier(QuikBezier[] segments)
        {
            Segments = segments;
        }
    }

    /// <summary>
    /// Draw a rectangle.
    /// </summary>
    public sealed class QuikCommandRectangle : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Rectangle;

        /// <summary>
        /// The rectangle to draw.
        /// </summary>
        public QuikRectangle Rectangle { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }
        
        /// <summary>
        /// Radius for round corners.
        /// </summary>
        public float CornerRadius { get; set; }

        /// <summary>
        /// Create a draw rectangle command.
        /// </summary>
        /// <param name="rectangle">The rectangle to draw.</param>
        public QuikCommandRectangle(QuikRectangle rectangle)
        {
            Rectangle = rectangle;
        }
    }

    /// <summary>
    /// Draw rectangles.
    /// </summary>
    public sealed class QuikCommandRectangles : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Rectangles;

        /// <summary>
        /// The rectangles to draw.
        /// </summary>
        public QuikRectangle[] Rectangles { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }
        
        /// <summary>
        /// Radius for round corners.
        /// </summary>
        public float CornerRadius { get; set; }

        /// <summary>
        /// Create a draw rectangles commands.
        /// </summary>
        /// <param name="rectangles">The rectangles to draw.</param>
        public QuikCommandRectangles(QuikRectangle[] rectangles)
        {
            Rectangles = rectangles;
        }
    }

    /// <summary>
    /// Draw an ellipse.
    /// </summary>
    public sealed class QuikCommandEllipse : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Ellipse;

        /// <summary>
        /// The ellipse to draw.
        /// </summary>
        public QuikEllipse Ellipse { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }

        /// <summary>
        /// Create a draw ellipse command.
        /// </summary>
        /// <param name="ellipse">The ellipse to draw.</param>
        public QuikCommandEllipse(QuikEllipse ellipse)
        {
            Ellipse = ellipse;
        }
    }

    /// <summary>
    /// Draw ellipses.
    /// </summary>
    public sealed class QuikCommandEllipses : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Ellipses;
        
        /// <summary>
        /// The ellipses to draw.
        /// </summary>
        public QuikEllipse[] Ellipses { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }

        /// <summary>
        /// Create a draw ellipses command.
        /// </summary>
        /// <param name="ellipses">The ellipses to draw.</param>
        public QuikCommandEllipses(QuikEllipse[] ellipses)
        {
            Ellipses = ellipses;
        }
    }

    /// <summary>
    /// Create a draw triangle command.
    /// </summary>
    public sealed class QuikCommandTriangle : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Triangle;

        /// <summary>
        /// The triangle to draw.
        /// </summary>
        public QuikTriangle Triangle { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }

        /// <summary>
        /// Create a draw triangle command.
        /// </summary>
        /// <param name="triangle">The triangles to draw.</param>
        public QuikCommandTriangle(QuikTriangle triangle)
        {
            Triangle = triangle;
        }
    }

    /// <summary>
    /// Draw triangles.
    /// </summary>
    public sealed class QuikCommandTriangles : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Triangles;

        /// <summary>
        /// The triangles to draw.
        /// </summary>
        public QuikTriangle[] Triangles { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }

        /// <summary>
        /// Create a draw triangles command.
        /// </summary>
        /// <param name="triangles">The triangles to draw.</param>
        public QuikCommandTriangles(QuikTriangle[] triangles)
        {
            Triangles = triangles;
        }
    }

    /// <summary>
    /// Draw a polygon.
    /// </summary>
    /// <remarks>Behavior is defined by rendering backend for concave polygons.</remarks>
    public sealed class QuikCommandPolygon : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Polygon;

        /// <summary>
        /// The vertices that make up the polygon.
        /// </summary>
        public QuikVec2[] Polygon { get; }
        
        /// <summary>
        /// Stroke style of the border.
        /// </summary>
        public QuikStrokeStyle StrokeStyle { get; set; }
        
        /// <summary>
        /// Fill style of the contents.
        /// </summary>
        public QuikFillStyle FillStyle { get; set; }

        /// <summary>
        /// Create a draw polygon command.
        /// </summary>
        /// <param name="polygon">The polygon to draw.</param>
        public QuikCommandPolygon(QuikVec2[] polygon)
        {
            Polygon = polygon;
        }
    }

    /// <summary>
    /// Put a character.
    /// </summary>
    public sealed class QuikCommandPutChar : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.PutChar;

        /// <summary>
        /// The character to put.
        /// </summary>
        /// <remarks>This field is integer to accomodate for surrogate pairs.</remarks>
        public int Character { get; }

        /// <summary>
        /// The baseline start position of the character.
        /// </summary>
        public QuikVec2 Position { get; }

        /// <summary>
        /// Create a put character command.
        /// </summary>
        /// <param name="character">The character to put.</param>
        /// <param name="position">The baseline start position of the character.</param>
        public QuikCommandPutChar(int character, QuikVec2 position)
        {
            Character = character;
            Position = position;
        }

        /// <summary>
        /// Create a put character command.
        /// </summary>
        /// <param name="character">The character to put.</param>
        /// <param name="position">The baseline start position of the character.</param>
        public QuikCommandPutChar(char character, QuikVec2 position) : this((int) character, position)
        {
        }
    }

    /// <summary>
    /// Put some text.
    /// </summary>
    public sealed class QuikCommandPutText : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.PutText;

        /// <summary>
        /// The text to put.
        /// </summary>
        public string Text { get; }

        /// <summary>
        /// The baseline start position of the text.
        /// </summary>
        public QuikVec2 Position { get; }

        /// <summary>
        /// Create a put text command.
        /// </summary>
        /// <param name="text">The text to put.</param>
        /// <param name="position">The baseline start position of the text.</param>
        public QuikCommandPutText(string text, QuikVec2 position)
        {
            Text = text;
            Position = position;
        }
    }

    /// <summary>
    /// Flow text into a box.
    /// </summary>
    public sealed class QuikCommandFlowText : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.FlowText;

        /// <summary>
        /// The text to flow.
        /// </summary>
        public string Text { get; }
        
        /// <summary>
        /// The flowing box boundaries. 
        /// </summary>
        public QuikRectangle Bounds { get; }
        
        /// <summary>
        /// Create a flow text command. 
        /// </summary>
        /// <param name="text">The text to flow.</param>
        /// <param name="bounds">The flowing box boundaries.</param>
        public QuikCommandFlowText(string text, QuikRectangle bounds)
        {
            Text = text;
            Bounds = bounds;
        }
    }

    /// <summary>
    /// Clear the stencil buffer.
    /// </summary>
    public sealed class QuikCommandStencilClear : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.StencilMaskClear;
    }

    /// <summary>
    /// Begin rendering to the stencil buffer.
    /// </summary>
    public sealed class QuikCommandStencilBegin : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.StencilMaskBegin;
    }

    /// <summary>
    /// End rendering to the stencil buffer.
    /// </summary>
    public sealed class QuikCommandStencilEnd : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.StencilMaskEnd;
    }

    /// <summary>
    /// Draw an image.
    /// </summary>
    public sealed class QuikCommandImage : QuikCommand
    {
        /// <inheritdoc/>
        public override QuikCommandType Type => QuikCommandType.Image;
    }
}