#ifndef DRAWING_HPP
#define DRAWING_HPP
#define IMGUI_DEFINE_MATH_OPERATORS

#include <core/inc.hpp>

namespace drawing_framework
{
	class c_drawing
	{
	public:
		ImFont* esp_font{ };
		ImFont* menu_font{ };
		ImFont* notifications_font{ };
		ImFont* icons_font { };

		IDirect3DTexture9* logo_texture{ };

	public:
		void triangle( ImVec2 x_point, ImVec2 y_point, ImVec2 z_point, ImColor color, bool outline = false, ImColor outline_color = ImColor( 0, 0, 0 ), bool fill = false, ImColor fill_color = ImColor( 0, 0, 0 ) );
		void outlined_rectangle( ImVec2 size, ImVec2 location, ImColor col );
		void skeleton( float p1, float p2, float p3, float p4, ImColor color, float thickness );
		void line( const ImVec2& from, const ImVec2& to, ImColor color, float thickness );
		void dashed_line( const ImVec2& start, const ImVec2& end, ImColor color, float thickness, float dash_length );
		void dotted_line( const ImVec2& start, const ImVec2& end, ImColor color, float thickness, float dot_spacing );
		void add_image( IDirect3DTexture9* texture, const ImVec2& top_left, const ImVec2& bottom_right, ImColor color );
		void circle( float x, float y, float radius, ImColor color, float stroke, bool filled = false );
		void filled_rect( float x, float y, float width, float height, ImColor color, float rounding = 0.0f );
		void bounding_box( const math::vector3& position, const math::vector3& size, ImColor color );
		void text( const std::string& input, ImVec2 pos, ImColor color, float size, ImFont* font, bool outline = true );

		void render_radar_object( const ImVec2& projection, const float& radius, const ImColor& object_color, const bool& outline, const bool& show_distance, const int& distance, const ImColor& outline_color = ImColor( 0, 0, 0 ), const float& stroke = 1.5f );
		void render_world_object( float& text_padding, const ImVec2& object_position, const std::string& object_name, const float& object_distance, const ImColor& object_color, const bool& display_distance, const bool& display_tier_name = false, const std::string& tier_name = "", const bool& display_box = false, const float& box_width = 0.00f, const float& box_height = 0.00f );

		ImColor get_rbg_color( const float& frequency = 2.00f );
		ImColor get_color( float r, float g, float b, enums::ColorMode mode, const float& frequency = 2.00f );

		auto chinese_hat_points(
			const math::vector3& player_head_position,
			int hat_points,
			float hat_radius,
			float hat_height,
			bool hat_outline,
			bool fading_hat,
			ImColor hat_color
		) -> void
		{
			// 1. Generate the circular base in the X–Y plane
			std::vector<math::vector3> hat_base_points;
			float angle_increment = ( 2.0f * 3.1415926535f ) / hat_points;

			for ( int i = 0; i < hat_points; ++i )
			{
				float angle = i * angle_increment;

				// X and Y form the circle, Z remains at head level
				float x = player_head_position.x + hat_radius * std::cos( angle );
				float y = player_head_position.y + hat_radius * std::sin( angle );
				float z = player_head_position.z;  // same “height” for all base points

				hat_base_points.emplace_back( x, y, z );
			}

			// 2. Tip of the hat goes up along Z, not Y
			math::vector3 hat_tip_point = player_head_position;
			hat_tip_point.z += hat_height;

			// Convert base points to screen space
			std::vector<math::vector2> screen_base_points;
			for ( auto& pt : hat_base_points )
			{
				math::vector2 screen_pt;
				if ( g_engine->world_to_point( pt, &screen_pt ) )
				{
					screen_base_points.push_back( screen_pt );
				}
			}

			// Convert tip to screen space
			math::vector2 screen_tip;
			if ( !g_engine->world_to_point( hat_tip_point, &screen_tip ) )
				return; // tip is offscreen, nothing to draw

			// 3. Draw lines between base points and tip
			for ( size_t i = 0; i < screen_base_points.size( ); ++i )
			{
				// This base point
				const math::vector2& curr = screen_base_points[ i ];
				// The next base point (wrapping around)
				const math::vector2& next = screen_base_points[ ( i + 1 ) % screen_base_points.size( ) ];

				// Draw outline in black if enabled
				if ( hat_outline )
				{
					this->line( ImVec2( curr.x, curr.y ), ImVec2( screen_tip.x, screen_tip.y ), ImColor( 0.f, 0.f, 0.f, hat_color.Value.w ), 1.2f );
					this->line( ImVec2( curr.x, curr.y ), ImVec2( next.x, next.y ), ImColor( 0.f, 0.f, 0.f, hat_color.Value.w ), 1.2f );
				}

				// Main lines for the hat
				this->line( ImVec2( curr.x, curr.y ), ImVec2( screen_tip.x, screen_tip.y ), hat_color, 1.f );
				this->line( ImVec2( curr.x, curr.y ), ImVec2( next.x, next.y ), hat_color, 1.f );
			}
		}

		void bounding_corner( double MostLeft, double DistanceOffset, double MostTop, double CornerWidth, double MostRight, double MostBottom, double CornerHeight, float thickness, ImColor color, ImColor fillbox_color ) {

			if ( g_vars->visuals.boxes_filled )
			{
				ImGui::GetBackgroundDrawList( )->AddRectFilled(
					ImVec2( MostLeft - DistanceOffset, MostTop - DistanceOffset ),
					ImVec2( MostRight + DistanceOffset, MostBottom + DistanceOffset ),
					fillbox_color
				);
			}

			line( ImVec2( MostLeft - DistanceOffset, MostTop - DistanceOffset ), ImVec2( MostLeft - DistanceOffset + CornerWidth, MostTop - DistanceOffset ), color, thickness ); 
			line( ImVec2( MostLeft - DistanceOffset, MostTop - DistanceOffset ), ImVec2( MostLeft - DistanceOffset, MostTop - DistanceOffset + CornerHeight ), color, thickness ); 

			line( ImVec2( MostRight + DistanceOffset, MostTop - DistanceOffset ), ImVec2( MostRight + DistanceOffset - CornerWidth, MostTop - DistanceOffset ), color, thickness ); 
			line( ImVec2( MostRight + DistanceOffset, MostTop - DistanceOffset ), ImVec2( MostRight + DistanceOffset, MostTop - DistanceOffset + CornerHeight ), color, thickness ); 

			line( ImVec2( MostLeft - DistanceOffset, MostBottom + DistanceOffset ), ImVec2( MostLeft - DistanceOffset + CornerWidth, MostBottom + DistanceOffset ), color, thickness );
			line( ImVec2( MostLeft - DistanceOffset, MostBottom + DistanceOffset ), ImVec2( MostLeft - DistanceOffset, MostBottom + DistanceOffset - CornerHeight ), color, thickness ); 

			line( ImVec2( MostRight + DistanceOffset, MostBottom + DistanceOffset ), ImVec2( MostRight + DistanceOffset - CornerWidth, MostBottom + DistanceOffset ), color, thickness ); 
			line( ImVec2( MostRight + DistanceOffset, MostBottom + DistanceOffset ), ImVec2( MostRight + DistanceOffset, MostBottom + DistanceOffset - CornerHeight ), color, thickness );

			if ( g_vars->visuals.boxes_outline ) 
			{
				ImColor outline_color = ImColor( 0, 0, 0 );

				line( ImVec2( MostLeft - DistanceOffset - 1, MostTop - DistanceOffset - 1 ), ImVec2( MostLeft - DistanceOffset - 1 + CornerWidth, MostTop - DistanceOffset - 1 ), outline_color, thickness ); 
				line( ImVec2( MostLeft - DistanceOffset - 1, MostTop - DistanceOffset - 1 ), ImVec2( MostLeft - DistanceOffset - 1, MostTop - DistanceOffset - 1 + CornerHeight ), outline_color, thickness ); 

				line( ImVec2( MostRight + DistanceOffset + 1, MostTop - DistanceOffset - 1 ), ImVec2( MostRight + DistanceOffset + 1 - CornerWidth, MostTop - DistanceOffset - 1 ), outline_color, thickness ); 
				line( ImVec2( MostRight + DistanceOffset + 1, MostTop - DistanceOffset - 1 ), ImVec2( MostRight + DistanceOffset + 1, MostTop - DistanceOffset - 1 + CornerHeight ), outline_color, thickness ); 

				line( ImVec2( MostLeft - DistanceOffset - 1, MostBottom + DistanceOffset + 1 ), ImVec2( MostLeft - DistanceOffset - 1 + CornerWidth, MostBottom + DistanceOffset + 1 ), outline_color, thickness ); 
				line( ImVec2( MostLeft - DistanceOffset - 1, MostBottom + DistanceOffset + 1 ), ImVec2( MostLeft - DistanceOffset - 1, MostBottom + DistanceOffset + 1 - CornerHeight ), outline_color, thickness ); 

				line( ImVec2( MostRight + DistanceOffset + 1, MostBottom + DistanceOffset + 1 ), ImVec2( MostRight + DistanceOffset + 1 - CornerWidth, MostBottom + DistanceOffset + 1 ), outline_color, thickness ); 
				line( ImVec2( MostRight + DistanceOffset + 1, MostBottom + DistanceOffset + 1 ), ImVec2( MostRight + DistanceOffset + 1, MostBottom + DistanceOffset + 1 - CornerHeight ), outline_color, thickness ); 
			}
		}

		void bounding_2d( const math::vector2& position, const math::vector2& size, float thickness, ImColor color, ImColor fillbox_color ) {

			if ( g_vars->visuals.boxes_filled )
			{
				ImGui::GetBackgroundDrawList( )->AddRectFilled(
					ImVec2( position.x, position.y ),
					ImVec2( position.x + size.x, position.y + size.y ),
					fillbox_color
				);
			}

			line( ImVec2( position.x, position.y ), ImVec2( position.x, position.y + size.y ), color, 1.f ); // top-left
			line( ImVec2( position.x, position.y ), ImVec2( position.x + size.x, position.y ), color, 1.f ); // top
			line( ImVec2( position.x + size.x, position.y ), ImVec2( position.x + size.x, position.y + size.y ), color, 1.f ); // top-right
			line( ImVec2( position.x + size.x, position.y + size.y ), ImVec2( position.x, position.y + size.y ), color, 1.f ); // bottom
			line( ImVec2( position.x, position.y + size.y ), ImVec2( position.x, position.y ), color, 1.f ); // bottom-left

			if ( g_vars->visuals.boxes_outline ) 
			{
				ImColor outline_color = ImColor( 0, 0, 0 );

				line( ImVec2( position.x - 1, position.y - 1 ), ImVec2( position.x - 1, position.y + size.y + 1 ), outline_color, thickness );
				line( ImVec2( position.x - 1, position.y - 1 ), ImVec2( position.x + size.x + 1, position.y - 1 ), outline_color, thickness );
				line( ImVec2( position.x + size.x + 1, position.y - 1 ), ImVec2( position.x + size.x + 1, position.y + size.y + 1 ), outline_color, thickness );
				line( ImVec2( position.x + size.x + 1, position.y + size.y + 1 ), ImVec2( position.x - 1, position.y + size.y + 1 ), outline_color, thickness );

				line( ImVec2( position.x + 1, position.y + 1 ), ImVec2( position.x + 1, position.y + size.y - 1 ), outline_color, thickness );
				line( ImVec2( position.x + 1, position.y + 1 ), ImVec2( position.x + size.x - 1, position.y + 1 ), outline_color, thickness );
				line( ImVec2( position.x + size.x - 1, position.y + 1 ), ImVec2( position.x + size.x - 1, position.y + size.y - 1 ), outline_color, thickness );
				line( ImVec2( position.x + size.x - 1, position.y + size.y - 1 ), ImVec2( position.x + 1, position.y + size.y - 1 ), outline_color, thickness );
			}
		}


		void poly_rect( const math::vector3& bottomLeft, const math::vector3& bottomRight, const math::vector3& topLeft, const math::vector3& topRight, float thickness, ImColor boxColor, ImColor fillColor )
		{
			ImVec2 points[ 4 ] = {
				ImVec2( bottomLeft.x, bottomLeft.y ),
				ImVec2( bottomRight.x, bottomRight.y ),
				ImVec2( topRight.x, topRight.y ),
				ImVec2( topLeft.x, topLeft.y )
			};

			if ( g_vars->visuals.boxes_filled ) {
				ImGui::GetBackgroundDrawList( )->AddConvexPolyFilled( points, 4, fillColor );
			}

			if ( g_vars->visuals.boxes_outline ) {

				auto offset = 1.0f;

				ImVec2 offsetPoints[ 4 ];

				offsetPoints[ 0 ] = ImVec2( points[ 0 ].x - offset, points[ 0 ].y - offset );
				offsetPoints[ 1 ] = ImVec2( points[ 1 ].x + offset, points[ 1 ].y - offset );
				offsetPoints[ 2 ] = ImVec2( points[ 2 ].x + offset, points[ 2 ].y + offset );
				offsetPoints[ 3 ] = ImVec2( points[ 3 ].x - offset, points[ 3 ].y + offset );

				ImGui::GetBackgroundDrawList( )->AddPolyline( offsetPoints, 4, ImColor( 0, 0, 0 ), true, thickness );
			}

			ImGui::GetBackgroundDrawList( )->AddPolyline( points, 4, boxColor, true, thickness );
		}

		auto fov( ) -> void
		{
			ImGui::GetBackgroundDrawList( )->AddCircle( ImVec2( g_engine->width_center, g_engine->height_center ), g_vars->aim.fov_c, ImColor( 255, 255, 255 ), 100, 1.f );
		}

		auto load_fonts( IDirect3DDevice9* pDevice ) -> void
		{
			ImGuiIO& io = ImGui::GetIO( ); ( void ) io;

			ImFontConfig font_config;
			font_config.PixelSnapH = false;
			font_config.OversampleH = 5;
			font_config.OversampleV = 5;
			font_config.RasterizerMultiply = 1.2f;

			static const ImWchar ranges[ ] =
			{
				0x0020, 0x00FF, // Basic Latin + Latin Supplement
				0x0400, 0x052F, // Cyrillic + Cyrillic Supplement
				0x2DE0, 0x2DFF, // Cyrillic Extended-A
				0xA640, 0xA69F, // Cyrillic Extended-B
				0xE000, 0xE226, // icons
				0,
			};

			font_config.GlyphRanges = ranges;

			this->menu_font = io.Fonts->AddFontFromMemoryTTF(
				&bits::fonts::roboto_bold_raw,
				sizeof( bits::fonts::roboto_bold_raw ),
				14.0f,
				&font_config,
				io.Fonts->GetGlyphRangesCyrillic( )
			);

			this->esp_font = io.Fonts->AddFontFromMemoryTTF(
				&bits::fonts::geist_medium,
				sizeof( bits::fonts::geist_medium ),
				g_vars->font.size,
				&font_config,
				io.Fonts->GetGlyphRangesCyrillic( )
			);

			this->notifications_font = io.Fonts->AddFontFromMemoryTTF(
				&bits::fonts::notifications,
				sizeof( bits::fonts::notifications ),
				22.0f
			);

			const static ImWchar icons_ranges[ ] = { 0x1 + 1000, 0x1 + 5000, 0 };

			this->icons_font = io.Fonts->AddFontFromMemoryTTF( 
				&icons::icons_binary,
				sizeof( icons::icons_binary ),
				14.0f,
				0,
				icons_ranges
			);
		}
	};

} inline const auto g_framework = std::make_unique<drawing_framework::c_drawing>( );

#endif // ! guard