#ifndef MISC_CPP
#define MISC_CPP

#include <core/inc.hpp>

void misc::c_misc::first_tick( )
{
	constexpr unsigned int misc_update_interval = 1;
	auto last_update_tick = std::chrono::steady_clock::now( );

	constexpr std::array<unsigned int, 1> intervals = {
		misc_update_interval
	};

	constexpr unsigned int lowest_interval = *std::min_element( intervals.begin( ), intervals.end( ) );

	for ( ;; )
	{
		{
			this->update_function( misc_update_interval, last_update_tick, [ & ] {
				this->cache_misc( );

				} );

			std::this_thread::sleep_for( std::chrono::milliseconds( lowest_interval ) );
		}
	}
}

auto misc::c_misc::cache_misc( ) -> void
{
	auto& local_player_info = g_ctx->localplayer;

	auto local_player = local_player_info.local_player;
	if ( !local_player->is_valid( ) ) {
		return;
	}

	if ( !local_player_info.player_pawn->is_valid( ) ) return;

	game::a_fort_weapon* player_weapon = local_player_info.player_pawn->current_weapon();

	if ( player_weapon->is_valid() )
	{
		local_player_info.animation_type = player_weapon->core_animation_type();
	}

	static auto applied_sky = false;

	if ( g_vars->exploits.dark_sky )
	{
		auto viewport = local_player_info.local_player->viewport( );

		if ( viewport->is_valid( ) && !applied_sky ) {
			viewport->set_fog_cast( 1 ); applied_sky = true;
		}
	}
	else if ( !g_vars->exploits.dark_sky && applied_sky ) 
	{
		auto viewport = local_player_info.local_player->viewport( );

		if ( viewport->is_valid( ) ) {
			viewport->set_fog_cast( 3 ); applied_sky = false;
		}
	}

	game::a_fort_athena_vehicle* player_vehicle = local_player_info.player_pawn->current_vehicle( );

	if ( player_vehicle->is_valid( ) )
	{
		game::a_fort_physics_vehicle* fort_physics_conf = player_vehicle->fort_physics_vehicle_config( );

		if ( g_vars->exploits.disable_car_gravity )
		{
			if ( fort_physics_conf->is_valid( ) ) 
			{
				fort_physics_conf->vehicle_gravity( 0.0f );
			}
		}

		if ( g_vars->exploits.vehicle_airstuck )
		{
			static auto active = false;

			if ( GetAsyncKeyState( g_vars->exploits.key_vehicle_airstuck ) ) {
				fort_physics_conf->bIsVehicleKinematic( true );
				active = true;
			}
			else if ( active ) {
				active = false;
				fort_physics_conf->bIsVehicleKinematic( false );
			}
		}

		if ( g_vars->exploits.remove_car_animations )
		{
			if ( fort_physics_conf->is_valid( ) ) 
			{
				fort_physics_conf->exit_vehicle_cooldown( 0.00f );
				fort_physics_conf->VehicleFlipCooldown( 0.00f );
				fort_physics_conf->TimeBetweenShallowWaterLevelUpdates( 10000.00f );
				player_vehicle->seat_cooldown( 0.00f );
			}
		}

		if ( g_vars->exploits.vehicle_teleport )
		{
			switch ( g_vars->exploits.vehicle_tp_type )
			{
			case 0:
			{
				auto do_teleport = [ & ] ( ) -> void
				{
					if ( !g_engine->waypoint.is_valid( ) ) return;

					if ( !GetAsyncKeyState( g_vars->exploits.vehicle_tp_key ) ) return;

					auto vehicle_root_component = player_vehicle->root_component( );
					if ( !vehicle_root_component->is_valid( ) || !fort_physics_conf->is_valid( ) ) return;

					fort_physics_conf->teleport_to(
						g_engine->waypoint,
						vehicle_root_component,
						TRUE
					);
				};

				do_teleport( );
				break;
			}

			case 1:
			{
				auto do_teleport = [ & ] ( ) -> void
				{
					if ( !g_aimbot->closest_actor.m_actor ) return;

					if ( !GetAsyncKeyState( g_vars->exploits.vehicle_tp_key ) ) return;

					const auto& target_root_component = g_aimbot->closest_actor.m_actor->root_component( );
					if ( !target_root_component->is_valid( ) ) return;

					const auto& target_location = target_root_component->get_relative_transform( );

					auto vehicle_root_component = player_vehicle->root_component( );
					if ( !vehicle_root_component->is_valid( ) || !fort_physics_conf->is_valid( ) ) return;

					fort_physics_conf->teleport_to(
						target_location,
						vehicle_root_component,
						FALSE
					);
				};

				do_teleport( );
				break;
			}

			default:
				break;
			}
		}

		if ( g_vars->exploits.infinite_fuel )
		{
			game::a_fort_vehicle_fuel_component* cached_fuel_component = player_vehicle->cached_fuel_component( );

			if ( cached_fuel_component->is_valid( ) ) {
				cached_fuel_component->server_fuel( FLT_MAX );
				cached_fuel_component->infinite_fuel( FLT_MAX );
			}
		}

		if ( g_vars->exploits.bike_jump )
		{
			game::a_fort_motorcycle_vehicle* motor_vehicle_conf = player_vehicle->motorcycle_vehicle_config( );

			if ( motor_vehicle_conf->is_valid( ) ) {
				motor_vehicle_conf->max_charge_jump_force( g_vars->exploits.max_jump_force );
				motor_vehicle_conf->max_jump_forward_angle( g_vars->exploits.max_jump_forward );

				motor_vehicle_conf->max_jump_charge_time( 0.0f );
				motor_vehicle_conf->jump_charge_grace_time_seconds( 0.0f );
				motor_vehicle_conf->jump_grace_time_seconds( 0.0f );
			}
		}

		if ( g_vars->exploits.car_fly )
		{
			if ( GetAsyncKeyState( g_vars->exploits.key_up ) ) {
				player_vehicle->set_VehicleGravityScale( 10 * -1 );
			}
			else if ( GetAsyncKeyState( g_vars->exploits.key_down ) ) {
				player_vehicle->set_VehicleGravityScale( 10 );
			}
			else {
				player_vehicle->set_VehicleGravityScale( 1 );
			}
		}

		if ( g_vars->exploits.vehicle_speedhack )
		{
			if ( GetAsyncKeyState( 0x57 ) ) {
				player_vehicle->set_ForwardForceTractionScale( 5.00f );
				player_vehicle->set_TopSpeedScale( g_vars->exploits.vehicle_speed );
			}
			else {
				player_vehicle->set_ForwardForceTractionScale( 1.00f );
				player_vehicle->set_TopSpeedScale( 1.00f );
			}
		}

		if ( g_vars->exploits.instant_brakes )
		{
			player_vehicle->set_BrakeForceTractionScale( 3.00f );
		}

		if ( g_vars->exploits.grippy_cars )
		{
			player_vehicle->set_FrontLateralFrictionScale( 3.00f );
			player_vehicle->set_RearLateralFrictionScale( 3.00f );
		}
	}

	if ( g_vars->exploits.fov_changer || g_vars->exploits.zoom )
	{
		auto smoothness = g_vars->exploits.zoom_speed;
		static float current_fov = 90.f;

		auto set_fov_value = 90.f;

		if ( g_vars->exploits.zoom && GetAsyncKeyState( g_vars->exploits.zoom_keycode ) )
		{
			set_fov_value = g_vars->exploits.zoom_value;
		}
		else if ( g_vars->exploits.fov_changer )
		{
			set_fov_value = g_vars->exploits.camera_fov;
		}

		current_fov += ( set_fov_value - current_fov ) * smoothness;

		float applied_fov = g_vars->exploits.camera_zoom_effect ? current_fov : set_fov_value;

		game::a_player_controller* controller = local_player->controller( );

		if ( controller->is_valid( ) ) {
			controller->fov_min( applied_fov );
			controller->fov_max( applied_fov );
		}

		game::a_fort_weapon* player_weapon = local_player_info.player_pawn->current_weapon( );
		if ( player_weapon->is_valid( ) ) {

			game::u_fort_weapon_optic_definition* optic_def = player_weapon->optic_definition( );
			if ( optic_def->is_valid( ) ) {
				optic_def->set_FieldOfView( g_vars->exploits.ads_fov );
			}
		}
	}

	if ( g_vars->misc.movement_tracers )
	{
		auto do_tracers = [ & ] ( ) -> void
		{
			bool is_found = false;

			std::chrono::steady_clock::time_point time_point = std::chrono::high_resolution_clock::now( );

			if ( !local_player_info.player_pawn->is_valid( ) ) return;

			game::u_skeletal_mesh_component* mesh = local_player_info.player_pawn->mesh( );
			math::vector3 root_bone = g_engine->get_bone( mesh->get_current_class( ), 0 );

			std::unique_lock<std::mutex> lock( movement_mutex );

			for ( const auto& it : this->g_movement_positions )
			{
				if ( it.position == root_bone ) {
					is_found = true;
					break;
				}
			}

			if ( !is_found )
			{
				object::movements movement;

				movement.position = root_bone;
				movement.timestamp = time_point;

				this->g_movement_positions.emplace_back( movement );
			}

			lock.unlock( );
		};

		do_tracers( );
	}

	if ( g_vars->misc.bullet_tracers )
	{
		auto get_tracers = [ & ] ( ) -> void
		{
			math::vector3 hit_position = g_ctx->localplayer.player_pawn->get_LastFiredDirection( );
			if ( hit_position.is_empty( ) || !hit_position.is_valid( ) ) return;

			auto local_mesh = g_ctx->localplayer.player_pawn->mesh( );
			if ( !local_mesh->is_valid( ) ) return;

			auto local_head_bone = g_engine->get_bone( local_mesh->get_current_class( ), enums::c_bones::head );
			if ( !local_head_bone ) return;

			local_head_bone.z += 18.f;

			std::chrono::steady_clock::time_point time_point = std::chrono::high_resolution_clock::now( );

			object::projcetiles projectile;
			projectile.start_point = local_head_bone;
			projectile.hit_point = hit_position;
			projectile.timestamp = time_point;

			std::unique_lock<std::mutex> lock( projectiles_mutex );
			this->g_projectiles.emplace_back( projectile );
			lock.unlock( );
		};

		get_tracers( );
	}

}

#endif // !MISC_CPP
