using System.Numerics;

namespace Voile.Rendering
{
    public enum BlendMode
    {
        //
        // Summary:
        //     Blend textures considering alpha (default)
        BlendAlpha,
        //
        // Summary:
        //     Blend textures adding colors
        BlendAdditive,
        //
        // Summary:
        //     Blend textures multiplying colors
        BlendMultiplied,
        //
        // Summary:
        //     Blend textures adding colors (alternative)
        BlendAdd,
        //
        // Summary:
        //     Blend textures subtracting colors (alternative)
        BlendSubtract,
        //
        // Summary:
        //     Blend premultiplied textures considering alpha
        BlendAlphaPremul,
        //
        // Summary:
        //     Blend textures using custom src/dst factors (use rlSetBlendMode())
        BlendCustom
    }

    /// <summary>
    /// An abstract class representing the graphics renderer.
    /// </summary>
    public abstract class RenderSystem : IStartableSystem<RendererSettings>, IReloadableSystem, IDisposable
    {
        public void Start(RendererSettings settings)
        {
            CreateAndInitializeWithWindow(settings);
        }

        public void Reload() => ReloadResources();

        /// <inheritdoc/>
        public void Dispose()
        {
            Shutdown();
            GC.SuppressFinalize(this);
        }

        // INIT
        /// <summary>
        /// Creates the renderer with an OS window and initializes internal resources.
        /// </summary>
        /// <param name="renderSettings">Rendering settings.</param>
        public abstract void CreateAndInitializeWithWindow(RendererSettings renderSettings);
        /// <summary>
        /// Initializes internal resources. Should be called before other methods.
        /// </summary>
        /// <param name="settings">Rendering settings.</param>
        public abstract void Initialize(RendererSettings settings);

        // UTIL
        /// <summary>
        /// Indicates if the renderer will render the next frame.
        /// </summary>
        public abstract bool ShouldRun { get; }
        /// <summary>
        /// Target frames per second for rendering.
        /// </summary>
        public abstract int TargetFps { get; set; }
        public abstract bool VSync { get; set; }
        public double FrameTime => GetFrameTime();

        // WINDOW
        /// <summary>
        /// The size of the render window.
        /// </summary>
        public abstract Vector2 WindowSize { get; set; }

        public abstract string WindowTitle { get; set; }
        public abstract bool Fullscreen { get; set; }

        /// <summary>
        /// Active monitor's size.
        /// </summary>
        public abstract Vector2 MonitorSize { get; }

        protected abstract void ReloadResources();

        /// <summary>
        /// Creates a window.
        /// </summary>
        /// <param name="windowSettings">Window settings to use to create the window.</param>
        public abstract void CreateWindow(WindowSettings windowSettings);
        protected abstract void SetWindowTitle(string title);
        protected abstract void SetWindowVSync(bool value);
        protected abstract void SetTargetFps(int fps);
        protected abstract double GetFrameTime();
        protected abstract int GetMonitorWidth(int monitorId);
        protected abstract int GetMonitorHeight(int monitorId);
        protected abstract int GetCurrentMonitor();
        protected abstract bool WindowShouldClose();
        protected abstract void Shutdown();

        // DRAWING
        /// <summary>
        /// Prepares the renderer for drawing the next frame.
        /// </summary>
        public abstract void BeginFrame();
        /// <summary>
        /// Ends rendering of the frame.
        /// </summary>
        public abstract void EndFrame();

        /// <summary>
        /// Begin drawing with texture blending.
        /// </summary>
        /// <param name="blendMode">Type of texture blending.</param>
        public abstract void BeginBlended(BlendMode blendMode);
        public abstract void EndBlended();

        /// <summary>
        /// Begins drawing using a 2D camera.
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="target"></param>
        /// <param name="rotation"></param>
        /// <param name="zoom"></param>
        public abstract void BeginCamera2d(Vector2 offset, Vector2 target, float rotation, float zoom);
        public abstract void EndCamera2d();

        /// <summary>
        /// Clears the render canvas and sets a background color.
        /// </summary>
        /// <param name="color">Background color.</param>
        public abstract void ClearBackground(Color color);

        /// <summary>
        /// Resets all transform operations.
        /// </summary>
        public void ResetTransform()
        {
            transformPosition = Vector2.Zero;
            transformPivot = Vector2.Zero;
            transformRotation = 0.0f;
        }

        /// <summary>
        /// Sets transforms for the next draw operation.
        /// </summary>
        /// <param name="position">Global transform position.</param>
        /// <param name="pivot">Local offset point around which shapes will rotate.</param> 
        /// <param name="rotation">Rotation.</param>
        public void SetTransform(Vector2 position, Vector2 pivot, float rotation = 0.0f)
        {
            transformPosition = position;
            transformPivot = pivot;
            transformRotation = rotation;
        }
        /// <summary>
        /// Sets the transform for the next draw operation.
        /// </summary>
        /// <param name="transform">Transform matrix.</param>
        public abstract void SetTransform(Matrix4x4 transform);
        /// <summary>
        /// Draws a filled circle.
        /// </summary>
        /// <param name="radius">Radius of a circle.</param>
        /// <param name="color">Fill color.</param>
        public abstract void DrawCircle(float radius, Color color);
        /// <summary>
        /// Draws a filled rectangle.
        /// </summary>
        /// <param name="size">Rectangle size.</param>
        /// <param name="color">Fill color.</param>
        public abstract void DrawRectangle(Vector2 size, Color color);

        public abstract void DrawRectangleOutline(Vector2 size, Color color, float outlineWidth = 1.0f);

        /// <summary>
        /// Draws a debug text with a default font.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="text"></param>
        /// <param name="fontSize"></param>
        /// <param name="color"></param>
        public abstract void DrawDebugText(string text, int fontSize, Color color);
        /// <summary>
        /// Draws text using a signed distance field font atlas.
        /// </summary>
        /// <param name="text">Text to draw.</param>
        /// <param name="fontSize">Size of the font.</param>
        /// <param name="color">Color of the text.</param>
        public abstract void DrawSdfText(string text, int fontSize, Color color);

        /// <summary>
        /// Draw text using a rasterized font resource.
        /// </summary>
        /// <param name="font">Rasterized font.</param>
        /// <param name="text">Text to draw.</param>
        /// <param name="color">Color of the text.</param>
        public abstract void DrawText(ResourceRef<Font> font, string text, Color color);

        /// <summary>
        /// Draws the texture.
        /// </summary>
        /// <param name="id">Texture to draw.</param>
        /// <param name="tint">Texture tint.</param>
        public abstract void DrawTexture(ResourceRef<Texture2d> textureResource, Color tint);

        protected Vector2 transformPosition, transformPivot;
        protected float transformRotation;
    }

    /// <summary>
    /// Quality of MSAA anti-aliasing.
    /// </summary>
    public enum Msaa
    {
        None,
        Msaa2x,
        Msaa4x,
        Msaa8x
    }
    /// <summary>
    /// Settings for the renderer.
    /// </summary>
    public struct RendererSettings
    {
        public Msaa Msaa;
        public bool UseVSync;
        public bool Fullscreen;
        public int TargetFps = 60;

        public WindowSettings WindowSettings;

        public RendererSettings() { }

        public static RendererSettings Default => new RendererSettings()
        {
            UseVSync = true,
            TargetFps = 60,
            WindowSettings = WindowSettings.Default
        };
    }
    /// <summary>
    /// Settings for window.
    /// </summary>
    public struct WindowSettings
    {
        public string Title;
        public Vector2 Size = new Vector2(1280, 720);
        public bool Resizable { get; set; } = true;

        public WindowSettings(string title, Vector2 size)
        {
            Title = title;
            Size = size;
        }

        /// <summary>
        /// Default window settings.
        /// </summary>
        public static WindowSettings Default => new("Voile Game", new Vector2(1280, 720));
    }
}