par-term 0.28.0

Cross-platform GPU-accelerated terminal emulator with inline graphics support (Sixel, iTerm2, Kitty)
//! Shader installation prompt UI.
//!
//! Displays a dialog on first startup when the shaders folder is missing or empty,
//! offering to download and install the shader pack from GitHub releases.

use crate::shader_installer;
use crate::ui_constants::{
    SHADER_INSTALL_BUTTON_HEIGHT, SHADER_INSTALL_BUTTON_WIDTH, SHADER_INSTALL_DIALOG_WIDTH,
    SHADER_INSTALL_INNER_MARGIN,
};
use egui::{Align2, Color32, Context, Frame, RichText, Window, epaint::Shadow};

/// User's response to the shader install prompt
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShaderInstallResponse {
    /// User clicked "Yes, Install" - download and install shaders
    Install,
    /// User clicked "Never" - save preference to config
    Never,
    /// User clicked "Later" - dismiss for this session only
    Later,
    /// No response yet (dialog still showing or not shown)
    None,
}

/// Shader install dialog UI manager
pub struct ShaderInstallUI {
    /// Whether the dialog is currently visible
    pub visible: bool,
    /// Whether installation is in progress
    pub installing: bool,
    /// Installation progress message
    pub progress_message: Option<String>,
    /// Installation error message
    pub error_message: Option<String>,
    /// Installation success message
    pub success_message: Option<String>,
}

impl ShaderInstallUI {
    /// Create a new shader install UI
    pub fn new() -> Self {
        Self {
            visible: false,
            installing: false,
            progress_message: None,
            error_message: None,
            success_message: None,
        }
    }

    /// Show the dialog
    pub fn show_dialog(&mut self) {
        self.visible = true;
        self.installing = false;
        self.progress_message = None;
        self.error_message = None;
        self.success_message = None;
    }

    /// Hide the dialog
    pub fn hide(&mut self) {
        self.visible = false;
    }

    /// Render the shader install dialog
    /// Returns the user's response
    pub fn show(&mut self, ctx: &Context) -> ShaderInstallResponse {
        if !self.visible {
            return ShaderInstallResponse::None;
        }

        let mut response = ShaderInstallResponse::None;

        // Ensure dialog is fully opaque
        let mut style = (*ctx.style()).clone();
        let solid_bg = Color32::from_rgba_unmultiplied(32, 32, 32, 255);
        style.visuals.window_fill = solid_bg;
        style.visuals.panel_fill = solid_bg;
        style.visuals.widgets.noninteractive.bg_fill = solid_bg;
        ctx.set_style(style);

        let viewport = ctx.input(|i| i.viewport_rect());

        Window::new("Shader Pack Available")
            .resizable(false)
            .collapsible(false)
            .default_width(SHADER_INSTALL_DIALOG_WIDTH)
            .default_pos(viewport.center())
            .pivot(Align2::CENTER_CENTER)
            .frame(
                Frame::window(&ctx.style())
                    .fill(solid_bg)
                    .inner_margin(SHADER_INSTALL_INNER_MARGIN)
                    .stroke(egui::Stroke::new(1.0, Color32::from_gray(80)))
                    .shadow(Shadow {
                        offset: [4, 4],
                        blur: 16,
                        spread: 4,
                        color: Color32::from_black_alpha(180),
                    }),
            )
            .show(ctx, |ui| {
                ui.vertical_centered(|ui| {
                    // Header with icon
                    ui.add_space(8.0);
                    ui.label(
                        RichText::new("Custom Shaders Available")
                            .size(20.0)
                            .strong(),
                    );
                    ui.add_space(16.0);
                });

                // Description
                ui.label(
                    "par-term includes 49+ custom background shaders and 12 cursor \
                     effect shaders that can transform your terminal experience.",
                );
                ui.add_space(8.0);

                ui.label("Effects include:");
                ui.indent("effects_list", |ui| {
                    ui.label("- CRT monitors, scanlines, and retro effects");
                    ui.label("- Matrix rain, starfields, and particle systems");
                    ui.label("- Plasma, fire, and abstract visualizations");
                    ui.label("- Cursor trails, glows, and ripple effects");
                });

                ui.add_space(16.0);

                // Show installation progress/error/success
                if self.installing {
                    ui.horizontal(|ui| {
                        ui.spinner();
                        ui.label(
                            self.progress_message
                                .as_deref()
                                .unwrap_or("Installing shaders..."),
                        );
                    });
                } else if let Some(error) = &self.error_message {
                    ui.colored_label(Color32::from_rgb(255, 100, 100), error);
                    ui.add_space(8.0);
                    ui.label("You can try again later using: par-term install-shaders");
                } else if let Some(success) = &self.success_message {
                    ui.colored_label(Color32::from_rgb(100, 255, 100), success);
                    ui.add_space(8.0);
                    ui.label("Configure shaders in Settings (F12) under 'Background & Effects'.");
                }

                ui.add_space(16.0);

                // Buttons (centered)
                ui.vertical_centered(|ui| {
                    // Don't show buttons during installation or after success
                    if !self.installing && self.success_message.is_none() {
                        ui.horizontal(|ui| {
                            if ui
                                .add_sized(
                                    [SHADER_INSTALL_BUTTON_WIDTH, SHADER_INSTALL_BUTTON_HEIGHT],
                                    egui::Button::new("Yes, Install"),
                                )
                                .clicked()
                            {
                                response = ShaderInstallResponse::Install;
                            }

                            ui.add_space(8.0);

                            if ui
                                .add_sized(
                                    [SHADER_INSTALL_BUTTON_WIDTH, SHADER_INSTALL_BUTTON_HEIGHT],
                                    egui::Button::new("Never"),
                                )
                                .on_hover_text("Don't ask again")
                                .clicked()
                            {
                                response = ShaderInstallResponse::Never;
                            }

                            ui.add_space(8.0);

                            if ui
                                .add_sized(
                                    [SHADER_INSTALL_BUTTON_WIDTH, SHADER_INSTALL_BUTTON_HEIGHT],
                                    egui::Button::new("Later"),
                                )
                                .on_hover_text("Ask again next time")
                                .clicked()
                            {
                                response = ShaderInstallResponse::Later;
                            }
                        });
                    } else if self.success_message.is_some() {
                        // Show OK button after successful install
                        if ui
                            .add_sized(
                                [SHADER_INSTALL_BUTTON_WIDTH, SHADER_INSTALL_BUTTON_HEIGHT],
                                egui::Button::new("OK"),
                            )
                            .clicked()
                        {
                            self.visible = false;
                        }
                    }
                });

                ui.add_space(8.0);

                // Help text
                if !self.installing
                    && self.success_message.is_none()
                    && self.error_message.is_none()
                {
                    ui.vertical_centered(|ui| {
                        ui.label(
                            RichText::new(
                                "You can always install later with: par-term install-shaders",
                            )
                            .weak()
                            .small(),
                        );
                    });
                }
            });

        response
    }

    /// Set installation in progress
    pub fn set_installing(&mut self, message: &str) {
        self.installing = true;
        self.progress_message = Some(message.to_string());
        self.error_message = None;
    }

    /// Set installation error
    pub fn set_error(&mut self, error: &str) {
        self.installing = false;
        self.progress_message = None;
        self.error_message = Some(error.to_string());
    }

    /// Set installation success
    pub fn set_success(&mut self, message: &str) {
        self.installing = false;
        self.progress_message = None;
        self.error_message = None;
        self.success_message = Some(message.to_string());
    }
}

impl Default for ShaderInstallUI {
    fn default() -> Self {
        Self::new()
    }
}

impl crate::traits::OverlayComponent for ShaderInstallUI {
    type Action = ShaderInstallResponse;

    fn show(&mut self, ctx: &egui::Context) -> Self::Action {
        ShaderInstallUI::show(self, ctx)
    }

    fn is_visible(&self) -> bool {
        self.visible
    }

    fn set_visible(&mut self, visible: bool) {
        self.visible = visible;
    }
}

/// Install shaders from GitHub release (delegates to shared shader_installer module)
/// This is a blocking operation that downloads and extracts shaders
pub fn install_shaders_headless() -> Result<usize, String> {
    shader_installer::install_shaders()
}