// Ardura 2023 - Changing the toggle_switch.rs to be a Button now that I've proven that works as a param for sample loading
// This lets me have buttons in nih-plug without using native egui (especially with turning off params for users)

use nih_plug::prelude::{Param, ParamSetter};
use nih_plug_egui::egui::{
    self, style::WidgetVisuals, Align2, Color32, FontId, Rect, Response, Stroke, Ui, Vec2, Widget,
};

struct SliderRegion<'a, P: Param> {
    param: &'a P,
    param_setter: &'a ParamSetter<'a>,
    font: FontId,
    background_color: Color32,
    text_color: Color32,
}

impl<'a, P: Param> SliderRegion<'a, P> {
    fn new(
        param: &'a P,
        param_setter: &'a ParamSetter,
        font: FontId,
        background_color: Color32,
        text_color: Color32,
    ) -> Self {
        SliderRegion {
            param,
            param_setter,
            font,
            background_color,
            text_color,
        }
    }

    // Handle the input for a given response. Returns an f32 containing the normalized value of
    // the parameter.
    fn handle_response(&mut self, ui: &Ui, response: &Response, rect: Rect) -> f32 {
        let mut value = self.param.modulated_normalized_value();
        let how_on;
        let visuals: WidgetVisuals;

        // Check if our button is clicked
        if response.clicked() {
            if value == 0.0 {
                self.param_setter.set_parameter_normalized(self.param, 1.0);
                how_on = ui.ctx().animate_bool(response.id, true);
                visuals = ui.style().interact_selectable(&response, true);
                self.text_color = Color32::BLACK;
                value = 1.0;
            } else {
                self.param_setter.set_parameter_normalized(self.param, 0.0);
                how_on = ui.ctx().animate_bool(response.id, false);
                visuals = ui.style().interact_selectable(&response, false);
                self.text_color = visuals.text_color();
                value = 0.0;
            }
        } else {
            let temp: bool = if value > 0.0 { true } else { false };
            how_on = ui.ctx().animate_bool(response.id, temp);
            visuals = ui.style().interact_selectable(&response, temp);
            if temp {
                self.text_color = Color32::BLACK;
            } else {
                self.text_color = visuals.text_color();
            }
        }

        // DRAWING
        let rect = rect.expand(visuals.expansion);
        ui.painter().rect(
            rect,
            0.5,
            if self.background_color == Color32::PLACEHOLDER {
                visuals.bg_fill.linear_multiply(0.8)
            } else {
                self.background_color
            },
            visuals.bg_stroke,
            egui::StrokeKind::Middle
        );
        // Paint the circle, animating it from left to right with `how_on`:
        ui.painter().rect_stroke(
            rect,
            0.5,
            Stroke::new(
                1.0,
                visuals
                    .bg_stroke
                    .color
                    .linear_multiply((how_on + 0.2).clamp(0.2, 1.2)),
            ),
            egui::StrokeKind::Middle
        );
        let center = egui::pos2(rect.center().x, rect.center().y);
        ui.painter().text(
            center,
            Align2::CENTER_CENTER,
            self.param.name(),
            self.font.clone(),
            if self.text_color == Color32::PLACEHOLDER {
                visuals.text_color()
            } else {
                self.text_color
            },
        );

        value
    }
}

pub struct BoolButton<'a, P: Param> {
    slider_region: SliderRegion<'a, P>,
    // Scaling is in ui.spacing().interact_size.y Units
    scaling_x: f32,
    scaling_y: f32,
    deselect_timer: usize,
    inactive_iterator: usize,
}

#[allow(dead_code)]
/// Create a BoolButton Object sized by ui.spacing().interact_size.y Units
impl<'a, P: Param> BoolButton<'a, P> {
    pub fn for_param(
        param: &'a P,
        param_setter: &'a ParamSetter,
        x_scaling: f32,
        y_scaling: f32,
        font: FontId,
    ) -> Self {
        BoolButton {
            // Pass things to slider to get around
            slider_region: SliderRegion::new(
                param,
                param_setter,
                font,
                Color32::PLACEHOLDER,
                Color32::PLACEHOLDER,
            ),
            scaling_x: x_scaling,
            scaling_y: y_scaling,
            deselect_timer: 200,
            inactive_iterator: 0,
        }
    }

    // The time for the button to become available again
    pub fn with_deselect_timer(mut self, amount_in_samples: usize) -> Self {
        self.deselect_timer = amount_in_samples;
        self
    }

    // To be called from gui thread to move from inactive to active
    pub fn increment_deselect(mut self) {
        self.inactive_iterator += 1;
    }

    pub fn with_background_color(mut self, new_color: Color32) -> Self {
        self.slider_region.background_color = new_color;
        self
    }

    pub fn with_text_color(mut self, new_color: Color32) -> Self {
        self.slider_region.background_color = new_color;
        self
    }
}

impl<'a, P: Param> Widget for BoolButton<'a, P> {
    fn ui(mut self, ui: &mut Ui) -> Response {
        // Figure out the size to reserve on screen for widget
        let (rect, response) = ui.allocate_exact_size(
            ui.spacing().interact_size.y * Vec2::new(self.scaling_x, self.scaling_y),
            egui::Sense::click(),
        );
        self.slider_region.handle_response(&ui, &response, rect);
        if self.inactive_iterator < self.deselect_timer {
            self.increment_deselect();
        }
        response
    }
}
