#pragma once
#include "YBaseLib/String.h"
#include "YBaseLib/Timer.h"
#include "common/gl_program.h"
#include "common/gl_texture.h"
#include "core/host_interface.h"
#include <SDL.h>
#include <array>
#include <deque>
#include <mutex>
#include <memory>

class System;
class DigitalController;
class MemoryCard;

class SDLInterface : public HostInterface
{
public:
  SDLInterface();
  ~SDLInterface();

  static std::unique_ptr<SDLInterface> Create();

  static TinyString GetSaveStateFilename(u32 index);

  void SetDisplayTexture(GL::Texture* texture, u32 offset_x, u32 offset_y, u32 width, u32 height, float aspect_ratio) override;

  void ReportMessage(const char* message) override;

  // Adds OSD messages, duration is in seconds.
  void AddOSDMessage(const char* message, float duration = 2.0f) override;

  void ConnectDevices();

  void Run();

private:
  struct OSDMessage
  {
    String text;
    Timer time;
    float duration;
  };

  bool CreateSDLWindow();
  bool CreateGLContext();
  bool CreateImGuiContext();
  bool CreateGLResources();

  // We only pass mouse input through if it's grabbed
  bool IsWindowFullscreen() const;
  void DrawImGui();
  void DoLoadState(u32 index);
  void DoSaveState(u32 index);

  bool HandleSDLEvent(const SDL_Event* event);
  bool PassEventToImGui(const SDL_Event* event);
  void Render();
  void RenderDisplay();
  void DrawMainMenuBar();
  void DrawOSDMessages();

  SDL_Window* m_window = nullptr;
  SDL_GLContext m_gl_context = nullptr;
  int m_window_width = 0;
  int m_window_height = 0;

  GL::Program m_display_program;
  GLuint m_display_vao = 0;
  GL::Texture* m_display_texture = nullptr;
  u32 m_display_texture_offset_x = 0;
  u32 m_display_texture_offset_y = 0;
  u32 m_display_texture_width = 0;
  u32 m_display_texture_height = 0;
  float m_display_aspect_ratio = 1.0f;
  bool m_display_texture_changed = false;

  std::deque<OSDMessage> m_osd_messages;
  std::mutex m_osd_messages_lock;

  std::shared_ptr<DigitalController> m_controller;
  std::shared_ptr<MemoryCard> m_memory_card;

  float m_vps = 0.0f;
  float m_fps = 0.0f;
  float m_speed = 1.0f;
  u32 m_last_frame_number = 0;
  u32 m_last_internal_frame_number = 0;
  u32 m_last_global_tick_counter = 0;
  Timer m_fps_timer;

  // UI options
  bool m_show_gpu_statistics = false;
};