#ifndef RENDER_CPP
#define RENDER_CPP

#include <core/inc.hpp>

auto render::c_render::setup( const std::wstring& proc_name ) -> render::setup_error_code {
	auto user32_dll = LoadLibraryA( hash_str( "user32.dll" ) );
	if ( !user32_dll ) return render::setup_error_code::render_invalid_common_error;

	auto m_target_pid = g_vm->get_process_pid( proc_name.c_str( ) );
	if ( !m_target_pid ) return render::setup_error_code::render_invalid_pid;

	auto w_handle = this->get_window_handle( m_target_pid );
	if ( !w_handle ) return render::setup_error_code::render_invalid_window;

	this->m_pid = std::move( m_target_pid );
	this->window_target = std::move( w_handle );

	auto get_window = this->get_window( );
	if ( !get_window ) return render::setup_error_code::render_invalid_window;

	auto window_handle = HWND( );
		
	while ( !window_handle ) {
		window_handle = ime::find_ime_window_by_process_id( m_target_pid );

		if ( !window_handle ) {
			Sleep( 500 );
		}
	}

	if ( !window_handle ) return render::setup_error_code::render_invalid_window;

	Sleep( 1500 );

	EnableWindow( window_handle, TRUE );
	ShowWindow( window_handle, TRUE );

	MARGINS margin = { -1, -1, -1, -1 };
	DwmExtendFrameIntoClientArea( window_handle, &margin );

	SetWindowLongA( window_handle, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT );

	this->overlay = std::move( window_handle );
	this->p_object = Direct3DCreate9( D3D_SDK_VERSION );

	if ( !this->p_object )
		return render::setup_error_code::render_invalid_d3d_failure;

	ZeroMemory( &p_params, sizeof( p_params ) );

	p_params.Windowed = TRUE;
	p_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
	p_params.hDeviceWindow = this->overlay;
	p_params.BackBufferFormat = D3DFMT_A8R8G8B8;
	p_params.BackBufferWidth = this->width;
	p_params.BackBufferHeight = this->height;
	p_params.AutoDepthStencilFormat = D3DFMT_D16;
	p_params.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
	p_params.EnableAutoDepthStencil = TRUE;

	if ( p_object->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, this->overlay, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_PUREDEVICE, &p_params, &p_device ) < 0 ) {
		p_object->Release( );
		return render::setup_error_code::render_invalid_d3d_failure;
	}

	IMGUI_CHECKVERSION( );
	ImGui::CreateContext( );
	ImGuiIO& io = ImGui::GetIO( ); ( void ) io;
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
	
	ImGui::StyleColorsDark( );

	if ( !ImGui_ImplWin32_Init( this->overlay ) )
		return render::setup_error_code::render_invalid_d3d_failure;

	if ( !ImGui_ImplDX9_Init( this->p_device ) )
		return render::setup_error_code::render_invalid_d3d_failure;

	if ( this->p_device )
		this->p_device->Release( );
	
	if ( this->p_object )
		p_object->Release( );

	return render::setup_error_code::system_operational;
}

auto render::c_render::begin_scene( ) -> void
{
	SetWindowPos( this->overlay, ( HWND ) 0, 0, 0, this->width, this->height, SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE );
	UpdateWindow( this->overlay );

	p_device->BeginScene( );
	p_device->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB( 0, 0, 0, 0 ), 1.0f, 0 );

	ImGui_ImplDX9_NewFrame( );
	ImGui_ImplWin32_NewFrame( );
	ImGui::NewFrame( );
}


auto render::c_render::tick( ) -> void
{
	g_vars->load( );

	g_framework->load_fonts( this->p_device );
	g_interface->load( );

	constexpr std::uint32_t flag = 0x0001;
	constexpr std::uint32_t wm_quit = 0x0012;

	while ( this->m_msg.message != wm_quit )
	{
		if ( PeekMessageA( &this->m_msg, this->overlay, 0, 0, flag ) )
		{
			TranslateMessage( &this->m_msg );
			DispatchMessage( &this->m_msg );
		}

		auto menu = ( GetAsyncKeyState( g_vars->misc.menu_key ) & 1 );
		if ( menu ) {
			g_vars->misc.menu = !g_vars->misc.menu;
		}

		//if ( !is_spoofed ) {
			//g_vm->hide_window_attributes( ( uintptr_t ) this->overlay );
			//is_spoofed = true;
		//}

		this->update_cursor( );

		this->begin_scene( );
		{
			if ( g_vars->misc.menu ) 
			{
				ImGui::PushFont( g_framework->menu_font );

				auto cursor_position = ImGui::GetMousePos( );

				ImGui::GetForegroundDrawList( )->AddLine(
					ImVec2( cursor_position.x - 8,
						cursor_position.y ),
					ImVec2( cursor_position.x + 8,
						cursor_position.y ),
					ImColor( 255, 0, 0 )
				);

				ImGui::GetForegroundDrawList( )->AddLine(
					ImVec2( cursor_position.x,
						cursor_position.y - 8 ),
					ImVec2( cursor_position.x,
						cursor_position.y + 8 ),
					ImColor( 255, 0, 0 )
				);

				g_interface->draw_interface( );

				ImGui::PopFont( );
			}

			{
				ImGui::PushFont( g_framework->menu_font );

				g_debug_callback->render_notifications( );

				ImGui::PopFont( );
			}

			{
				ImGui::PushFont( g_framework->esp_font );

				g_visual->render_indicators( );

				g_visual->render( );

				g_visual->render_world( );

				g_visual->render_other( );

				ImGui::PopFont( );
			}
		}

		this->end_scene( );
	}
}

auto render::c_render::end_scene( ) -> void
{
	ImGui::EndFrame( );
	p_device->SetRenderState( D3DRS_ZENABLE, FALSE );
	p_device->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	p_device->SetRenderState( D3DRS_SCISSORTESTENABLE, FALSE );
	ImGui::Render( );

	ImGui_ImplDX9_RenderDrawData( ImGui::GetDrawData( ) );
	this->p_device->EndScene( );
	this->p_device->Present( NULL, NULL, NULL, NULL );
}


auto render::c_render::get_screen_status( ) -> bool
{
	return true;
}

auto render::c_render::get_window( ) -> bool
{
	auto result = GetWindowRect( this->window_target, &this->m_rect );
	if ( !result )
		return false;

	this->width = m_rect.right - m_rect.left;
	this->height = m_rect.bottom - m_rect.top;

	this->width_center = width / 2;
	this->height_center = height / 2;

	g_engine->width = this->width;
	g_engine->height = this->height;

	g_engine->width_center = width / 2;
	g_engine->height_center = height / 2;

	return true;
}

auto render::c_render::get_window_handle( std::uint32_t pid ) -> HWND
{
	std::pair<HWND, DWORD> params = { 0, pid };

	auto result = EnumWindows( []( HWND hwnd, LPARAM lParam ) -> int
	{
		auto pParams = ( std::pair<HWND, DWORD>* )( lParam );

		DWORD processId;
		if ( GetWindowThreadProcessId( hwnd, &processId ) && processId == pParams->second )
		{
			SetLastError( -1 );
			pParams->first = hwnd;
			return false;
		}

		return true;

	}, reinterpret_cast< LPARAM >( &params ) );

	if ( !result && GetLastError( ) == -1 && params.first )
	{
		return params.first;
	}

	return 0;
}

#endif // ! guard