#ifndef LUNAR_WM_TYPES_H
#define LUNAR_WM_TYPES_H

#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <GLES3/gl31.h>
#include <openxr/openxr.h>
#include <openxr/openxr_platform.h>

#include <wayland-client-protocol.h>
#include <wayland-egl.h>
#include <wayland-server-core.h>

#include <wlr/backend/session.h>
#include <wlr/backend/wayland.h>
#include <wlr/render/allocator.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <wlr/xwayland/xwayland.h>

#include <raylib.h>
#include <raymath.h>
#include <rlgl.h>

#include "Config.h"
#include "RayExt.h"

struct LunarWM;

typedef struct {
	float r, theta, phi;
} SphericalCoord;

static inline SphericalCoord Vector3ToSpherical(Vector3 v)
{
	SphericalCoord s;
	s.r = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
	if (s.r > 0.0f) {
		s.theta = atan2f(v.z, v.x);
		s.phi = acosf(v.y / s.r);
	} else {
		s.theta = 0.0f;
		s.phi = 0.0f;
	}
	return s;
}

static inline Vector3 SphericalToVector3(SphericalCoord s)
{
	Vector3 v;
	float sin_phi = sinf(s.phi);
	v.x = s.r * cosf(s.theta) * sin_phi;
	v.y = s.r * cosf(s.phi);
	v.z = s.r * sinf(s.theta) * sin_phi;
	return v;
}

typedef struct virtual_output {
	struct wl_global *global;
	struct wl_display *display;
	struct wl_list clients;

	int32_t x, y;
	int32_t phys_w_mm, phys_h_mm;
	int32_t width, height;
	int32_t refresh_mhz;
	int32_t scale;
	enum wl_output_subpixel subpixel;
	enum wl_output_transform transform;
	char const *make, *model;
	char const *name, *desc;
} LunarWM_VirtualOutput;

typedef struct {
	struct LunarWM *server;

	struct wl_list link;
	struct wlr_keyboard *wlr_keyboard;

	struct wl_listener modifiers;
	struct wl_listener key;
	struct wl_listener destroy;
} LunarWM_Keyboard;

typedef struct {
	struct LunarWM *server;

	struct wl_list link;
	struct wlr_pointer *wlr_pointer;

	struct wl_listener motion;
	struct wl_listener destroy;
} LunarWM_Pointer;

typedef struct {
	struct LunarWM *wm;
	struct wlr_output *wlr_output;

	struct wl_listener frame;
	struct wl_listener destroy;
} LunarWM_Output;


typedef struct {
	uint32_t id;

	bool is_xwayland;

	struct LunarWM *server;

	struct wl_listener commit;
	struct wl_listener destroy;

	struct wl_listener map, unmap;

	union {
		struct wlr_xdg_toplevel *xdg;
		struct wlr_xwayland_surface *xwl;
	} u;

	struct wlr_surface *surface;
	struct wlr_texture *texture;

	struct wlr_buffer *locked_buffer;
	struct wlr_gles2_texture_attribs attribs;
	struct wlr_gles2_texture *gles_texture;
	Texture2D rl_texture;
	RenderTexture2D surface_rt;
	struct wlr_box surface_extents;
	bool composed_has_alpha;
} LunarWM_Toplevel;

typedef struct {
	LunarWM_Toplevel *tl;
	SphericalCoord coord;
} LunarWM_Window;

bool LunarWM_Toplevel_init_xdg(
    LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xdg_toplevel *xdg);
bool LunarWM_Toplevel_init_xwayland(
    LunarWM_Toplevel *tl, struct LunarWM *wm, struct wlr_xwayland_surface *xwl);
bool LunarWM_Toplevel_destroy(LunarWM_Toplevel *this);

bool LunarWM_Toplevel_update(LunarWM_Toplevel *this);
void LunarWM_Toplevel_focus(LunarWM_Toplevel *this);

typedef struct {
	XrSwapchain swapchain;
	int64_t swapchain_format;
	GLuint *v_image_views;
} LunarWM_SwapchainInfo;

typedef struct {
	XrSwapchain handle;
	XrSwapchainImageOpenGLESKHR *a_imgs;
	uint32_t a_imgs_count;
} LunarWM_SwapchainImagesEntry;

typedef struct {
	XrHandJointLocationEXT joint_locations[XR_HAND_JOINT_COUNT_EXT];
	XrHandTrackerEXT hand_tracker;
} LunarWM_Hand;

typedef struct {
	XrTime predicted_display_time;
	XrCompositionLayerProjection layer_projection;
	XrCompositionLayerBaseHeader *layers[10];
	uint32_t layers_count;
	XrCompositionLayerProjectionView layer_projection_views[10];
	uint32_t layer_projection_views_count;
} LunarWM_RenderLayerInfo;

typedef struct LunarWM {
	struct {
		struct wl_display *display;
		struct wl_event_loop *event_loop;

		struct wlr_backend *backend;
		struct wlr_renderer *renderer;
		struct wlr_session *session;

		struct wlr_egl *egl;
		EGLDisplay egl_display;
		EGLContext egl_context;
		EGLConfig egl_config;

		struct wlr_allocator *allocator;
		struct wlr_compositor *compositor;
		struct wlr_subcompositor *subcompositor;
		struct wlr_data_device_manager *data_device_manager;

		struct wlr_seat *seat;
		struct wl_list keyboards;
		struct wl_list pointers;
		struct wl_listener new_input_listener;

		struct wlr_xdg_shell *xdg_shell;
		struct wl_listener new_xdg_toplevel_listener;
		struct wl_listener new_xdg_popup_listener;

		struct wlr_xwayland *xwayland;

		struct wl_listener xwayland_ready;
		struct wl_listener xwayland_new_surface;
		struct wl_listener xwayland_associate_tmp;
		struct wl_listener xwayland_dissociate_tmp;

		LunarWM_VirtualOutput *custom_out_virtual;
		LunarWM_VirtualOutput *custom_out_hud;

		LunarWM_Toplevel **v_toplevels;
		int current_focus;

		struct wl_listener new_output_listener;
		LunarWM_Output **v_outputs;

	} wayland;

	struct {
		bool available;

		XrInstance instance;
		XrSystemId system_id;
		XrSession session;
		XrSessionState session_state;
		struct {
			LunarWM_SwapchainInfo *v_color;
			LunarWM_SwapchainInfo *v_depth;
		} swapchains;
		LunarWM_SwapchainImagesEntry swapchain_images[2];
		XrViewConfigurationView *a_view_configuration_views;
		uint32_t view_configuration_views_count;
		XrEnvironmentBlendMode environment_blend_mode;
		XrSpace local_space, view_space;
		LunarWM_Hand hands[2];
		XrSystemHandTrackingPropertiesEXT hand_tracking_system_properties;

		PFN_xrCreateHandTrackerEXT CreateHandTrackerEXT;
		PFN_xrDestroyHandTrackerEXT DestroyHandTrackerEXT;
		PFN_xrLocateHandJointsEXT LocateHandJointsEXT;
		bool hand_tracking_enabled;

		Quaternion recenter_rot;
		Vector3 recenter_trans;
		bool recenter_active;

		bool session_running;
	} xr;

	struct {
		GLuint fbo;
		RenderTexture2D tmp_rt;
		RenderTexture2D main_rt;
		RenderTexture2D hud_rt;
		Camera3D camera;
		Shader linear_srgb;

		Skybox skybox;

		bool first_frame;
	} renderer;

	struct {
		SphericalCoord pointer;
		int active_workspace;
		struct {
			LunarWM_Window *v_windows;
		} workspaces[10];
	} wm;

	ConfigManager *cman;

	_Atomic(int) counter;

	bool initialized;
	bool running;
} LunarWM;

static inline bool LunarWM_get_new_id(LunarWM *this) { return ++this->counter; }

#endif
