#pragma once

#include <string>

#include "Common/Log.h"
#include "Common/GPU/thin3d.h"
#include "Common/TimeUtil.h"

struct WindowDesc {
	WindowSystem winsys{};
	void *data1 = nullptr;
	void *data2 = nullptr;

	bool Valid() const {
		return winsys != WindowSystem::WINDOWSYSTEM_UNINITIALIZED && data2;  // Not all platforms use data1, so don't check it.
	}
};

// Init is done differently on each platform, and done close to the creation, so it's
// expected to be implemented by subclasses.
class GraphicsContext {
public:
	virtual ~GraphicsContext() = default;

	// Some implementations will separate init of the API and of the rendering surface.
	// wnd here is only to be used for messageboxes and similar, not for the surface.
	virtual bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { return true; }
	virtual void ShutdownAPI() {}

	// These should be called on the render thread if NeedsRenderThread.
	virtual bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { return true; }
	virtual void ShutdownSurface() {}

	// Used during window resize on desktop ONLY. Must be called from the window thread,
	// not the rendering thread or CPU thread.
	virtual void Pause() {}
	virtual void Resume() {}

	virtual void Resize() = 0;
	virtual void NotifyWindowRestored() {}

	// Needs casting to the appropriate type, unfortunately. Should find a better solution..
	virtual void *GetAPIContext() { return nullptr; }

	// OpenGL returns true here. Later we will need to do the same with Vulkan.
	virtual bool NeedsSeparateEmuThread() const { return false; }

	// Called from the render thread from threaded backends.
	virtual void ThreadStart() {}
	virtual bool ThreadFrame() { return true; }   // Returns false when it's time to exit the loop.
	virtual void ThreadEnd() {}

	// When this is called, the emulation thread has stopped producing frames completely.
	// Called from the emu thread, so be ready for that.
	virtual void NotifyEmuThreadExit() {}

	// Useful for checks that need to be performed every frame.
	// Should strive to get rid of these.
	virtual void Poll() {}

	// TODO: Store in a protected variable?
	virtual Draw::DrawContext *GetDrawContext() = 0;
};

// This is used by the headless build, for the case of software rendering where we really don't need any context,
// unlike the standalone build where we do need a context anyway for UI and stuff.
class NullGraphicsContext : public GraphicsContext {
public:
	NullGraphicsContext() {}
	Draw::DrawContext *GetDrawContext() override { return nullptr; }
	void Resize() override {}
	bool NeedsSeparateEmuThread() const override { return false; }
};
