use druid::{piet::*, *};
pub struct ToggleSwitch {
    current_thumb_extension: f64,
    current_thumb_radius: f64,
    current_thumb_position: f64,
    dragging_thumb_position: f64,
}
impl ToggleSwitch {
    pub fn new() -> Self {
        Self::default()
    }
    fn is_hovering_thumb(&self, x: f64, y: f64) -> bool {
        let thumb_origin_pos = (4. + 4. + 6. + self.current_thumb_position, 6. + 4. + 6.);
        let thumb_radius = 6f64;
        let distance = (x - thumb_origin_pos.0).powi(2) + (y - thumb_origin_pos.1).powi(2);
        distance <= thumb_radius.powi(2)
    }
}
impl Default for ToggleSwitch {
    fn default() -> Self {
        Self {
            dragging_thumb_position: f64::NAN,
            current_thumb_position: 0.,
            current_thumb_radius: 0.,
            current_thumb_extension: 0.,
        }
    }
}
impl Widget<bool> for ToggleSwitch {
    fn event(&mut self, ctx: &mut EventCtx, evt: &Event, data: &mut bool, _env: &Env) {
        match evt {
            Event::MouseDown(m) => {
                if !ctx.is_disabled() {
                    ctx.set_active(true);
                    if self.is_hovering_thumb(m.pos.x, m.pos.y) {
                        self.dragging_thumb_position = m.pos.x;
                        ctx.request_anim_frame();
                    }
                }
            }
            Event::MouseUp(m) => {
                if !ctx.is_disabled() {
                    ctx.set_active(false);
                    let _last_data = *data;
                    if self.dragging_thumb_position.is_nan()
                        || (self.dragging_thumb_position - m.pos.x).abs() < f64::EPSILON
                    {
                        *data = !*data;
                    } else {
                        *data =
                            self.current_thumb_position > 10. - self.current_thumb_extension / 2.;
                    }
                    ctx.request_anim_frame();
                    self.dragging_thumb_position = f64::NAN;
                }
            }
            Event::MouseMove(m) => {
                if !ctx.is_disabled() && ctx.is_hot() && !self.dragging_thumb_position.is_nan() {
                    if *data {
                        self.current_thumb_position = 20. - self.current_thumb_extension + m.pos.x
                            - self.dragging_thumb_position;
                    } else {
                        self.current_thumb_position = m.pos.x - self.dragging_thumb_position;
                    }
                    self.current_thumb_position = self
                        .current_thumb_position
                        .clamp(0., 20. - self.current_thumb_extension);
                }
            }
            Event::AnimFrame(_) => {
                let mut should_animate = false;
                if self.dragging_thumb_position.is_nan() {
                    if ctx.is_active() {
                        let target_position = if *data {
                            20. - self.current_thumb_extension
                        } else {
                            0.
                        };
                        self.current_thumb_position +=
                            (target_position - self.current_thumb_position) / 4.;
                        should_animate |=
                            (target_position - self.current_thumb_position).abs() > f64::EPSILON;
                        self.current_thumb_extension += (3. - self.current_thumb_extension) / 4.;
                        should_animate |= (3. - self.current_thumb_extension).abs() > f64::EPSILON;
                    } else if ctx.is_hot() {
                        self.current_thumb_radius += (1. - self.current_thumb_radius) / 4.;
                        should_animate |= (1. - self.current_thumb_radius).abs() > f64::EPSILON;
                        ctx.request_paint();
                    } else {
                        self.current_thumb_radius -= self.current_thumb_radius / 4.;
                        should_animate |= self.current_thumb_radius.abs() > f64::EPSILON;
                    }
                    let target_position = if *data {
                        20. - self.current_thumb_extension
                    } else {
                        0.
                    };
                    self.current_thumb_position +=
                        (target_position - self.current_thumb_position) / 4.;
                    should_animate |=
                        (target_position - self.current_thumb_position).abs() > f64::EPSILON;
                    self.current_thumb_extension -= self.current_thumb_extension / 4.;
                    should_animate |= self.current_thumb_extension > f64::EPSILON;
                } else {
                    self.current_thumb_extension += (3. - self.current_thumb_extension) / 4.;
                    should_animate |= (3. - self.current_thumb_extension).abs() > f64::EPSILON;
                }
                ctx.request_paint();
                if should_animate {
                    ctx.request_anim_frame();
                }
            }
            _ => {}
        }
    }
    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &bool, _env: &Env) {
        if let LifeCycle::WidgetAdded = event {
            self.current_thumb_position = if *data {
                20. - self.current_thumb_extension
            } else {
                0.
            };
        } else if let LifeCycle::HotChanged(_) = event {
            if !ctx.is_disabled() {
                ctx.request_anim_frame();
            }
        }
    }
    fn update(&mut self, ctx: &mut UpdateCtx, _old_data: &bool, _data: &bool, _env: &Env) {
        ctx.request_anim_frame();
    }
    fn layout(
        &mut self,
        _ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &bool,
        _env: &Env,
    ) -> Size {
        bc.debug_check("ToggleSwitch");
        bc.constrain((48., 32.))
    }
    fn paint(&mut self, ctx: &mut PaintCtx, data: &bool, env: &Env) {
        let disabled = ctx.is_disabled();
        let outline = Rect::from_origin_size((4. + 0.5, 6. + 0.5), (38. + 1., 18. + 1.));
        let outline = outline.to_rounded_rect(outline.height() / 2.);
        ctx.fill(
            outline,
            &if disabled {
                if *data {
                    PaintBrush::Color(env.get(crate::theme::color::base::LOW))
                } else {
                    PaintBrush::Color(Color::TRANSPARENT)
                }
            } else if *data {
                PaintBrush::Color(env.get(crate::theme::color::main::PRIMARY))
            } else {
                PaintBrush::Color(env.get(crate::theme::color::base::LOW))
            },
        );
        ctx.stroke(
            outline,
            &if disabled {
                PaintBrush::Color(env.get(crate::theme::color::base::LOW))
            } else if *data {
                PaintBrush::Color(env.get(crate::theme::color::main::PRIMARY))
            } else {
                PaintBrush::Color(env.get(crate::theme::color::base::MEDIUM))
            },
            1.,
        );
        let thumb_outline = Rect::from_origin_size(
            (
                4. + 4. + self.current_thumb_position - self.current_thumb_radius / 2.,
                6. + 4. - self.current_thumb_radius / 2.,
            ),
            (
                12. + self.current_thumb_radius + self.current_thumb_extension,
                12. + self.current_thumb_radius,
            ),
        );
        let thumb_outline = thumb_outline.to_rounded_rect(thumb_outline.height() / 2.);
        ctx.fill(
            thumb_outline,
            &if disabled {
                PaintBrush::Color(env.get(crate::theme::color::base::MEDIUM_LOW))
            } else if *data {
                PaintBrush::Color(env.get(crate::theme::color::alt::HIGH))
            } else {
                PaintBrush::Color(env.get(crate::theme::color::chrome::ALT_LOW))
            },
        );
        if !disabled {
            ctx.stroke(
                thumb_outline,
                &PaintBrush::Color(env.get(crate::theme::color::base::LOW)),
                1.,
            );
        }
    }
}