using Engine.Graphics.Pixel;
using Engine.Graphics.Render.Mesh;
using Engine.Graphics.Render.Quad;
using Engine.Graphics.Render.Text;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;

namespace Engine.Graphics;

public class GenericRenderer : IRenderer
{
  public QuadRenderer QuadRenderer => _quadRenderer ??= new QuadRenderer(_engine, 1024 * 8);
  public AnyMeshRenderer AnyMeshRenderer => _anyMeshRenderer ??= new AnyMeshRenderer(_engine, 1024);
  public TextRenderer TextRenderer => _textRenderer ??= new TextRenderer(_engine, 1024 * 8);

  private QuadRenderer? _quadRenderer;
  private AnyMeshRenderer? _anyMeshRenderer;
  private TextRenderer? _textRenderer;

  private readonly Engine _engine;
  internal readonly Framebuffer.Framebuffer _framebuffer;

  private bool _frameStarted;

  public GenericRenderer(Engine parEngine, int parWidth, int parHeight)
  {
    _engine = parEngine;

    _framebuffer = Framebuffer.Framebuffer.Builder(parWidth, parHeight)
      .AddColorAttachment<Rgba8>()
      .AddDepthAttachment()
      .Build();
  }

  public void StartFrame()
  {
    _frameStarted = true;
  }

  public void EndFrame(in Matrix4 parProjectionMatrix, in Matrix4 parViewMatrix)
  {
    if (!_frameStarted)
      throw new InvalidOperationException("Frame not started");

    _framebuffer.Bind();

    GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

    if (_anyMeshRenderer != null)
    {
      _anyMeshRenderer.Render(parProjectionMatrix, parViewMatrix);
      _anyMeshRenderer.Reset();
    }

    if (_quadRenderer != null)
    {
      _quadRenderer.Render(parProjectionMatrix, parViewMatrix);
      _quadRenderer.Reset();
    }

    if (_textRenderer != null)
    {
      _textRenderer.Render(parProjectionMatrix, parViewMatrix);
      _textRenderer.Reset();
    }

    _framebuffer.Unbind();

    _frameStarted = false;
  }

  public void Resize(int parWidth, int parHeight)
  {
    _framebuffer.Resize(parWidth, parHeight);
  }
}