/*******************************************************************************************
*
*   raylib-extras [ImGui] example - editor
*
*	This is a more complex ImGui Integration
*	It shows how to build windows on top of 2d and 3d views using a render texture
*
*   Copyright (c) 2021 Jeffery Myers
*
********************************************************************************************/


#include "raylib.h"
#include "raymath.h"

#include "imgui.h"
#include "rlImGui.h"
#include "rlImGuiColors.h"

bool Quit = false;

bool ImGuiDemoOpen = false;

// DPI scaling functions
float ScaleToDPIF(float value)
{
    return GetWindowScaleDPI().x * value;
}

int ScaleToDPII(int value)
{
    return int(GetWindowScaleDPI().x * value);
}

class DocumentWindow
{
public:
	bool Open = false;

	RenderTexture ViewTexture;

	virtual void Setup() = 0;
	virtual void Shutdown() = 0;
	virtual void Show() = 0;
	virtual void Update() = 0;

	bool Focused = false;

	Rectangle ContentRect = { 0 };
};

class ImageViewerWindow : public DocumentWindow
{
public:

	void Setup() override
	{
		Camera.zoom = 1;
		Camera.target.x = 0;
		Camera.target.y = 0;
		Camera.rotation = 0;
		Camera.offset.x = GetScreenWidth() / 2.0f;
		Camera.offset.y = GetScreenHeight() / 2.0f;

		ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
		ImageTexture = LoadTexture("resources/parrots.png");

		UpdateRenderTexture();
	}

	void Show() override
	{
		ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
		ImGui::SetNextWindowSizeConstraints(ImVec2(ScaleToDPIF(400.0f), ScaleToDPIF(400.0f)), ImVec2(float(GetScreenWidth()), float(GetScreenHeight())));

		Focused = false;

		if (ImGui::Begin("Image Viewer", &Open, ImGuiWindowFlags_NoScrollbar))
		{
			// save off the screen space content rectangle
			ContentRect = { ImGui::GetWindowPos().x + ImGui::GetCursorScreenPos().x, ImGui::GetWindowPos().y + ImGui::GetCursorScreenPos().y, ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y };

			Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);

			ImVec2 size = ImGui::GetContentRegionAvail();

			// center the scratch pad in the view
			Rectangle viewRect = { 0 };
			viewRect.x = ViewTexture.texture.width / 2 - size.x / 2;
			viewRect.y = ViewTexture.texture.height / 2 - size.y / 2;
			viewRect.width = size.x;
			viewRect.height = -size.y;

			if (ImGui::BeginChild("Toolbar", ImVec2(ImGui::GetContentRegionAvail().x, 25)))
			{
				ImGui::SetCursorPosX(2);
				ImGui::SetCursorPosY(3);

				if (ImGui::Button("None"))
				{
					CurrentToolMode = ToolMode::None;
				}
				ImGui::SameLine();

				if (ImGui::Button("Move"))
				{
					CurrentToolMode = ToolMode::Move;
				}

				ImGui::SameLine();
				switch (CurrentToolMode)
				{
					case ToolMode::None:
						ImGui::TextUnformatted("No Tool");
						break;
					case ToolMode::Move:
						ImGui::TextUnformatted("Move Tool");
						break;
					default:
						break;
				}

				ImGui::SameLine();
				ImGui::TextUnformatted(TextFormat("camera target X%f Y%f", Camera.target.x, Camera.target.y));
				ImGui::EndChild();
			}

			rlImGuiImageRect(&ViewTexture.texture, int(size.x), int(size.y), viewRect);	
		}
		ImGui::End();
		ImGui::PopStyleVar();
	}

	void Update() override
	{
		if (!Open)
			return;

		if (IsWindowResized())
		{
			UnloadRenderTexture(ViewTexture);
			ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());

			Camera.offset.x = GetScreenWidth() / 2.0f;
			Camera.offset.y = GetScreenHeight() / 2.0f;
		}

		Vector2 mousePos = GetMousePosition();

		if (Focused)
		{
			if (CurrentToolMode == ToolMode::Move)
			{
				// only do this tool when the mouse is in the content area of the window
				if (IsMouseButtonDown(0) && CheckCollisionPointRec(mousePos, ContentRect))
				{
					if (!Dragging)
					{
						LastMousePos = mousePos;
						LastTarget = Camera.target;
					}
					Dragging = true;
					Vector2 mouseDelta = Vector2Subtract(LastMousePos, mousePos);

					mouseDelta.x /= Camera.zoom;
					mouseDelta.y /= Camera.zoom;
					Camera.target = Vector2Add(LastTarget, mouseDelta);

					DirtyScene = true;

				}
				else
				{
					Dragging = false;
				}
			}
		}
		else
		{
			Dragging = false;
		}

		if (DirtyScene)
		{
			DirtyScene = false;
			UpdateRenderTexture();
		}
	}

	Texture ImageTexture;
	Camera2D Camera = { 0 };

	Vector2 LastMousePos = { 0 };
	Vector2 LastTarget = { 0 };
	bool Dragging = false;

	bool DirtyScene = false;

	enum class ToolMode
	{
		None,
		Move,
	};

	ToolMode CurrentToolMode = ToolMode::None;

	void UpdateRenderTexture()
	{
		BeginTextureMode(ViewTexture);
		ClearBackground(BLUE);

		// camera with our view offset with a world origin of 0,0
		BeginMode2D(Camera);

		// center the image at 0,0
		DrawTexture(ImageTexture, ImageTexture.width / -2, ImageTexture.height / -2, WHITE);

		EndMode2D();
		EndTextureMode();
	}

	void Shutdown() override
	{
		UnloadRenderTexture(ViewTexture);
		UnloadTexture(ImageTexture);
	}
};

class SceneViewWindow : public DocumentWindow
{
public:
	Camera3D Camera = { 0 };

	void Setup() override
	{
		ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());

		Camera.fovy = 45;
		Camera.up.y = 1;
		Camera.position.y = 3;
		Camera.position.z = -25;

		Image img = GenImageChecked(ScaleToDPII(256), ScaleToDPII(256), ScaleToDPII(32), ScaleToDPII(32), DARKGRAY, WHITE);
		GridTexture = LoadTextureFromImage(img);
		UnloadImage(img);
		GenTextureMipmaps(&GridTexture);
		SetTextureFilter(GridTexture, TEXTURE_FILTER_ANISOTROPIC_16X);
		SetTextureWrap(GridTexture, TEXTURE_WRAP_CLAMP);
	}

	void Shutdown() override
	{
		UnloadRenderTexture(ViewTexture);
		UnloadTexture(GridTexture);
	}

	void Show() override
	{
		ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
		ImGui::SetNextWindowSizeConstraints(ImVec2(ScaleToDPIF(400.0f), ScaleToDPIF(400.0f)), ImVec2((float)GetScreenWidth(), (float)GetScreenHeight()));

		if (ImGui::Begin("3D View", &Open, ImGuiWindowFlags_NoScrollbar))
		{
			Focused = ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows);
			// draw the view
			rlImGuiImageRenderTextureFit(&ViewTexture, true);
		}
		ImGui::End();
		ImGui::PopStyleVar();
	}

	void Update() override
	{
		if (!Open)
			return;

		if (IsWindowResized())
		{
			UnloadRenderTexture(ViewTexture);
			ViewTexture = LoadRenderTexture(GetScreenWidth(), GetScreenHeight());
		}

		float period = 10;
		float magnitude = 25;

		Camera.position.x = sinf(float(GetTime() / period)) * magnitude;

		BeginTextureMode(ViewTexture);
		ClearBackground(SKYBLUE);

		BeginMode3D(Camera);

		// grid of cube trees on a plane to make a "world"
		DrawPlane(Vector3{ 0, 0, 0 }, Vector2{ 50, 50 }, BEIGE); // simple world plane
		float spacing = 4;
		int count = 5;

		for (float x = -count * spacing; x <= count * spacing; x += spacing)
		{
			for (float z = -count * spacing; z <= count * spacing; z += spacing)
			{
				Vector3 pos = { x, 0.5f, z };

				Vector3 min = { x - 0.5f,0,z - 0.5f };
				Vector3 max = { x + 0.5f,1,z + 0.5f };

				DrawCube(Vector3{ x, 1.5f, z }, 1, 1, 1, GREEN);
				DrawCube(Vector3{ x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN);
			}
		}

		EndMode3D();
		EndTextureMode();
	}

	Texture2D GridTexture = { 0 };
};


ImageViewerWindow ImageViewer;
SceneViewWindow SceneView;

void DoMainMenu()
{
	if (ImGui::BeginMainMenuBar())
	{
		if (ImGui::BeginMenu("File"))
		{
			if (ImGui::MenuItem("Exit"))
				Quit = true;

			ImGui::EndMenu();
		}

		if (ImGui::BeginMenu("Window"))
		{
			ImGui::MenuItem("ImGui Demo", nullptr, &ImGuiDemoOpen);
			ImGui::MenuItem("Image Viewer", nullptr, &ImageViewer.Open);
			ImGui::MenuItem("3D View", nullptr, &SceneView.Open);

			ImGui::EndMenu();
		}
		ImGui::EndMainMenuBar();
	}
}

int main(int argc, char* argv[])
{
	// Initialization
	//--------------------------------------------------------------------------------------
	int screenWidth = 1900;
	int screenHeight = 900;

    // do not set the FLAG_WINDOW_HIGHDPI flag, that scales a low res framebuffer up to the native resolution.
	// use the native resolution and scale your geometry.
	SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT);
	InitWindow(screenWidth, screenHeight, "raylib-Extras [ImGui] example - Editor Example");
	SetTargetFPS(144);
	rlImGuiSetup(true);
	ImGui::GetIO().ConfigWindowsMoveFromTitleBarOnly = true;

	ImageViewer.Setup();
	ImageViewer.Open = true;

	SceneView.Setup();
	SceneView.Open = true;

	// Main game loop
	while (!WindowShouldClose() && !Quit)    // Detect window close button or ESC key
	{
		ImageViewer.Update();
		SceneView.Update();

		BeginDrawing();
		ClearBackground(DARKGRAY);

		rlImGuiBegin();
		DoMainMenu();

		if (ImGuiDemoOpen)
			ImGui::ShowDemoWindow(&ImGuiDemoOpen);

		if (ImageViewer.Open)
			ImageViewer.Show();

		if (SceneView.Open)
			SceneView.Show();

		rlImGuiEnd();

		EndDrawing();
		//----------------------------------------------------------------------------------
	}
	rlImGuiShutdown();

	ImageViewer.Shutdown();
	SceneView.Shutdown();

	// De-Initialization
	//--------------------------------------------------------------------------------------   
	CloseWindow();        // Close window and OpenGL context
	//--------------------------------------------------------------------------------------

	return 0;
}