// Bottom-edge cursor proximity recogniser. Emits a transition each time the
// cursor crosses into or out of a thin band at the bottom of the screen.
// Crustace uses it to drive the auto-hide dock reveal — enter signals show,
// leave signals hide-after-grace. Two thresholds give a hysteresis band so a
// cursor parked at the boundary does not oscillate.

use smithay::utils::{Logical, Point};

const ENTER_BAND_LOG:         f64 = 2.0;
const DEFAULT_LEAVE_BAND_LOG: f64 = 8.0;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum EdgeEvent {
    Enter,
    Leave,
}

#[derive(Debug)]
pub struct BottomEdgeRecognizer {
    inside:     bool,
    leave_band: f64,
}

impl BottomEdgeRecognizer {
    pub fn new() -> Self {
        Self { inside: false, leave_band: DEFAULT_LEAVE_BAND_LOG }
    }

    pub fn set_leave_band(&mut self, band_log: f64) {
        self.leave_band = band_log.max(ENTER_BAND_LOG);
    }

    pub fn on_motion(&mut self, pos: Point<f64, Logical>, screen_h: i32) -> Option<EdgeEvent> {
        let h = screen_h as f64;
        if self.inside {
            if pos.y < h - self.leave_band {
                self.inside = false;
                return Some(EdgeEvent::Leave);
            }
        } else if pos.y >= h - ENTER_BAND_LOG {
            self.inside = true;
            return Some(EdgeEvent::Enter);
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn enter_fires_on_first_band_cross() {
        let mut r = BottomEdgeRecognizer::new();
        assert_eq!(r.on_motion(Point::from((100.0, 1078.5)), 1080), Some(EdgeEvent::Enter));
    }

    #[test]
    fn leave_fires_after_pulling_back_past_outer_threshold() {
        let mut r = BottomEdgeRecognizer::new();
        r.on_motion(Point::from((100.0, 1079.0)), 1080);
        assert_eq!(r.on_motion(Point::from((100.0, 1070.0)), 1080), Some(EdgeEvent::Leave));
    }

    #[test]
    fn hysteresis_band_does_not_oscillate() {
        let mut r = BottomEdgeRecognizer::new();
        r.on_motion(Point::from((100.0, 1079.0)), 1080);
        // Pull back to a y inside the hysteresis band (between enter and leave thresholds):
        // still considered inside, no spurious Leave.
        assert_eq!(r.on_motion(Point::from((100.0, 1075.0)), 1080), None);
        assert_eq!(r.on_motion(Point::from((100.0, 1079.5)), 1080), None);
    }

    #[test]
    fn outside_band_emits_nothing() {
        let mut r = BottomEdgeRecognizer::new();
        assert_eq!(r.on_motion(Point::from((100.0, 500.0)), 1080), None);
        assert_eq!(r.on_motion(Point::from((100.0, 700.0)), 1080), None);
    }
}
