use crate::ecs::world::World;

pub type SystemFn = fn(&mut World);

#[derive(Clone)]
pub struct FrameScheduleEntry {
    pub name: &'static str,
    pub system: SystemFn,
}

#[derive(Clone, Default)]
pub struct FrameSchedule {
    pub entries: Vec<FrameScheduleEntry>,
}

impl FrameSchedule {
    pub fn push(&mut self, name: &'static str, system: SystemFn) {
        self.entries.push(FrameScheduleEntry { name, system });
    }

    pub fn insert_before(&mut self, target: &str, name: &'static str, system: SystemFn) {
        let position = self
            .entries
            .iter()
            .position(|entry| entry.name == target)
            .unwrap_or_else(|| {
                panic!("FrameSchedule::insert_before: no system named \"{target}\"")
            });
        self.entries
            .insert(position, FrameScheduleEntry { name, system });
    }

    pub fn insert_after(&mut self, target: &str, name: &'static str, system: SystemFn) {
        let position = self
            .entries
            .iter()
            .position(|entry| entry.name == target)
            .unwrap_or_else(|| panic!("FrameSchedule::insert_after: no system named \"{target}\""))
            + 1;
        self.entries
            .insert(position, FrameScheduleEntry { name, system });
    }

    pub fn remove(&mut self, name: &str) {
        self.entries.retain(|entry| entry.name != name);
    }

    pub fn contains(&self, name: &str) -> bool {
        self.entries.iter().any(|entry| entry.name == name)
    }

    pub fn run(&self, world: &mut World) {
        let _span = tracing::info_span!("systems").entered();
        for entry in &self.entries {
            (entry.system)(world);
        }
    }
}

pub mod system_names {
    pub const INITIALIZE_AUDIO: &str = "initialize_audio";
    pub const UPDATE_AUDIO: &str = "update_audio";
    pub const UPDATE_CAMERA_ASPECT_RATIOS: &str = "update_camera_aspect_ratios";
    pub const RUN_PHYSICS: &str = "run_physics";
    pub const RUN_SCRIPTS: &str = "run_scripts";
    pub const UPDATE_TWEENS: &str = "update_tweens";
    pub const UPDATE_ANIMATION_PLAYERS: &str = "update_animation_players";
    pub const APPLY_ANIMATIONS: &str = "apply_animations";
    pub const TRANSFORM_SYSTEMS: &str = "transform_systems";
    pub const UPDATE_INSTANCED_MESH_CACHES: &str = "update_instanced_mesh_caches";
    pub const UI_RETAINED_INPUT_SYNC: &str = "ui_retained_input_sync";
    pub const UI_LAYOUT_PICKING: &str = "ui_layout_picking";
    pub const UI_WIDGET_INTERACTION: &str = "ui_widget_interaction";
    pub const UI_COMPOSITE_UPDATE: &str = "ui_composite_update";
    pub const UI_EVENT_BUBBLE: &str = "ui_event_bubble";
    pub const UI_EVENT_DISPATCH: &str = "ui_event_dispatch";
    pub const UI_LAYOUT_STATE_UPDATE: &str = "ui_layout_state_update";
    pub const UI_DOCKED_PANEL_LAYOUT: &str = "ui_docked_panel_layout";
    pub const UI_LAYOUT_COMPUTE: &str = "ui_layout_compute";
    pub const UI_THEME_APPLY: &str = "ui_theme_apply";
    pub const UI_LAYOUT_COLOR_BLEND: &str = "ui_layout_color_blend";
    pub const UI_LAYOUT_RENDER_SYNC: &str = "ui_layout_render_sync";
    pub const SYNC_HUD_TEXT: &str = "sync_hud_text";
    pub const RESET_MOUSE: &str = "reset_mouse";
    pub const RESET_KEYBOARD: &str = "reset_keyboard";
    pub const RESET_TOUCH: &str = "reset_touch";
    pub const PROCESS_COMMANDS: &str = "process_commands";
    pub const CLEANUP_UNUSED_RESOURCES: &str = "cleanup_unused_resources";
    pub const POLL_FILE_WATCHER: &str = "poll_file_watcher";
    pub const POLL_ASSET_WATCHER: &str = "poll_asset_watcher";
}

pub fn build_default_frame_schedule() -> FrameSchedule {
    let mut schedule = FrameSchedule::default();

    #[cfg(feature = "audio")]
    {
        schedule.push(
            system_names::INITIALIZE_AUDIO,
            crate::ecs::audio::systems::initialize_audio_system,
        );
        schedule.push(
            system_names::UPDATE_AUDIO,
            crate::ecs::audio::systems::update_audio_system,
        );
    }

    schedule.push(
        system_names::UPDATE_CAMERA_ASPECT_RATIOS,
        crate::ecs::camera::systems::update_camera_aspect_ratios,
    );

    #[cfg(feature = "physics")]
    schedule.push(
        system_names::RUN_PHYSICS,
        crate::ecs::physics::systems::run_physics_systems,
    );

    #[cfg(feature = "scripting")]
    schedule.push(
        system_names::RUN_SCRIPTS,
        crate::ecs::script::systems::run_scripts_system,
    );

    schedule.push(
        system_names::UPDATE_TWEENS,
        crate::ecs::tween::systems::tween_system,
    );
    schedule.push(
        system_names::UPDATE_ANIMATION_PLAYERS,
        crate::ecs::animation::systems::update_animation_players,
    );
    schedule.push(
        system_names::APPLY_ANIMATIONS,
        crate::ecs::animation::systems::apply_animations,
    );
    schedule.push(
        system_names::TRANSFORM_SYSTEMS,
        crate::ecs::transform::systems::run_systems,
    );
    schedule.push(
        system_names::UPDATE_INSTANCED_MESH_CACHES,
        crate::ecs::mesh::systems::update_instanced_mesh_caches_system,
    );
    schedule.push(
        system_names::UI_RETAINED_INPUT_SYNC,
        crate::ecs::ui::widget_systems::ui_retained_input_sync_system,
    );
    schedule.push(
        system_names::UI_LAYOUT_PICKING,
        crate::ecs::ui::picking::ui_layout_picking_system,
    );
    schedule.push(
        system_names::UI_WIDGET_INTERACTION,
        crate::ecs::ui::widget_systems::ui_widget_interaction_system,
    );
    schedule.push(
        system_names::UI_COMPOSITE_UPDATE,
        crate::ecs::ui::composite::ui_composite_update_system,
    );
    schedule.push(
        system_names::UI_EVENT_BUBBLE,
        crate::ecs::ui::widget_systems::ui_event_bubble_system,
    );
    schedule.push(
        system_names::UI_EVENT_DISPATCH,
        crate::ecs::ui::widget_systems::ui_event_dispatch_system,
    );
    schedule.push(
        system_names::UI_LAYOUT_STATE_UPDATE,
        crate::ecs::ui::systems::ui_layout_state_update_system,
    );
    schedule.push(
        system_names::UI_DOCKED_PANEL_LAYOUT,
        crate::ecs::ui::systems::ui_docked_panel_layout_system,
    );
    schedule.push(
        system_names::UI_LAYOUT_COMPUTE,
        crate::ecs::ui::systems::ui_layout_compute_system,
    );
    schedule.push(
        system_names::UI_THEME_APPLY,
        crate::ecs::ui::systems::ui_theme_apply_system,
    );
    schedule.push(
        system_names::UI_LAYOUT_COLOR_BLEND,
        crate::ecs::ui::systems::ui_layout_color_blend_system,
    );
    schedule.push(
        system_names::UI_LAYOUT_RENDER_SYNC,
        crate::ecs::ui::render_sync::ui_layout_render_sync_system,
    );
    schedule.push(
        system_names::SYNC_HUD_TEXT,
        crate::ecs::text::systems::sync_hud_text_system,
    );
    schedule.push(
        system_names::RESET_MOUSE,
        crate::ecs::input::systems::reset_mouse_system,
    );
    schedule.push(
        system_names::RESET_KEYBOARD,
        crate::ecs::input::systems::reset_keyboard_system,
    );
    schedule.push(
        system_names::RESET_TOUCH,
        crate::ecs::input::systems::reset_touch_system,
    );
    #[cfg(all(feature = "file_watcher", not(target_arch = "wasm32")))]
    schedule.push(
        system_names::POLL_FILE_WATCHER,
        crate::ecs::file_watcher::poll_file_watcher_system,
    );
    #[cfg(all(
        feature = "assets",
        feature = "file_watcher",
        not(target_arch = "wasm32")
    ))]
    schedule.push(
        system_names::POLL_ASSET_WATCHER,
        crate::ecs::asset_watcher::poll_asset_watcher_system,
    );
    schedule.push(
        system_names::PROCESS_COMMANDS,
        crate::ecs::world::process_commands_system,
    );
    schedule.push(
        system_names::CLEANUP_UNUSED_RESOURCES,
        crate::ecs::world::cleanup_unused_resources_system,
    );

    schedule
}

pub fn run_retained_ui_systems(world: &mut World) {
    crate::ecs::ui::widget_systems::ui_retained_input_sync_system(world);
    crate::ecs::ui::picking::ui_layout_picking_system(world);
    crate::ecs::ui::widget_systems::ui_widget_interaction_system(world);
    crate::ecs::ui::widget_systems::ui_property_sync_system(world);
    crate::ecs::ui::composite::ui_composite_update_system(world);
    crate::ecs::ui::widget_systems::ui_event_bubble_system(world);
    crate::ecs::ui::widget_systems::ui_event_dispatch_system(world);
    crate::ecs::ui::systems::ui_layout_state_update_system(world);
    crate::ecs::ui::systems::ui_docked_panel_layout_system(world);
    crate::ecs::ui::systems::ui_layout_compute_system(world);
    crate::ecs::ui::systems::ui_theme_apply_system(world);
    crate::ecs::ui::systems::ui_layout_color_blend_system(world);
    crate::ecs::ui::render_sync::ui_layout_render_sync_system(world);
}
