//! eframe - the [`egui`] framework crate
//!
//! If you are planning to write an app for web or native,
//! and want to use [`egui`] for everything, then `eframe` is for you!
//!
//! To get started, see the [examples](https://github.com/emilk/egui/tree/main/examples).
//! To learn how to set up `eframe` for web and native, go to <https://github.com/emilk/eframe_template/> and follow the instructions there!
//!
//! In short, you implement [`App`] (especially [`App::ui`]) and then
//! call [`crate::run_native`] from your `main.rs`, and/or use `eframe::WebRunner` from your `lib.rs`.
//!
//! ## Compiling for web
//! You need to install the `wasm32` target with `rustup target add wasm32-unknown-unknown`.
//!
//! Build the `.wasm` using `cargo build --target wasm32-unknown-unknown`
//! and then use [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) to generate the JavaScript glue code.
//!
//! See the [`eframe_template` repository](https://github.com/emilk/eframe_template/) for more.
//!
//! ## Simplified usage
//! If your app is only for native, and you don't need advanced features like state persistence,
//! then you can use the simpler function [`run_ui_native`].
//!
//! ## Usage, native:
//! ``` no_run
//! use eframe::egui;
//!
//! fn main() {
//!     let native_options = eframe::NativeOptions::default();
//!     eframe::run_native("My egui App", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))));
//! }
//!
//! #[derive(Default)]
//! struct MyEguiApp {}
//!
//! impl MyEguiApp {
//!     fn new(cc: &eframe::CreationContext<'_>) -> Self {
//!         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.
//!         // Restore app state using cc.storage (requires the "persistence" feature).
//!         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
//!         // for e.g. egui::PaintCallback.
//!         Self::default()
//!     }
//! }
//!
//! impl eframe::App for MyEguiApp {
//!    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
//!        egui::CentralPanel::default().show(ui, |ui| {
//!            ui.heading("Hello World!");
//!        });
//!    }
//! }
//! ```
//!
//! ## Usage, web:
//! ``` no_run
//! # #[cfg(target_arch = "wasm32")]
//! use wasm_bindgen::prelude::*;
//!
//! /// Your handle to the web app from JavaScript.
//! # #[cfg(target_arch = "wasm32")]
//! #[derive(Clone)]
//! #[wasm_bindgen]
//! pub struct WebHandle {
//!     runner: eframe::WebRunner,
//! }
//!
//! # #[cfg(target_arch = "wasm32")]
//! #[wasm_bindgen]
//! impl WebHandle {
//!     /// Installs a panic hook, then returns.
//!     #[expect(clippy::new_without_default)]
//!     #[wasm_bindgen(constructor)]
//!     pub fn new() -> Self {
//!         // Redirect [`log`] message to `console.log` and friends:
//!         eframe::WebLogger::init(log::LevelFilter::Debug).ok();
//!
//!         Self {
//!             runner: eframe::WebRunner::new(),
//!         }
//!     }
//!
//!     /// Call this once from JavaScript to start your app.
//!     #[wasm_bindgen]
//!     pub async fn start(&self, canvas: web_sys::HtmlCanvasElement) -> Result<(), wasm_bindgen::JsValue> {
//!         self.runner
//!             .start(
//!                 canvas,
//!                 eframe::WebOptions::default(),
//!                 Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc))),)
//!             )
//!             .await
//!     }
//!
//!     // The following are optional:
//!
//!     /// Shut down eframe and clean up resources.
//!     #[wasm_bindgen]
//!     pub fn destroy(&self) {
//!         self.runner.destroy();
//!     }
//!
//!     /// Example on how to call into your app from JavaScript.
//!     #[wasm_bindgen]
//!     pub fn example(&self) {
//!         if let Some(app) = self.runner.app_mut::<MyEguiApp>() {
//!             app.example();
//!         }
//!     }
//!
//!     /// The JavaScript can check whether or not your app has crashed:
//!     #[wasm_bindgen]
//!     pub fn has_panicked(&self) -> bool {
//!         self.runner.has_panicked()
//!     }
//!
//!     #[wasm_bindgen]
//!     pub fn panic_message(&self) -> Option<String> {
//!         self.runner.panic_summary().map(|s| s.message())
//!     }
//!
//!     #[wasm_bindgen]
//!     pub fn panic_callstack(&self) -> Option<String> {
//!         self.runner.panic_summary().map(|s| s.callstack())
//!     }
//! }
//! ```
//!
//! ## Feature flags
#![doc = document_features::document_features!()]
//!
//! ## Instrumentation
//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation.
//! You can enable features on the profiling crates in your application to add instrumentation for all
//! crates that support it, including egui. See the profiling crate docs for more information.
//! ```toml
//! [dependencies]
//! profiling = "1.0"
//! [features]
//! profile-with-puffin = ["profiling/profile-with-puffin"]
//! ```
//!

#![warn(missing_docs)] // let's keep eframe well-documented

// Limitation imposed by `accesskit_winit`:
// https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility`
#[cfg(all(
    target_os = "android",
    feature = "accesskit",
    feature = "android-native-activity"
))]
compile_error!("`accesskit` feature is only available with `android-game-activity`");

// Re-export all useful libraries:
pub use {egui, egui::emath, egui::epaint};

#[cfg(feature = "glow")]
pub use {egui_glow, glow};

#[cfg(feature = "wgpu_no_default_features")]
pub use {egui_wgpu, egui_wgpu::SurfaceConfig, egui_wgpu::WgpuConfiguration, egui_wgpu::wgpu};

mod epi;

// Re-export everything in `epi` so `eframe` users don't have to care about what `epi` is:
pub use epi::*;

pub(crate) mod stopwatch;

// ----------------------------------------------------------------------------
// When compiling for web

#[cfg(target_arch = "wasm32")]
pub use wasm_bindgen;

#[cfg(target_arch = "wasm32")]
pub use web_sys;

#[cfg(target_arch = "wasm32")]
pub mod web;

#[cfg(target_arch = "wasm32")]
pub use web::{WebLogger, WebRunner};

// ----------------------------------------------------------------------------
// When compiling natively

#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
mod native;

#[cfg(target_os = "macos")]
pub use native::macos::WindowChromeMetrics;

#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
pub use native::run::EframeWinitApplication;

#[cfg(not(any(target_arch = "wasm32", target_os = "ios")))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
pub use native::run::EframePumpStatus;

#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
#[cfg(feature = "persistence")]
pub use native::file_storage::storage_dir;

#[cfg(not(target_arch = "wasm32"))]
pub mod icon_data;

// ----------------------------------------------------------------------------

/// Attach an [`egui_inspection::InspectionPlugin`] to `ctx` when enabled via the environment.
#[cfg(all(feature = "inspection", not(target_arch = "wasm32")))]
pub(crate) fn maybe_attach_inspection_plugin(ctx: &egui::Context, label: Option<String>) {
    match egui_inspection::attach_from_env(ctx, label) {
        Ok(true) => log::info!("egui_inspection plugin attached"),
        Ok(false) => {}
        Err(err) => log::warn!("egui_inspection attach failed: {err}"),
    }
}

/// Fallback for native builds without the `inspection` feature. Logs warning if inspection env
/// var was set.
#[cfg(all(
    not(feature = "inspection"),
    not(target_arch = "wasm32"),
    any(feature = "glow", feature = "wgpu_no_default_features")
))]
pub(crate) fn maybe_attach_inspection_plugin(_ctx: &egui::Context, _label: Option<String>) {
    if let Ok(value) = std::env::var("EGUI_INSPECTION")
        && value != "0"
        && value != "false"
        && !value.is_empty()
    {
        log::warn!("Inspection env var set but app was compiled without eframe/inspection feature");
    }
}

/// This is how you start a native (desktop) app.
///
/// The first argument is name of your app, which is an identifier
/// used for the save location of persistence (see [`App::save`]).
/// It is also used as the application id on wayland.
/// If you set no title on the viewport, the app id will be used
/// as the title.
///
/// For details about application ID conventions, see the
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
///
/// Call from `fn main` like this:
/// ``` no_run
/// use eframe::egui;
///
/// fn main() -> eframe::Result {
///     let native_options = eframe::NativeOptions::default();
///     eframe::run_native("MyApp", native_options, Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))))
/// }
///
/// #[derive(Default)]
/// struct MyEguiApp {}
///
/// impl MyEguiApp {
///     fn new(cc: &eframe::CreationContext<'_>) -> Self {
///         // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.
///         // Restore app state using cc.storage (requires the "persistence" feature).
///         // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
///         // for e.g. egui::PaintCallback.
///         Self::default()
///     }
/// }
///
/// impl eframe::App for MyEguiApp {
///    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
///        egui::CentralPanel::default().show(ui, |ui| {
///            ui.heading("Hello World!");
///        });
///    }
/// }
/// ```
///
/// # Errors
/// This function can fail if we fail to set up a graphics context.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)]
pub fn run_native(
    app_name: &str,
    native_options: NativeOptions,
    app_creator: AppCreator<'_>,
) -> Result {
    run_native_ext(app_name, native_options, None, app_creator)
}

/// Like [`run_native`], but lets you supply a pre-existing [`egui::Context`].
///
/// If `egui_ctx` is `Some`, that context will be used by eframe instead of creating a fresh one.
/// If it is `None`, eframe creates a new context (same behavior as [`run_native`]).
///
/// # Errors
/// This function can fail if we fail to set up a graphics context.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)]
pub fn run_native_ext(
    app_name: &str,
    mut native_options: NativeOptions,
    egui_ctx: Option<egui::Context>,
    app_creator: AppCreator<'_>,
) -> Result {
    let renderer = init_native(app_name, &mut native_options);

    match renderer {
        #[cfg(feature = "glow")]
        Renderer::Glow => {
            log::debug!("Using the glow renderer");
            native::run::run_glow(app_name, native_options, egui_ctx, app_creator)
        }

        #[cfg(feature = "wgpu_no_default_features")]
        Renderer::Wgpu => {
            log::debug!("Using the wgpu renderer");
            native::run::run_wgpu(app_name, native_options, egui_ctx, app_creator)
        }
    }
}

/// Provides a proxy for your native eframe application to run on your own event loop.
///
/// See `run_native` for details about `app_name`.
///
/// Call from `fn main` like this:
/// ``` no_run
/// use eframe::{egui, UserEvent};
/// use winit::event_loop::{ControlFlow, EventLoop};
///
/// fn main() -> eframe::Result {
///     let native_options = eframe::NativeOptions::default();
///     let eventloop = EventLoop::<UserEvent>::with_user_event().build()?;
///     eventloop.set_control_flow(ControlFlow::Poll);
///
///     let mut winit_app = eframe::create_native(
///         "MyExtApp",
///         native_options,
///         Box::new(|cc| Ok(Box::new(MyEguiApp::new(cc)))),
///         &eventloop,
///     );
///
///     eventloop.run_app(&mut winit_app)?;
///
///     Ok(())
/// }
///
/// #[derive(Default)]
/// struct MyEguiApp {}
///
/// impl MyEguiApp {
///     fn new(cc: &eframe::CreationContext<'_>) -> Self {
///         Self::default()
///     }
/// }
///
/// impl eframe::App for MyEguiApp {
///    fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
///        egui::CentralPanel::default().show(ui, |ui| {
///            ui.heading("Hello World!");
///        });
///    }
/// }
/// ```
///
/// See the `external_eventloop` example for a more complete example.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
pub fn create_native<'a>(
    app_name: &str,
    mut native_options: NativeOptions,
    app_creator: AppCreator<'a>,
    event_loop: &winit::event_loop::EventLoop<UserEvent>,
) -> EframeWinitApplication<'a> {
    let renderer = init_native(app_name, &mut native_options);

    match renderer {
        #[cfg(feature = "glow")]
        Renderer::Glow => {
            log::debug!("Using the glow renderer");
            EframeWinitApplication::new(native::run::create_glow(
                app_name,
                native_options,
                app_creator,
                event_loop,
            ))
        }

        #[cfg(feature = "wgpu_no_default_features")]
        Renderer::Wgpu => {
            log::debug!("Using the wgpu renderer");
            EframeWinitApplication::new(native::run::create_wgpu(
                app_name,
                native_options,
                app_creator,
                event_loop,
            ))
        }
    }
}

#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
fn init_native(app_name: &str, native_options: &mut NativeOptions) -> Renderer {
    #[cfg(not(feature = "__screenshot"))]
    assert!(
        std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
        "EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature"
    );

    if native_options.viewport.title.is_none() {
        native_options.viewport.title = Some(app_name.to_owned());
    }
    if native_options.viewport.app_id.is_none() {
        native_options.viewport.app_id = Some(app_name.to_owned());
    }

    let renderer = native_options.renderer;

    #[cfg(all(feature = "glow", feature = "wgpu_no_default_features"))]
    {
        match native_options.renderer {
            Renderer::Glow => "glow",
            Renderer::Wgpu => "wgpu",
        };
        log::info!("Both the glow and wgpu renderers are available. Using {renderer}.");
    }

    renderer
}

// ----------------------------------------------------------------------------

/// The simplest way to get started when writing a native app.
///
/// This does NOT support persistence of custom user data. For that you need to use [`run_native`].
/// However, it DOES support persistence of egui data (window positions and sizes, how far the user has scrolled in a
/// [`ScrollArea`](egui::ScrollArea), etc.) if the persistence feature is enabled.
///
/// # Example
/// ``` no_run
/// fn main() -> eframe::Result {
///     // Our application state:
///     let mut name = "Arthur".to_owned();
///     let mut age = 42;
///
///     let options = eframe::NativeOptions::default();
///     eframe::run_ui_native("My egui App", options, move |ui, _frame| {
///         // Wrap everything in a CentralPanel so we get some margins and a background color:
///         egui::CentralPanel::default().show(ui, |ui| {
///             ui.heading("My egui Application");
///             ui.horizontal(|ui| {
///                 let name_label = ui.label("Your name: ");
///                 ui.text_edit_singleline(&mut name)
///                     .labelled_by(name_label.id);
///             });
///             ui.add(egui::Slider::new(&mut age, 0..=120).text("age"));
///             if ui.button("Increment").clicked() {
///                 age += 1;
///             }
///             ui.label(format!("Hello '{name}', age {age}"));
///         });
///     })
/// }
/// ```
///
/// # Errors
/// This function can fail if we fail to set up a graphics context.
#[cfg(not(target_arch = "wasm32"))]
#[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))]
pub fn run_ui_native(
    app_name: &str,
    native_options: NativeOptions,
    ui_fun: impl FnMut(&mut egui::Ui, &mut Frame) + 'static,
) -> Result {
    struct SimpleApp<U> {
        ui_fun: U,
    }

    impl<U: FnMut(&mut egui::Ui, &mut Frame) + 'static> App for SimpleApp<U> {
        fn ui(&mut self, ui: &mut egui::Ui, frame: &mut Frame) {
            (self.ui_fun)(ui, frame);
        }
    }

    run_native(
        app_name,
        native_options,
        Box::new(|_cc| Ok(Box::new(SimpleApp { ui_fun }))),
    )
}

// ----------------------------------------------------------------------------

/// The different problems that can occur when trying to run `eframe`.
#[derive(Debug)]
pub enum Error {
    /// Something went wrong in user code when creating the app.
    AppCreation(Box<dyn std::error::Error + Send + Sync>),

    /// An error from [`winit`].
    #[cfg(not(target_arch = "wasm32"))]
    Winit(winit::error::OsError),

    /// An error from [`winit::event_loop::EventLoop`].
    #[cfg(not(target_arch = "wasm32"))]
    WinitEventLoop(winit::error::EventLoopError),

    /// An error from [`glutin`] when using [`glow`].
    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
    Glutin(glutin::error::Error),

    /// An error from [`glutin`] when using [`glow`].
    #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
    NoGlutinConfigs(glutin::config::ConfigTemplate, Box<dyn std::error::Error>),

    /// An error from [`glutin`] when using [`glow`].
    #[cfg(feature = "glow")]
    OpenGL(egui_glow::PainterError),

    /// An error from [`wgpu`].
    #[cfg(feature = "wgpu_no_default_features")]
    Wgpu(egui_wgpu::WgpuError),
}

impl std::error::Error for Error {}

#[cfg(not(target_arch = "wasm32"))]
impl From<winit::error::OsError> for Error {
    #[inline]
    fn from(err: winit::error::OsError) -> Self {
        Self::Winit(err)
    }
}

#[cfg(not(target_arch = "wasm32"))]
impl From<winit::error::EventLoopError> for Error {
    #[inline]
    fn from(err: winit::error::EventLoopError) -> Self {
        Self::WinitEventLoop(err)
    }
}

#[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
impl From<glutin::error::Error> for Error {
    #[inline]
    fn from(err: glutin::error::Error) -> Self {
        Self::Glutin(err)
    }
}

#[cfg(feature = "glow")]
impl From<egui_glow::PainterError> for Error {
    #[inline]
    fn from(err: egui_glow::PainterError) -> Self {
        Self::OpenGL(err)
    }
}

#[cfg(feature = "wgpu_no_default_features")]
impl From<egui_wgpu::WgpuError> for Error {
    #[inline]
    fn from(err: egui_wgpu::WgpuError) -> Self {
        Self::Wgpu(err)
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::AppCreation(err) => write!(f, "app creation error: {err}"),

            #[cfg(not(target_arch = "wasm32"))]
            Self::Winit(err) => {
                write!(f, "winit error: {err}")
            }

            #[cfg(not(target_arch = "wasm32"))]
            Self::WinitEventLoop(err) => {
                write!(f, "winit EventLoopError: {err}")
            }

            #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
            Self::Glutin(err) => {
                write!(f, "glutin error: {err}")
            }

            #[cfg(all(feature = "glow", not(target_arch = "wasm32")))]
            Self::NoGlutinConfigs(template, err) => {
                write!(
                    f,
                    "Found no glutin configs matching the template: {template:?}. Error: {err}"
                )
            }

            #[cfg(feature = "glow")]
            Self::OpenGL(err) => {
                write!(f, "egui_glow: {err}")
            }

            #[cfg(feature = "wgpu_no_default_features")]
            Self::Wgpu(err) => {
                write!(f, "WGPU error: {err}")
            }
        }
    }
}

/// Short for `Result<T, eframe::Error>`.
pub type Result<T = (), E = Error> = std::result::Result<T, E>;
