#ifndef OBJECT_EVENTS_CPP
#define OBJECT_EVENTS_CPP
#include <core/inc.hpp>

using namespace enums;

auto object_event::c_entity::get_network( const game::u_world& world ) -> void
{
    this->uworld = world.get_world( 0x1714D538 );
    if ( !this->uworld ) { g_debug_callback->add_callback( I_INFO3916699, ImColor( 177, 77, 77 ), hash_str( "World Instance was not found during runtime scan" ), hash_str( "Error" ), 6.f ); return; }

    this->game_state = uworld->game_state();
    if ( !game_state ) { g_debug_callback->add_callback( I_INFO3916699, ImColor( 177, 77, 77 ), hash_str( "Game State was not found during runtime scan" ), hash_str( "Error" ), 6.f ); return; }

    this->in_lobby = game_state->is_in_lobby();

    auto game_instance = uworld->game_instance();
    if ( !game_instance->is_valid() ) { g_debug_callback->add_callback( I_INFO3916699, ImColor( 177, 77, 77 ), hash_str( "World Instance was not found during runtime scan" ), hash_str( "Error" ), 6.f ); return; }

    auto localplayer = game_instance->get_local();
    if ( !localplayer->is_valid() ) { g_debug_callback->add_callback( I_INFO3916699, ImColor( 177, 77, 77 ), hash_str( "Local Instance was not found during runtime scan" ), hash_str( "Error" ), 6.f ); return; }

    this->localplayer.local_player = localplayer;

    this->localplayer.player_controller = localplayer->controller();
    this->localplayer.player_pawn = this->localplayer.player_controller->acknowledged_pawn();

    this->view_state = localplayer->view_state().get( 1 );
}

auto object_event::c_entity::get_objects() -> void
{
    this->reset();

    if ( !this->game_state || !this->game_state->is_valid() ) return;

    auto actor_array = game_state->player_array();

    if ( !actor_array.is_valid() ) {
        return;
    }

    auto actor_array_itter = actor_array.get_itter();

    auto acotr_array_size = actor_array_itter.size();
    if ( !acotr_array_size ) {
        return;
    }

    for ( auto index = 0ul; index < acotr_array_size; index++ ) {

        const auto player_state = game::gve<game::a_fort_player_state_athena*>( actor_array_itter, index );
        if ( !player_state )
            continue;

        const auto is_bot = !this->in_lobby && player_state->is_bot();

        const auto player_pawn = player_state->pawn_private();

        if ( player_pawn == this->localplayer.player_pawn )
        {
            localplayer.player_state = player_state;
            localplayer.player_pawn = player_pawn;
            localplayer.team_id = in_lobby ? 0 : player_state->team_id();

            game::a_fort_weapon* weapon = player_pawn->current_weapon();

            if ( weapon->is_valid() )
            {
                localplayer.weapon = weapon;

                game::u_fort_weapon_item_defintion* weapon_data = weapon->weapon_data();

                if ( weapon_data->is_valid() )
                {
                    //struct FDataTableRowHandle { game::data_table* DataTable; game::FName RowName; };

                    //FDataTableRowHandle statHandle = g_vm->read<FDataTableRowHandle>( weapon_data->get_current_class() + 0x128 );

                    //if ( !statHandle.DataTable || !statHandle.RowName.ComparisonIndex ) {
                    //    continue;
                    //}


                    // Definition->GetOffset("WeaponStatHandle") == 0x128
                    //auto weapon_handle =

                    //if ( !weapon_handle.DataTable || !weapon_handle.RowName.ComparisonIndex ) {
                    //    continue;
                    //}

                    //auto fname = statHandle.RowName.ToString();

                    //printf( "weapon row name: %s\n", fname.c_str() );

                    //auto RangedWeaponsTable = weapon_handle.DataTable;
                    //if ( !RangedWeaponsTable ) {
                    //    printf( "invalid datatable.\n" );
                    //    continue;
                    //}

                    //auto RangedWeaponRows = *( TMap<FName, uint8_t*>* )( __int64( RangedWeaponsTable ) + ( RowStructOffset + sizeof( UObject* ) ) ); // because after rowstruct is rowmap


                    //// Step 2: Compute the RowMap offset
                    //constexpr std::uintptr_t RowStructOffset = 0x28;  // Found externally
                    //constexpr std::uintptr_t RowMapOffset = RowStructOffset + sizeof( std::uintptr_t ); // 0x28 + 8 = 0x30

                    //// Step 3: Read the RowMap address
                    //std::uintptr_t RowMapAddress = g_vm->read<std::uintptr_t>( RangedWeaponsTable->get_current_class( ) + RowMapOffset);
                    //if ( !RowMapAddress ) {
                    //    printf( "Invalid RowMap.\n" );
                    //    return;
                    //}

                    //// Step 4: Read TMap<FName, RowDataType*>
                    //game::tmap<game::FName, std::uintptr_t> RowMap;
                    //RowMap.Pairs = g_vm->read<game::tset<game::tpair<game::FName, std::uintptr_t>>>( RowMapAddress );

                    //// Step 5: Iterate through the map and extract rows
                    //auto rows = RowMap.get_all_pairs( RowMapAddress );
                    //for ( auto row : rows ) {
                    //    auto row_name = row.get_key( reinterpret_cast< std::uintptr_t >( &row.First ) ).ToString();
                    //    auto row_data = row.get_value( reinterpret_cast< std::uintptr_t >( &row.Second ) );

                    //    printf( "Weapon: %s -> Data Address: 0x%llX\n", row_name.c_str(), row_data );
                    //}


                    auto get_valid_name = []( const std::string& name ) -> std::string
                    {
                        static const std::string no_item_held = hash_str( "Harvesting Tool" );
                        static const std::string undefined = hash_str( "NULL" );
                        static const std::string unhashed = hash_str( "?" );

                        if ( name.empty() || name.find( undefined ) != std::string::npos )
                            return no_item_held;

                        std::string sanitized_name;
                        std::copy_if( name.begin(), name.end(), std::back_inserter( sanitized_name ),
                            []( char c ) { return c != '?'; } );

                        return sanitized_name.empty() ? no_item_held : sanitized_name;
                    };

                    game::f_text_data* display_name = weapon_data->display_name();

                    if ( display_name ) {
                        localplayer.weapon_name = get_valid_name( display_name->get() );
                    }
                }
            }

            continue;
        }

        const auto is_dying = player_pawn->is_dying();
        if ( is_dying ) continue;

        const auto is_dbno = player_pawn->is_dbno();
        if ( is_dbno && !g_vars->visuals.show_dbno ) continue;

        auto team_index = player_state->team_id();
        if ( this->in_lobby ) team_index = 0;

        const auto is_team = ( team_index == this->localplayer.team_id && team_index != 0 );
        if ( is_team && !g_vars->visuals.show_team ) continue;

        const auto player_mesh = player_pawn->mesh();
        if ( !player_mesh ) continue;

        const auto is_visible = this->uworld->get_current_class() ? player_mesh->is_visible( uworld->get_current_class() ) : false;

        const auto root_component = player_pawn->root_component();
        if ( !root_component ) continue;

        engine::frotator rotation;

        if ( g_vars->visuals.rotating || g_vars->visuals.view_direction ) {
            rotation = g_engine->get_player_rotation( root_component->relative_rotation() );
        }

        auto weapon_info = object::c_weapon_info();

        if ( g_vars->visuals.weapon )
        {
            const auto weapon = player_pawn->current_weapon();

            if ( weapon->is_valid() ) {

                const auto is_reloading = weapon->reloading();
                const auto ammo_count = weapon->ammo_count();
                const auto animation_type = weapon->core_animation_type();

                weapon_info.animation_type = animation_type;
                weapon_info.m_ammo_count = ammo_count;
                weapon_info.m_reloading = is_reloading;

                const auto weapon_data = weapon->weapon_data();

                if ( weapon_data->is_valid() ) 
                {
                    const auto tier = weapon_data->tier();
                    weapon_info.m_tier = tier;

                    auto get_valid_name = []( const std::string& name ) -> std::string
                    {
                        static const std::string no_item_held = hash_str( "Harvesting Tool" );
                        static const std::string undefined = hash_str( "NULL" );
                        static const std::string unhashed = hash_str( "?" );

                        if ( name.empty() || name.find( undefined ) != std::string::npos )
                            return no_item_held;

                        std::string sanitized_name;
                        std::copy_if( name.begin(), name.end(), std::back_inserter( sanitized_name ),
                            []( char c ) { return c != '?'; } );

                        return sanitized_name.empty() ? no_item_held : sanitized_name;
                    };

                    const auto display_name = weapon_data->display_name();
                    if ( display_name ) {
                        weapon_info.m_name = get_valid_name( display_name->get() );
                    }
                }
            }
        }

        auto rank = enums::FRankedProgressReplicatedData();

        if ( g_vars->visuals.rank && !is_bot )
        {
            auto player_habanero = player_state->habanero_component();

            if ( player_habanero ) {
                rank = player_habanero->rank_information();
            }
        }

        std::string platform{};
        std::string username{};

        if ( g_vars->visuals.platform ) {
            platform = player_state->decrypt_platform();
        }


        if ( g_vars->visuals.username ) {
            username = player_state->decrypt_name( this->in_lobby );
        }

        auto kill_score = std::uint32_t();

        if ( g_vars->visuals.kill_count )
        {
            if ( this->in_lobby ) {
                kill_score = 0;
            }
            else {
                kill_score = player_state->kill_score();
            }
        }

        auto flag_info = object::c_movement_mode();
        auto has_flags = ( g_vars->flags.aiming || g_vars->flags.crouched || g_vars->flags.falling || g_vars->flags.flying || g_vars->flags.idle || g_vars->flags.swimming || g_vars->flags.walking );

        if ( has_flags )
        {
            auto movement_mode = player_pawn->character_movement();

            if ( movement_mode ) {

                auto get_movement_flag = []( game::u_character_movement_component* u_character_movement, object::c_movement_mode& movement ) -> void
                {
                    auto movement_mode_flag = u_character_movement->movement_mode();
                    if ( !movement_mode_flag ) 
                        return;

                    switch ( movement_mode_flag )
                    {
                    case enums::EMovementMode::MOVE_None:
                        movement.m_is_idle = true;
                        break;

                    case enums::EMovementMode::MOVE_Walking:
                        movement.m_is_walking = true;
                        break;

                    case enums::EMovementMode::MOVE_Falling:
                        movement.m_is_falling = true;
                        break;

                    case enums::EMovementMode::MOVE_Swimming:
                        movement.m_is_swimming = true;
                        break;

                    case enums::EMovementMode::MOVE_Flying:
                        movement.m_is_flying = true;
                        break;
                    }

                    return;
                };

                get_movement_flag( movement_mode, flag_info );
            }
        }

        if ( g_vars->visuals.chams && !is_team )
        {
            //auto material_array = player_pawn->pawn_materials( );

            //if ( material_array.is_valid( ) ) 
            //{
            //    for ( auto id = 0; id < material_array.size( ); id++ )
            //    {
            //        auto material_instance = material_array.get( id );
            //        if ( !memory::is_valid( material_instance ) )
            //            continue;

            //        auto material_deref = material_instance->get_material( );
            //        if ( !memory::is_valid( material_deref ) )
            //            continue;

            //        material_deref->BlendMode( EBlendMode::BLEND_Additive );
            //        material_deref->bDisableDepthTest( 1 );
            //        material_deref->AllowTranslucentCustomDepthWrites( 1 );
            //        material_deref->bIsBlendable( 1 );
            //    }
            //}

            //player_mesh->bRenderCustomDepth( 1 );


            auto do_chams = [ & ]() -> void
            {
                auto mesh_components = player_pawn->character_part_skeletal_mesh_components();

                for ( int id = 0; id < 7; id++ ) {

                    auto component = mesh_components.component[ id ];
                    if ( !memory::is_valid( component->get_current_class( ) ) )
                        continue;

                    auto current_context = component->get_highlight_context();
                    auto selected_context = component->get_material( g_vars->visuals.selected_material );

                    if ( current_context != selected_context )
                    {
                        component->set_highlight_context( selected_context );
                        component->set_field_depth( 1 );
                        component->set_replication( 1 );
                    }
                }
            };

            do_chams();
        }

        object::c_list list_holder;

        list_holder.m_root_component = root_component;
        list_holder.m_state = player_state;
        list_holder.m_actor = player_pawn;
        list_holder.m_mesh = player_mesh;
        list_holder.m_username = username;
        list_holder.m_platform = platform;
        list_holder.m_kill_count = kill_score;
        list_holder.m_team_index = team_index;
        list_holder.m_is_teammate = is_team;
        list_holder.is_bot = is_bot;
        list_holder.is_dbno = is_dbno;
        list_holder.is_visible = is_visible;
        list_holder.rotation = rotation;

        list_holder.m_movement = flag_info;
        list_holder.m_rank_information = rank;
        list_holder.m_weapon_info = weapon_info;

        this->m_temp_players.insert(
            m_temp_players.end(),
            list_holder
        );
    }

    if ( in_lobby )
    {
        if ( !uworld->is_valid() ) 
            return;

        auto persistent_level = uworld->persistent_level();
        if ( !persistent_level->is_valid() )
            return;

        auto actors_array = persistent_level->actors();
        if ( !actors_array.is_valid() )
            return;

        auto actors_array_itter = actors_array.get_itter();

        auto actors_array_size = actors_array_itter.size();
        if ( !actors_array_size )
            return;

        for ( auto idx = 0; idx < actors_array_size; idx++ )
        {
            auto object = game::gve<game::a_fort_player_pawn*>( actors_array_itter, idx );
            if ( !object->is_valid() )
                continue;

            std::string name_private = object->name_private().ToString();
            if ( name_private.find( hash_str( "PlayerPawn" ) ) == std::string::npos )
                continue;

            const auto player_mesh = object->mesh();
            if ( !player_mesh )
                continue;

            const auto player_state = object->player_state();
            if ( player_state )
                continue;

            object::c_weapon_info weapon;

            weapon.m_name = hash_str( "Harvesting Tool" );
            weapon.m_ammo_count = 0;
            weapon.m_reloading = false;
            weapon.m_tier = enums::fort_rarity::Common;

            object::c_list list_holder;

            list_holder.m_mesh = player_mesh;
            list_holder.m_username = hash_str( "Teammate" );
            list_holder.m_platform = hash_str( "WIN" );
            list_holder.m_kill_count = 0;
            list_holder.m_team_index = 0;
            list_holder.m_is_teammate = false;
            list_holder.is_bot = false;
            list_holder.is_dbno = false;
            list_holder.is_visible = true;

            list_holder.m_weapon_info = weapon;
            list_holder.m_rank_information.Rank = 18;

            this->m_temp_players.insert(
                m_temp_players.end(),
                list_holder
            );
        }
    }

    this->m_players.swap( m_temp_players );
}

auto object_event::c_entity::get_levels() -> void
{
    this->reset_world();

    if ( !this->uworld->is_valid() ) return;

    auto levels_array = this->uworld->levels_array();

    if ( !levels_array.is_valid() ) {
        return;
    }

    auto level_array_itter = levels_array.get_itter();

    auto level_array_size = level_array_itter.size();

    if ( !level_array_size || level_array_size > 4096 ) {
        return;
    }

    for ( auto index = 0; index < level_array_size; index++ ) {

        auto level = game::gve<game::u_level*>( level_array_itter, index );
        if ( !memory::is_valid( level->get_current_class() ) ) {
            continue;
        }

        auto actors_array = level->actors();
        if ( !actors_array.is_valid() ) {
            continue;
        }

        auto actors_array_itter = actors_array.get_itter();
        auto actors_array_size = actors_array_itter.size();

        if ( !actors_array_size || actors_array_size > 4096 ) {
            return;
        }

        for ( auto idx = 0; idx < actors_array_size; idx++ )
        {
            auto object = game::gve<game::a_fort_player_pawn*>( actors_array_itter, idx );
            if ( !memory::is_valid( object->get_current_class() ) ) {
                continue;
            }

            std::string name_private = object->name_private().ToString();

            if ( g_vars->exploits.sniper_bullet_tp && name_private.find( hash_str( "B_Prj_Bullet" ) ) != std::string::npos
                || name_private.find( hash_str( "B_Prj_Arrow" ) ) != std::string::npos
                || name_private.find( hash_str( "OnRep_FireStart" ) ) != std::string::npos
                )
            {
                auto do_bullet_tp = [ & ]() -> void
                {
                    const object::c_closest& closest_actor = g_aimbot->closest_actor;
                    if ( !closest_actor.m_mesh->is_valid() ) return;

                    auto root_component = object->root_component();
                    if ( !root_component->is_valid() ) return;

                    const auto& target_mesh = closest_actor.m_mesh;
                    if ( !target_mesh->is_valid() ) return;

                    const auto& head_location = g_engine->get_bone( target_mesh->get_current_class(), c_bones::head );
                    if ( !head_location.is_valid() ) return;

                    object->set_FireStartLoc( head_location );
                    root_component->set_relative_transform( head_location );
                    root_component->set_component_velocity( math::vector3( 0, 0, 0 ) );
                };

                do_bullet_tp();
            }
            
            std::pair<bool, enums::ItemGroup> should_cache{};

            //if ( g_vars->exploits.vehicle_teleport && g_vars->exploits.vehicle_tp_key == 0 &&
            //    name_private.find( hash_str( "B_AthenaPlayerMarker_WithCustomization_C" ) ) != std::string::npos )
            //{
            //    should_cache = { g_vars->loot.s.ammo_boxes, enums::ItemGroup::Marker };

            //    //auto store_waypoint = [ & ]() -> void
            //    //{
            //    //    auto root_component = object->root_component();
            //    //    if ( !root_component->is_valid() ) return;

            //    //    math::vector3 transform_location = root_component->get_relative_transform();

            //    //    g_engine->waypoint = math::vector3(
            //    //        transform_location.x,
            //    //        transform_location.y,
            //    //        transform_location.z + 30
            //    //    );
            //    //};

            //    //store_waypoint();
            //}

            if ( name_private.find( hash_str( "Tiered_Chest" ) ) != std::string::npos ) 
            {
                // ABuildingContainer.bAlreadySearched
                auto is_searched = bool( g_vm->read<char>( object->get_current_class( ) + 0xe02 ) & ( 1 << 2 ));

                if ( !is_searched )
                    should_cache = { g_vars->loot.s.chests, enums::ItemGroup::Searchables };
            }

            if ( name_private.find( hash_str( "Tiered_Ammo" ) ) != std::string::npos ) 
            {
                // ABuildingContainer.bAlreadySearched
                auto is_searched = bool( g_vm->read<char>( object->get_current_class() + 0xe02 ) & ( 1 << 2 ) );

                if ( !is_searched )
                    should_cache = { g_vars->loot.s.ammo_boxes, enums::ItemGroup::Searchables };
            }

            if ( name_private.find( hash_str( "AthenaSupplyDrop_Llama_C" ) ) != std::string::npos )
                should_cache = { g_vars->loot.s.lammas, enums::ItemGroup::Searchables };

            if ( name_private.find( hash_str( "B_SpecialSupplyDrop_Base_BlastBerry_C" ) ) != std::string::npos )
                should_cache = { g_vars->loot.s.airdrops, enums::ItemGroup::Searchables };

            if ( name_private.find( hash_str( "Motorcycle_Sport_Vehicle_C" ) ) != std::string::npos )
                should_cache = { g_vars->loot.v.bikes, enums::ItemGroup::Vehicles };

            if ( name_private.find( hash_str( "Valet_BasicSUV_Vehicle_C" ) ) != std::string::npos )
                should_cache = { g_vars->loot.v.sedans, enums::ItemGroup::Vehicles };

            if ( name_private.find( hash_str( "WeakSpot_C" ) ) != std::string::npos )
            {
                // ABuildingWeakSpot::bHit
                auto is_hit = bool( g_vm->read<char>( object->get_current_class() + 0x2d8 ) & ( 1 << 0 ) );
                
                // ABuildingWeakSpot::bActive
                auto is_active = bool( g_vm->read<char>( object->get_current_class() + 0x2d8 ) & ( 1 << 2 ) );

                if ( !is_hit && is_active )
                    should_cache = { g_vars->loot.w.weakspots, enums::ItemGroup::World };
            }

            if ( name_private.find( hash_str( "FortPickupAthena" ) ) != std::string::npos )
                should_cache = { g_vars->loot.show_dropped, enums::ItemGroup::Dropped };

            if ( should_cache.first )
            {
                auto root_component = object->root_component();
                if ( !root_component->is_valid() ) continue;

                auto relative_location = root_component->relative_location();
                if ( relative_location.is_empty() ) continue;

                int distance = g_engine->location.distance_to( relative_location );

                auto get_pickup_info = [ & ]() -> object::pickup_info
                {
                    auto pickup_entry = object->pickup_entry();
                    if ( !pickup_entry->is_valid() ) return object::pickup_info();

                    auto display_name = pickup_entry->display_name();
                    if ( !display_name ) return object::pickup_info();

                    enums::fort_rarity actor_tier = pickup_entry->tier();
                    std::string actor_name = display_name->get();

                    object::pickup_info pickup;

                    pickup.actor_tier = actor_tier;
                    pickup.actor_name = actor_name;

                    return pickup;
                };

                object::c_world list_holder;

                list_holder.pickup_info = should_cache.second == enums::ItemGroup::Dropped ? get_pickup_info() : object::pickup_info();

                switch ( should_cache.second )
                {
                case enums::ItemGroup::Searchables:
                    if ( distance > g_vars->loot.s.searchables_render_distance ) continue;
                    break;
                case enums::ItemGroup::Dropped:
                {
                    if ( distance > g_vars->loot.d.dropped_render_distance ) continue;

                    if ( g_vars->loot.d.sort_dropped_rarity )
                    {
                        switch ( list_holder.pickup_info.actor_tier )
                        {
                        case enums::fort_rarity::Uncommon:
                            if ( !g_vars->loot.d.uncommon ) continue;
                            break;
                        case enums::fort_rarity::Common:
                            if ( !g_vars->loot.d.common ) continue;
                            break;
                        case enums::fort_rarity::Rare:
                            if ( !g_vars->loot.d.rare ) continue;
                            break;
                        case enums::fort_rarity::Epic:
                            if ( !g_vars->loot.d.epic ) continue;
                            break;
                        case enums::fort_rarity::Legendary:
                            if ( !g_vars->loot.d.legendary ) continue;
                            break;
                        }
                    }

                    break;
                }
                case enums::ItemGroup::World:
                    if ( distance > g_vars->loot.w.world_render_distance ) continue;
                    break;
                case enums::ItemGroup::Vehicles:
                    if ( distance > g_vars->loot.v.vehicles_render_distance ) continue;
                    break;
                }

                list_holder.name_private = name_private;
                list_holder.actor = object;
                list_holder.root_component = root_component;
                list_holder.relative_location = relative_location;
                list_holder.distance = distance;
                list_holder.item_group = should_cache.second;

                this->m_temp_world.insert(
                    m_temp_world.end(),
                    list_holder
                );
            }
        }
    }

    this->m_world_items.swap( m_temp_world );
}

auto object_event::c_entity::get_spectators() -> void
{
    if ( !g_vars->visuals.spectators || !localplayer.player_state )
        return;

    this->reset_spectators();

    auto spectators = localplayer.player_state->spectators();
    if ( !spectators.is_valid() )
        return;

    auto spectators_size = spectators.size();
    if ( !spectators_size )
        return;

    for ( auto idx = 0; idx < spectators_size; idx++ )
    {
        auto player_state = g_vm->read<game::a_fort_player_state*>( spectators.get_addr() + ( idx * 0x18 ) + 0x10 );
        if ( !player_state )
            continue;

        auto username = player_state->decrypt_name( false );
        if ( username.empty() )
            continue;

        object::c_spectator list_holder;

        list_holder.index = idx;
        list_holder.name = username;

        this->m_temp_spectators.insert(
            m_temp_spectators.end(),
            list_holder
        );
    }

    this->m_spectators.swap( m_temp_spectators );
}

#endif // ! guard