// --------------------------------------------------------------------------------------------------------------------
// <copyright file="IRenderContext.cs" company="OxyPlot">
//   Copyright (c) 2014 OxyPlot contributors
// </copyright>
// <summary>
//   Specifies functionality to render 2D graphics.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

#nullable enable

namespace OxyPlot
{
    using System.Collections.Generic;

    /// <summary>
    /// Specifies functionality to render 2D graphics.
    /// </summary>
    public interface IRenderContext
    {
        /// <summary>
        /// Gets a value indicating whether the context renders to screen.
        /// </summary>
        /// <value><c>true</c> if the context renders to screen; otherwise, <c>false</c>.</value>
        bool RendersToScreen { get; }

        /// <summary>
        /// Draws an ellipse.
        /// </summary>
        /// <param name="extents">The rectangle defining the extents of the ellipse.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the extents will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the extents will not be stroked.</param>
        /// <param name="thickness">The thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        void DrawEllipse(OxyRect extents, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode);

        /// <summary>
        /// Draws a collection of ellipses, where all have the same stroke and fill.
        /// </summary>
        /// <param name="extents">The rectangles defining the extents of the ellipses.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the ellipses will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the ellipses will not be stroked.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <remarks>
        /// This should have better performance than calling <see cref="DrawEllipse" /> multiple times.
        /// </remarks>
        void DrawEllipses(IList<OxyRect> extents, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode);

        /// <summary>
        /// Draws a polyline.
        /// </summary>
        /// <param name="points">The points defining the polyline. The polyline is drawn from point 0, to point 1, to point 2 and so on.</param>
        /// <param name="stroke">The stroke color.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <param name="dashArray">The dash array (in device independent units, 1/96 inch). Use <c>null</c> to get a solid line.</param>
        /// <param name="lineJoin">The line join type.</param>
        void DrawLine(
            IList<ScreenPoint> points,
            OxyColor stroke,
            double thickness,
            EdgeRenderingMode edgeRenderingMode,
            double[]? dashArray = null,
            LineJoin lineJoin = LineJoin.Miter);

        /// <summary>
        /// Draws line segments.
        /// </summary>
        /// <param name="points">The points defining the line segments. Lines are drawn from point 0 to 1, point 2 to 3 and so on.</param>
        /// <param name="stroke">The stroke color.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param>
        /// <param name="lineJoin">The line join type.</param>
        /// <remarks>
        /// This should have better performance than calling <see cref="DrawLine" /> for each segment.
        /// </remarks>
        void DrawLineSegments(
            IList<ScreenPoint> points,
            OxyColor stroke,
            double thickness,
            EdgeRenderingMode edgeRenderingMode,
            double[]? dashArray = null,
            LineJoin lineJoin = LineJoin.Miter);

        /// <summary>
        /// Draws a polygon.
        /// </summary>
        /// <param name="points">The points defining the polygon.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the polygon will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the polygon will not be stroked.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param>
        /// <param name="lineJoin">The line join type.</param>
        void DrawPolygon(
            IList<ScreenPoint> points,
            OxyColor fill,
            OxyColor stroke,
            double thickness,
            EdgeRenderingMode edgeRenderingMode,
            double[]? dashArray = null,
            LineJoin lineJoin = LineJoin.Miter);

        /// <summary>
        /// Draws a collection of polygons, where all polygons have the same stroke and fill.
        /// </summary>
        /// <param name="polygons">The polygons to draw.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the polygons will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the polygons will not be stroked.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <param name="dashArray">The dash array (in device independent units, 1/96 inch).</param>
        /// <param name="lineJoin">The line join type.</param>
        /// <remarks>
        /// This performs better than calling <see cref="DrawPolygon" /> multiple times.
        /// </remarks>
        void DrawPolygons(
            IList<IList<ScreenPoint>> polygons,
            OxyColor fill,
            OxyColor stroke,
            double thickness,
            EdgeRenderingMode edgeRenderingMode,
            double[]? dashArray = null,
            LineJoin lineJoin = LineJoin.Miter);

        /// <summary>
        /// Draws a rectangle.
        /// </summary>
        /// <param name="rectangle">The rectangle to draw.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the rectangle will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the rectangle will not be stroked.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        void DrawRectangle(OxyRect rectangle, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode);

        /// <summary>
        /// Draws a collection of extents, where all have the same stroke and fill.
        /// </summary>
        /// <param name="rectangles">The extents to draw.</param>
        /// <param name="fill">The fill color. If set to <c>OxyColors.Undefined</c>, the extents will not be filled.</param>
        /// <param name="stroke">The stroke color. If set to <c>OxyColors.Undefined</c>, the extents will not be stroked.</param>
        /// <param name="thickness">The stroke thickness (in device independent units, 1/96 inch).</param>
        /// <param name="edgeRenderingMode">The edge rendering mode.</param>
        /// <remarks>
        /// This should have better performance than calling <see cref="DrawRectangle" /> multiple times.
        /// </remarks>
        void DrawRectangles(IList<OxyRect> rectangles, OxyColor fill, OxyColor stroke, double thickness, EdgeRenderingMode edgeRenderingMode);

        /// <summary>
        /// Draws text.
        /// </summary>
        /// <param name="p">The position.</param>
        /// <param name="text">The text.</param>
        /// <param name="fill">The text color.</param>
        /// <param name="fontFamily">The font family.</param>
        /// <param name="fontSize">Size of the font (in device independent units, 1/96 inch).</param>
        /// <param name="fontWeight">The font weight.</param>
        /// <param name="rotation">The rotation angle.</param>
        /// <param name="horizontalAlignment">The horizontal alignment.</param>
        /// <param name="verticalAlignment">The vertical alignment.</param>
        /// <param name="maxSize">The maximum size of the text (in device independent units, 1/96 inch). If set to <c>null</c>, the text will not be clipped.</param>
        /// <remarks>
        /// Multi-line text is not supported.
        /// </remarks>
        void DrawText(
            ScreenPoint p,
            string text,
            OxyColor fill,
            string? fontFamily = null,
            double fontSize = 10,
            double fontWeight = FontWeights.Normal,
            double rotation = 0,
            HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment verticalAlignment = VerticalAlignment.Top,
            OxySize? maxSize = null);

        /// <summary>
        /// Measures the size of the specified text.
        /// </summary>
        /// <param name="text">The text to measure.</param>
        /// <param name="fontFamily">The font family.</param>
        /// <param name="fontSize">Size of the font (in device independent units, 1/96 inch).</param>
        /// <param name="fontWeight">The font weight.</param>
        /// <returns>The size of the text (in device independent units, 1/96 inch).</returns>
        OxySize MeasureText(string text, string? fontFamily = null, double fontSize = 10, double fontWeight = 500);

        /// <summary>
        /// Sets the tool tip for the following items.
        /// </summary>
        /// <param name="text">The text in the tool tip, or <c>null</c> if no tool tip should be shown.</param>
        void SetToolTip(string text);

        /// <summary>
        /// Cleans up resources not in use.
        /// </summary>
        /// <remarks>This method is called at the end of each rendering.</remarks>
        void CleanUp();

        /// <summary>
        /// Draws a portion of the specified <see cref="OxyImage" />.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="srcX">The x-coordinate of the upper-left corner of the portion of the source image to draw.</param>
        /// <param name="srcY">The y-coordinate of the upper-left corner of the portion of the source image to draw.</param>
        /// <param name="srcWidth">Width of the portion of the source image to draw.</param>
        /// <param name="srcHeight">Height of the portion of the source image to draw.</param>
        /// <param name="destX">The x-coordinate of the upper-left corner of drawn image.</param>
        /// <param name="destY">The y-coordinate of the upper-left corner of drawn image.</param>
        /// <param name="destWidth">The width of the drawn image.</param>
        /// <param name="destHeight">The height of the drawn image.</param>
        /// <param name="opacity">The opacity.</param>
        /// <param name="interpolate">interpolate if set to <c>true</c>.</param>
        void DrawImage(OxyImage source, double srcX, double srcY, double srcWidth, double srcHeight, double destX, double destY, double destWidth, double destHeight, double opacity, bool interpolate);

        /// <summary>
        /// Pushes the clipping rectangle onto the clipping stack.
        /// </summary>
        /// <param name="clippingRectangle">The clipping rectangle.</param>
        /// <remarks>
        /// If there is already a clipping rectangle on the clipping stack, the new clipping rectangle will be intersected with the existing clipping rectangle.
        /// Calls to this method must be balanced by a call to <see cref="PopClip"/>.
        /// It is recommended to use <see cref="RenderingExtensions.AutoResetClip(IRenderContext, OxyRect)"/> in combination with a <c>using</c> statement instead of <see cref="PushClip(OxyRect)"/> and <see cref="PopClip"/> if possible.
        /// However if <see cref="PushClip(OxyRect)"/> and <see cref="PopClip"/> are used directly, it is recommended to wrap them in a try...finally block.
        /// </remarks>
        void PushClip(OxyRect clippingRectangle);

        /// <summary>
        /// Pops the most recently pushed clipping rectangle from the clipping stack.
        /// </summary>
        void PopClip();

        /// <summary>
        /// Gets the number of clipping rectangles on the clipping stack.
        /// </summary>
        int ClipCount { get; }
    }
}
