/**
 * @file TestRenderer.cpp
 * @brief 渲染功能综合测试实现。
 */

#include "TestRenderer.h"
#include "Platform/Platform.h"
#include "Platform/PlatformTypes.h"
#include "Graphics_Beta/Renderer.h"
#include "ShaderComponents.h"
#include "Content/ContentToEngine.h"
#include "Graphics_Beta/Direct3D12/D3D12Core.h"
#include "Components/Script.h"
#include "Components/Entity.h" 
#include "Components/Transform.h"
#include <filesystem>
#include <fstream>


#if TEST_RENDERER
using namespace XEngine;

class rotator_script;
REGISTER_SCRIPT(rotator_script);
class rotator_script :public script::entity_script
{
public:
	constexpr explicit rotator_script(game_entity::entity entity)
		: script::entity_script{ entity }{}
	void begin_play() override {}
	void update(float dt) override
	{
		_angle += 0.25f * dt * math::two_pi;
		if (_angle > math::two_pi) _angle -= math::two_pi;
		math::v3a rot{ 0.f, _angle, 0.f };
		DirectX::XMVECTOR quat{ DirectX::XMQuaternionRotationRollPitchYawFromVector(DirectX::XMLoadFloat3A(&rot)) };
		math::v4 rot_quat{};
		DirectX::XMStoreFloat4(&rot_quat, quat);
		set_rotation(rot_quat);
	}

private:
	f32 _angle{ 0.f };
};

// Multithreading test worker spawn code ///////////////////

#define ENABLE_TEST_WORKERS 0

constexpr u32	 num_threads{ 8 };
bool			wshutdown{ false };
std::thread		workers[num_threads];

utl::vector<u8> buffer(1024 * 1024, 0);

void buffer_test_worker()
{
	while (!wshutdown)
	{
		auto* resource = graphics::d3d12::d3dx::create_buffer(buffer.data(), (u32)buffer.size());

		graphics::d3d12::core::deferred_release(resource);
	}
}

template<class FnPtr, class... Args>
void init_test_workers(FnPtr&& fnPtr, Args&&... args)
{
#if ENABLE_TEST_WORKERS
	wshutdown = false;
	for (auto& w : workers)
		w = std::thread(std::forward<FnPtr>(fnPtr), std::forward<Args>(args)...);
#endif
}

void joint_test_workers()
{
#if ENABLE_TEST_WORKERS
	wshutdown = true;
	for (auto&w : workers) w.join();
#endif
}
//////////////////////
struct camera_surface {
	game_entity::entity entity{};
	graphics::camera camera{};
	graphics::render_surface surface{};
};
id::id_type item_id{ id::invalid_id };
id::id_type model_id{ id::invalid_id };

camera_surface _surfaces[3]{};
time_it timer{};

bool resized{ false };
bool is_restarting{ false };
void destroy_camera_surface(camera_surface& surface);
game_entity::entity create_one_game_entity(math::v3 position, math::v3 rotation, const char* script_name);
bool test_initialize();
void test_shutdown();
id::id_type create_render_item(id::id_type entity_id);
void destroy_render_item(id::id_type item_id);

void generate_lights();
void remove_lights();

LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	bool toggle_fullscreen{ false };
	switch (msg)
	{
	case WM_DESTROY:
	{
		bool all_closed{ true };
		for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
		{
			if (_surfaces[i].surface.window.is_valid())
			{

				if (_surfaces[i].surface.window.is_closed())
				{
					destroy_camera_surface(_surfaces[i]);
				}
				else
				{
					all_closed = false;
				}
			}
		}
		if (all_closed && !is_restarting)
		{
			PostQuitMessage(0);
			return 0;
		}
	}
	case WM_SIZE:
		resized = (wparam != SIZE_MINIMIZED);
		break;
	case WM_SYSCHAR:
		toggle_fullscreen = (wparam == VK_RETURN && (HIWORD(lparam) & KF_ALTDOWN));
		break;
	case WM_KEYDOWN:
		if (wparam == VK_ESCAPE)
		{
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		}
		else if (wparam == VK_F11)
		{
			is_restarting = true;
			test_shutdown();
			test_initialize();
		}
	}

	if ((resized & (GetAsyncKeyState(VK_LBUTTON) >= 0)) || toggle_fullscreen)
	{
		platform::window win{ platform::window_id{(id::id_type)GetWindowLongPtr(hwnd, GWLP_USERDATA)} };
		for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
		{
			if (win.get_id() == _surfaces[i].surface.window.get_id())
			{
				if (toggle_fullscreen)
				{
					win.set_fullscreen(!win.is_fullscreen());
					return 0;
				}
				else
				{
					_surfaces[i].surface.surface.resize(win.width(), win.height());
					_surfaces[i].camera.aspect_ratio((f32)win.width() / win.height());
					resized = false;
				}
				break;
			}
		}
	}

	return DefWindowProc(hwnd, msg, wparam, lparam);
}

void
create_camera_surface(camera_surface& surface, platform::window_init_info info)
{
	surface.surface.window = platform::create_window(&info);
	surface.surface.surface = graphics::create_surface(surface.surface.window);
	surface.entity = create_one_game_entity({ 0.0f,0.f,6.0f }, { 0.f, 3.14f, 0.f }, nullptr);
	surface.camera = graphics::create_camera(graphics::perspective_camera_init_info{ surface.entity.get_id() });
	surface.camera.aspect_ratio((f32)surface.surface.window.width() / surface.surface.window.height());
}


void
destroy_camera_surface(camera_surface& surface)
{
	camera_surface temp{ surface };
	surface = {};
	if (temp.surface.surface.is_valid()) graphics::remove_surface(temp.surface.surface.get_id());
	if (temp.surface.window.is_valid()) platform::remove_window(temp.surface.window.get_id());
	if (temp.camera.is_valid()) graphics::remove_camera(temp.camera.get_id());
	if (temp.entity.is_valid()) game_entity::remove(temp.entity.get_id());
}

game_entity::entity
create_one_game_entity(math::v3 position, math::v3 rotation, const char* script_name)
{
	transform::init_info transform_info{};
	DirectX::XMVECTOR quat{ DirectX::XMQuaternionRotationRollPitchYawFromVector(DirectX::XMLoadFloat3(&rotation)) };
	math::v4a rot_quat;
	DirectX::XMStoreFloat4A(&rot_quat, quat);
	memcpy(&transform_info.rotation[0], &rot_quat.x, sizeof(transform_info.rotation));
	memcpy(&transform_info.position[0], &position.x, sizeof(transform_info.position));

	script::init_info script_info{};
	if (script_name)
	{
		script_info.script_creator = script::detail::get_script_creator(script::detail::string_hash()(script_name));
		assert(script_info.script_creator);
	}


	game_entity::entity_info entity_info{};
	entity_info.transform = &transform_info;
	entity_info.script = &script_info;

	game_entity::entity ntt{ game_entity::create(entity_info) };
	assert(ntt.is_valid());
	return ntt;
}

void
remove_game_entity(game_entity::entity_id id)
{
	game_entity::remove(id);
}

bool
read_file(std::filesystem::path path, std::unique_ptr<u8[]>&data, u64& size)
{
	if (!std::filesystem::exists(path)) return false;

	size = std::filesystem::file_size(path);
	assert(size);
	if (!size) return false;
	data = std::make_unique<u8[]>(size);
	std::ifstream file{ path, std::ios::in | std::ios::binary };
	if (!file || !file.read((char*)data.get(), size))
	{
		file.close();
		return false;
	}

	file.close();
	return true;
}


bool
test_initialize()
{
	while (!compile_shaders())
	{
		if (MessageBox(nullptr, L"Failed to compile engine shaders.", L"sahder Compilation Error", MB_RETRYCANCEL) != IDRETRY)
			return false;
	}

	if (!graphics::initialize(graphics::graphics_platform::direct3d12))
		return false;

	platform::window_init_info info[]
	{
		{&win_proc, nullptr, L"Test Window 1", 200, 100,400,400},
		{&win_proc, nullptr, L"Test Window 2", 700, 100,400,400},
		{&win_proc, nullptr, L"Test Window 3", 1200,100,400,400},/*
		{&win_proc, nullptr, L"Test Window 4", 200, 600,400,400},
		{&win_proc, nullptr, L"Test Window 5", 700, 600,400,400},
		{&win_proc, nullptr, L"Test Window 6", 1200,600,400,400},*/
	};
	static_assert(_countof(info) == _countof(_surfaces));
	for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
		create_camera_surface(_surfaces[i], info[i]);


	//load test model
	std::unique_ptr<u8[]> model;
	u64 size{ 0 };
	if (!read_file("..\\..\\enginetest\\model.model", model, size)) return false;
	model_id = content::create_resource(model.get(), content::asset_type::mesh);
	if (!id::is_valid(model_id))return false;
	init_test_workers(buffer_test_worker);

	item_id = create_render_item(create_one_game_entity({  }, {  }, nullptr).get_id());

	generate_lights();

	is_restarting = false;
	return true;
}

void
test_shutdown()
{
	remove_lights();
	destroy_render_item(item_id);
	joint_test_workers();

	if (id::is_valid(model_id))
	{
		content::destroy_resource(model_id, content::asset_type::mesh);
	}
	for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
		destroy_camera_surface(_surfaces[i]);

	graphics::shutdown();
}

bool
engine_test::initialize()
{
	return test_initialize();
}

void
engine_test::run()
{
	timer.begin();
	std::this_thread::sleep_for(std::chrono::milliseconds(10));
	script::update(timer.dt_avg());
	for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
	{
		if (_surfaces[i].surface.surface.is_valid())
		{
			f32 threshold{ 10 };

			graphics::frame_info info{};
			info.render_item_ids = &item_id;
			info.render_item_count = 1;
			info.thresholds = &threshold;
			info.light_set_key = 0;
			info.average_frame_time = timer.dt_avg();
			info.camera_id = _surfaces[i].camera.get_id();

			_surfaces[i].surface.surface.render(info);
		}
	}
	timer.end();
}
bool
engine_test::shutdown()
{
	test_shutdown();
	return true;
}

#endif
