#ifndef ROGUE_RENDERER_H
#define ROGUE_RENDERER_H

#include "roguedefs.h"
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_stdinc.h>
#include <SDL2/SDL_video.h>
#include <filesystem>
#include <optional>
#include <vector>

namespace rogue {
// Contains all data required to render the game world
// IMPORTANT: store this in stack memory within the scope that encompasses the game loop (probably main(..)).
// When this is destroyed, rendering becomes impossible until a new one is provided to Render
class RenderData {
	friend class RenderDataSetup;
	friend class Render;
public: ~RenderData();
private: RenderData() = default;
	SDL_Window *window{nullptr};
	SDL_Renderer *renderer{nullptr};
	std::vector<SDL_Texture*> sprites{};
	std::vector<SDL_Texture*> menus{};
};

// Global interface for rendering
// Serves as a service locator for render data with a simplified interface
class Render {
private:
	friend class RenderData;
	// called by render data when it's constructor is called, de-initializes Renderer
	// once this is called rendering is no longer possible
	static void clear_render_data();
public:
	// provides the render data struct to the renderer, enabling it's use
	static void provide_render_data(RenderData &data);

	// clear the screen and set the camera location for the next frame
	static void start_frame(Tile camera_center, unsigned fov = 10);
	// draw a sprite to the screen using it's sprite index
	static void draw(Tile world_space_tile, Sprite sprite);
	// draw a menu image that fills screen
	static void draw_menu(Sprite menu);
	// draw a healthbar
	static void draw_healthbar(int current, int max, SDL_Rect area={0,0,500,50}, int border=10);
	// finish the frame and swap the buffers
	static void finish_frame();
private:
	// the tile at the center of the camera, set at the start of a frame
	static Tile camera_center;
	// number of tiles to render horizontally
	static unsigned camera_width;
	// the world offset to apply to sprites when drawing them to put them relative to the camera
	static Tile camera_offset;
	static SDL_Point window_size; // size of the window, computed at the start of the frame
	static int tile_screen_width; // width of a tile on the screen in pixels
	static int half_tile_screen_width; // half of 'tile_screen_width', pre-computed at frame start
	static RenderData *data; // render data provided to the renderer, rendering is only possible while this is set
};

// Render configuration, builder for RenderData
class RenderDataSetup {
public:
	RenderDataSetup() = default;
	// window has to be set up before anything else
	RenderDataSetup &with_window(char const *name, Uint32 flags);
	// renderer has to be set up before images can be loaded
	RenderDataSetup &with_renderer(Uint32 flags);
	// set the resource path, required to load resources
	RenderDataSetup &with_resource_path(std::filesystem::path base_path);
	// load a sprite and register it
	RenderDataSetup &with_sprite(std::filesystem::path sprite_path);
	// load a menu image and register it
	RenderDataSetup &with_menu(std::filesystem::path sprite_path);
	// construct render-data that can be provided to Render
	RenderData build();
private:
	// helper function for loading SDL textures from filepaths
	SDL_Texture *load_texture(std::filesystem::path file_path);
private:
	// the base path to load images from
	std::optional<std::filesystem::path> resource_base_path;
	SDL_Window *window{nullptr}; // window created for the application
	SDL_Renderer *renderer{nullptr}; // renderer created from the window
	// images loaded from resource_path
	std::vector<SDL_Texture*> sprites{};
	std::vector<SDL_Texture*> menus{};
};
}

#endif // !ROGUE_RENDERER_H
