#![allow(unused)]
#![allow(dead_code)]
use {
    std::cell::{Cell},
    crate::{
        makepad_micro_serde::*,
        makepad_live_tokenizer::{LiveErrorOrigin, live_error_origin},
        makepad_live_compiler::{
            LivePropType,
            LiveType,
            LiveTypeField,
            LiveFieldKind,
            LiveNode,
            LiveId,
            LiveModuleId,
            LiveTypeInfo,
            LiveNodeSliceApi
        },
        live_traits::{LiveNew, LiveHook, LiveHookDeref, LiveApplyValue, LiveApply, ApplyFrom},
        makepad_derive_live::*,
        makepad_math::*,
        makepad_live_id::{FromLiveId, live_id, live_id_num},
        event::{
            event::{Event, Hit}
        },
        window::WindowId,
        cx::Cx,
        area::Area,
    },
};
#[derive(Clone, Copy, Debug, Default, SerBin, DeBin, SerJson, DeJson, PartialEq)]
pub struct KeyModifiers {
    pub shift: bool,
    pub control: bool,
    pub alt: bool,
    pub logo: bool
}
#[derive(Clone, Debug, PartialEq)]
pub struct MouseDownEvent {
    pub abs: DVec2,
    pub button: usize,
    pub window_id: WindowId,
    pub modifiers: KeyModifiers,
    pub handled: Cell<Area>,
    pub time: f64
}
#[derive(Clone, Debug)]
pub struct MouseMoveEvent {
    pub abs: DVec2,
    pub window_id: WindowId,
    pub modifiers: KeyModifiers,
    pub time: f64,
    pub handled: Cell<Area>,
}
#[derive(Clone, Debug)]
pub struct MouseUpEvent {
    pub abs: DVec2,
    pub button: usize,
    pub window_id: WindowId,
    pub modifiers: KeyModifiers,
    pub time: f64
}
#[derive(Clone, Debug)]
pub struct ScrollEvent {
    pub window_id: WindowId,
    pub scroll: DVec2,
    pub abs: DVec2,
    pub modifiers: KeyModifiers,
    pub handled_x: Cell<bool>,
    pub handled_y: Cell<bool>,
    pub is_mouse: bool,
    pub time: f64
}
#[derive(Clone, Copy, Debug)]
pub enum TouchState {
    Start,
    Stop,
    Move,
    Stable
}
#[derive(Clone, Debug)]
pub struct TouchPoint {
    pub state: TouchState,
    pub abs: DVec2,
    pub uid: u64,
    pub rotation_angle: f64,
    pub force: f64,
    pub radius: DVec2,
    pub handled: Cell<Area>,
    pub sweep_lock: Cell<Area>,
}
#[derive(Clone, Debug)]
pub struct TouchUpdateEvent {
    pub time: f64,
    pub window_id: WindowId,
    pub modifiers: KeyModifiers,
    pub touches: Vec<TouchPoint>,
}
#[derive(Clone, Copy, Default, Debug, Live)]
#[live_ignore]
pub struct Margin {
    #[live] pub left: f64,
    #[live] pub top: f64,
    #[live] pub right: f64,
    #[live] pub bottom: f64
}
impl LiveHook for Margin {
    fn skip_apply(&mut self, _cx: &mut Cx, _apply_from: ApplyFrom, index: usize, nodes: &[LiveNode]) -> Option<usize> {
        if let Some(v) = nodes[index].value.as_float() {
            *self = Self {left: v, top: v, right: v, bottom: v};
            Some(index + 1)
        }
        else {
            None
        }
    }
}
impl Margin {
    pub fn left_top(&self) -> DVec2 {
        dvec2(self.left, self.top)
    }
    pub fn right_bottom(&self) -> DVec2 {
        dvec2(self.right, self.bottom)
    }
    pub fn size(&self) -> DVec2 {
        dvec2(self.left + self.right, self.top + self.bottom)
    }
    pub fn width(&self) -> f64 {
        self.left + self.right
    }
    pub fn height(&self) -> f64 {
        self.top + self.bottom
    }
    
    pub fn rect_contains_with_margin(rect: &Rect, pos: DVec2, margin: &Option<Margin>) -> bool {
        if let Some(margin) = margin {
            return
            pos.x >= rect.pos.x - margin.left
                && pos.x <= rect.pos.x + rect.size.x + margin.right
                && pos.y >= rect.pos.y - margin.top
                && pos.y <= rect.pos.y + rect.size.y + margin.bottom;
        }
        else {
            return rect.contains(pos);
        }
    }
}
pub const TAP_COUNT_TIME: f64 = 0.5;
pub const TAP_COUNT_DISTANCE: f64 = 10.0;
#[derive(Clone, Debug, Default, Eq, Hash, Copy, PartialEq, FromLiveId)]
pub struct DigitId(pub LiveId);
#[derive(Default, Clone)]
pub struct CxDigitCapture {
    digit_id: DigitId,
    pub area: Area,
    pub sweep_area: Area,
    pub switch_capture: Option<Area>,
    pub time: f64,
    pub abs_start: DVec2,
}
#[derive(Default, Clone)]
pub struct CxDigitTap {
    digit_id: DigitId,
    last_pos: DVec2,
    last_time: f64,
    count: u32
}
#[derive(Default, Clone)]
pub struct CxDigitHover {
    digit_id: DigitId,
    new_area: Area,
    area: Area,
}
#[derive(Default, Clone)]
pub struct CxFingers {
    pub first_mouse_button: Option<usize>,
    captures: Vec<CxDigitCapture>,
    tap: CxDigitTap,
    hovers: Vec<CxDigitHover>,
    sweep_lock: Option<Area>,
}
impl CxFingers {
    pub (crate) fn get_digit_for_captured_area(&self, area: Area) -> Option<DigitId> {
        if let Some(digit) = self.captures.iter().find( | d | d.area == area) {
            return Some(digit.digit_id)
        }
        None
    }
    
    pub (crate) fn update_area(&mut self, old_area: Area, new_area: Area) {
        for hover in &mut self.hovers {
            if hover.area == old_area {
                hover.area = new_area;
            }
        }
        for capture in &mut self.captures {
            if capture.area == old_area {
                capture.area = new_area;
            }
            if capture.sweep_area == old_area {
                capture.sweep_area = new_area;
            }
        }
        if self.sweep_lock == Some(old_area) {
            self.sweep_lock = Some(new_area);
        }
    }
    
    pub (crate) fn new_hover_area(&mut self, digit_id: DigitId, new_area: Area) {
        for hover in &mut self.hovers {
            if hover.digit_id == digit_id {
                hover.new_area = new_area;
                return
            }
        }
        self.hovers.push(CxDigitHover {
            digit_id,
            area: Area::Empty,
            new_area: new_area,
        })
    }
    
    pub (crate) fn get_hover_area(&self, digit: DigitId) -> Area {
        for hover in &self.hovers {
            if hover.digit_id == digit {
                return hover.area
            }
        }
        Area::Empty
    }
    
    pub (crate) fn cycle_hover_area(&mut self, digit_id: DigitId) {
        if let Some(hover) = self.hovers.iter_mut().find( | v | v.digit_id == digit_id) {
            hover.area = hover.new_area;
            hover.new_area = Area::Empty;
        }
    }
    
    pub (crate) fn capture_digit(&mut self, digit_id: DigitId, area: Area, sweep_area: Area, time: f64, abs_start: DVec2) {
        self.captures.push(CxDigitCapture {
            sweep_area,
            digit_id,
            area,
            time,
            abs_start,
            switch_capture: None
        })
        }
    
    pub (crate) fn get_digit_capture(&mut self, digit_id: DigitId) -> Option<&mut CxDigitCapture> {
        self.captures.iter_mut().find( | v | v.digit_id == digit_id)
    }
    
    
    pub (crate) fn get_area_capture(&mut self, area: Area) -> Option<&mut CxDigitCapture> {
        self.captures.iter_mut().find( | v | v.area == area)
    }
    
    pub fn is_area_captured(&self, area: Area) -> bool {
        self.captures.iter().find( | v | v.area == area).is_some()
    }
    
    pub (crate) fn release_digit(&mut self, digit_id: DigitId) {
        while let Some(index) = self.captures.iter_mut().position( | v | v.digit_id == digit_id) {
            self.captures.remove(index);
        }
    }
    
    pub (crate) fn remove_hover(&mut self, digit_id: DigitId) {
        while let Some(index) = self.hovers.iter_mut().position( | v | v.digit_id == digit_id) {
            self.hovers.remove(index);
        }
    }
    
    pub (crate) fn get_tap_count(&self) -> u32 {
        self.tap.count
    }
    
    pub (crate) fn process_tap_count(&mut self, pos: DVec2, time: f64) -> u32 {
        if (time - self.tap.last_time) < TAP_COUNT_TIME
            && pos.distance(&self.tap.last_pos) < TAP_COUNT_DISTANCE {
            self.tap.count += 1;
        }
        else {
            self.tap.count = 1;
        }
        self.tap.last_pos = pos;
        self.tap.last_time = time;
        return self.tap.count
    }
    
    pub (crate) fn process_touch_update_start(&mut self, time: f64, touches: &[TouchPoint]) {
        for touch in touches {
            if let TouchState::Start = touch.state {
                self.process_tap_count(touch.abs, time);
            }
        }
    }
    
    pub (crate) fn process_touch_update_end(&mut self, touches: &[TouchPoint]) {
        for touch in touches {
            let digit_id = live_id_num!(touch, touch.uid).into();
            match touch.state {
                TouchState::Stop => {
                    self.release_digit(digit_id);
                    self.remove_hover(digit_id);
                }
                TouchState::Start | TouchState::Move | TouchState::Stable => {
                    self.cycle_hover_area(digit_id);
                }
            }
        }
        self.switch_captures();
    }
    
    pub (crate) fn mouse_down(&mut self, button: usize) {
        if self.first_mouse_button.is_none() {
            self.first_mouse_button = Some(button);
        }
    }
    
    pub (crate) fn switch_captures(&mut self) {
        for capture in &mut self.captures {
            if let Some(area) = capture.switch_capture {
                capture.area = area;
                capture.switch_capture = None;
            }
        }
    }
    
    pub (crate) fn mouse_up(&mut self, button: usize) {
        if self.first_mouse_button == Some(button) {
            self.first_mouse_button = None;
            let digit_id = live_id!(mouse).into();
            self.release_digit(digit_id);
        }
    }
    
    pub (crate) fn test_sweep_lock(&mut self, sweep_area: Area) -> bool {
        if let Some(lock) = self.sweep_lock {
            if lock != sweep_area {
                return true
            }
        }
        false
    }
    
    pub fn sweep_lock(&mut self, area: Area) {
        if self.sweep_lock.is_none() {
            self.sweep_lock = Some(area);
        }
    }
    
    pub fn sweep_unlock(&mut self, area: Area) {
        if self.sweep_lock == Some(area) {
            self.sweep_lock = None;
        }
    }
    
}
#[derive(Clone, Debug)]
pub enum DigitDevice {
    Mouse {
        button: usize
    },
    Touch {
        uid: u64
    },
    XR {}
}
impl DigitDevice {
    pub fn is_touch(&self) -> bool {if let DigitDevice::Touch {..} = self {true}else {false}}
    pub fn is_mouse(&self) -> bool {if let DigitDevice::Mouse {..} = self {true}else {false}}
    pub fn is_xr(&self) -> bool {if let DigitDevice::XR {..} = self {true}else {false}}
    
    pub fn has_hovers(&self) -> bool {self.is_mouse() || self.is_xr()}
    
    pub fn mouse_button(&self) -> Option<usize> {if let DigitDevice::Mouse {button} = self {Some(*button)}else {None}}
    pub fn touch_uid(&self) -> Option<u64> {if let DigitDevice::Touch {uid} = self {Some(*uid)}else {None}}
    }
#[derive(Clone, Debug)]
pub struct FingerDownEvent {
    pub window_id: WindowId,
    pub abs: DVec2,
    
    pub digit_id: DigitId,
    pub device: DigitDevice,
    
    pub tap_count: u32,
    pub modifiers: KeyModifiers,
    pub time: f64,
    pub rect: Rect,
}
impl FingerDownEvent {
    pub fn mod_control(&self) -> bool {self.modifiers.control}
    pub fn mod_alt(&self) -> bool {self.modifiers.alt}
    pub fn mod_shift(&self) -> bool {self.modifiers.shift}
    pub fn mod_logo(&self) -> bool {self.modifiers.logo}
}
#[derive(Clone, Debug)]
pub struct FingerMoveEvent {
    pub window_id: WindowId,
    pub abs: DVec2,
    pub digit_id: DigitId,
    pub device: DigitDevice,
    
    pub tap_count: u32,
    pub modifiers: KeyModifiers,
    pub time: f64,
    
    pub abs_start: DVec2,
    pub rect: Rect,
    pub is_over: bool,
}
impl FingerMoveEvent {
    pub fn move_distance(&self) -> f64 {
        ((self.abs_start.x - self.abs.x).powf(2.) + (self.abs_start.y - self.abs.y).powf(2.)).sqrt()
    }
}
#[derive(Clone, Debug)]
pub struct FingerUpEvent {
    pub window_id: WindowId,
    pub abs: DVec2,
    pub capture_time: f64,
    
    pub digit_id: DigitId,
    pub device: DigitDevice,
    
    pub tap_count: u32,
    pub modifiers: KeyModifiers,
    pub time: f64,
    pub abs_start: DVec2,
    pub rect: Rect,
    pub is_over: bool,
    pub is_sweep: bool
}
impl FingerUpEvent {
    pub fn was_tap(&self) -> bool {
        self.time - self.capture_time < TAP_COUNT_TIME &&
        (self.abs_start - self.abs).length() < TAP_COUNT_DISTANCE
    }
    
    pub fn was_long_press(&self) -> bool {
        self.time - self.capture_time >= TAP_COUNT_TIME &&
        (self.abs_start - self.abs).length() < TAP_COUNT_DISTANCE
    }
}
#[derive(Clone, Debug, PartialEq)]
pub enum HoverState {
    In,
    Over,
    Out
}
impl Default for HoverState {
    fn default() -> HoverState {
        HoverState::Over
    }
}
#[derive(Clone, Debug)]
pub struct FingerHoverEvent {
    pub window_id: WindowId,
    pub abs: DVec2,
    pub digit_id: DigitId,
    pub device: DigitDevice,
    pub modifiers: KeyModifiers,
    pub time: f64,
    pub rect: Rect,
}
#[derive(Clone, Debug)]
pub struct FingerScrollEvent {
    pub window_id: WindowId,
    pub digit_id: DigitId,
    pub abs: DVec2,
    pub scroll: DVec2,
    pub device: DigitDevice,
    pub modifiers: KeyModifiers,
    pub time: f64,
    pub rect: Rect,
}
#[derive(Clone, Debug, Default)]
pub struct HitOptions {
    pub margin: Option<Margin>,
    pub sweep_area: Area,
    pub capture_overload: bool,
}
impl HitOptions {
    pub fn new() -> Self {
        Self::default()
    }
    
    pub fn with_sweep_area(self, area: Area) -> Self {
        Self {
            sweep_area: area,
            ..self
        }
    }
    pub fn with_margin(self, margin: Margin) -> Self {
        Self {
            margin: Some(margin),
            ..self
        }
    }
    pub fn with_capture_overload(self, capture_overload:bool) -> Self {
        Self {
            capture_overload,
            ..self
        }
    }
}
impl Event {
    
    pub fn hits(&self, cx: &mut Cx, area: Area) -> Hit {
        self.hits_with_options(cx, area, HitOptions::default())
    }
    
    pub fn hits_with_sweep_area(&self, cx: &mut Cx, area: Area, sweep_area: Area) -> Hit {
        self.hits_with_options(cx, area, HitOptions::new().with_sweep_area(sweep_area))
    }
    
    
    pub fn hits_with_capture_overload(&self, cx: &mut Cx, area: Area, capture_overload: bool) -> Hit {
        self.hits_with_options(cx, area, HitOptions::new().with_capture_overload(capture_overload))
    }
    
    pub fn hits_with_options(&self, cx: &mut Cx, area: Area, options: HitOptions) -> Hit {
        if !area.is_valid(cx) {
            return Hit::Nothing
        }
        match self {
            Event::KeyFocus(kf) => {
                if area == kf.prev {
                    return Hit::KeyFocusLost(kf.clone())
                }
                else if area == kf.focus {
                    return Hit::KeyFocus(kf.clone())
                }
            },
            Event::KeyDown(kd) => {
                if cx.keyboard.has_key_focus(area) {
                    return Hit::KeyDown(kd.clone())
                }
            },
            Event::KeyUp(ku) => {
                if cx.keyboard.has_key_focus(area) {
                    return Hit::KeyUp(ku.clone())
                }
            },
            Event::TextInput(ti) => {
                if cx.keyboard.has_key_focus(area) {
                    return Hit::TextInput(ti.clone())
                }
            },
            Event::TextCopy(tc) => {
                if cx.keyboard.has_key_focus(area) {
                    return Hit::TextCopy(tc.clone());
                }
            },
            Event::TextCut(tc) => {
                if cx.keyboard.has_key_focus(area) {
                    return Hit::TextCut(tc.clone());
                }
            },
            Event::Scroll(e) => {
                let digit_id = live_id!(mouse).into();
                
                let rect = area.get_clipped_rect(&cx);
                if Margin::rect_contains_with_margin(&rect, e.abs, &options.margin) {
                    let device = DigitDevice::Mouse {
                        button: 0,
                    };
                    return Hit::FingerScroll(FingerScrollEvent {
                        abs: e.abs,
                        rect,
                        window_id: e.window_id,
                        digit_id,
                        device,
                        modifiers: e.modifiers.clone(),
                        time: e.time,
                        scroll: e.scroll
                    })
                }
            },
            Event::TouchUpdate(e) => {
                if cx.fingers.test_sweep_lock(options.sweep_area) {
                    return Hit::Nothing
                }
                for t in &e.touches {
                    let digit_id = live_id_num!(touch, t.uid).into();
                    let device = DigitDevice::Touch {
                        uid: t.uid,
                    };
                    
                    match t.state {
                        TouchState::Start => {
                            
                            if !options.capture_overload && !t.handled.get().is_empty() {
                                continue;
                            }
                            
                            if cx.fingers.get_area_capture(area).is_some(){
                                continue;
                            }
                            
                            let rect = area.get_clipped_rect(&cx);
                            if !Margin::rect_contains_with_margin(&rect, t.abs, &options.margin) {
                                continue;
                            }
                            
                            cx.fingers.capture_digit(digit_id, area, options.sweep_area, e.time, t.abs);
                            
                            t.handled.set(area);
                            return Hit::FingerDown(FingerDownEvent {
                                window_id: e.window_id,
                                abs: t.abs,
                                digit_id,
                                device,
                                tap_count: cx.fingers.get_tap_count(),
                                modifiers: e.modifiers.clone(),
                                time: e.time,
                                rect,
                            })
                        }
                        TouchState::Stop => {
                            let tap_count = cx.fingers.get_tap_count();
                            let rect = area.get_clipped_rect(&cx);
                            if let Some(capture) = cx.fingers.get_area_capture(area) {
                                return Hit::FingerUp(FingerUpEvent {
                                    abs_start: capture.abs_start,
                                    rect: rect,
                                    window_id: e.window_id,
                                    abs: t.abs,
                                    digit_id,
                                    device,
                                    tap_count,
                                    capture_time: capture.time,
                                    modifiers: e.modifiers.clone(),
                                    time: e.time,
                                    is_over: rect.contains(t.abs),
                                    is_sweep: false,
                                })
                            }
                        }
                        TouchState::Move => {
                            let tap_count = cx.fingers.get_tap_count();
                            let rect = area.get_clipped_rect(&cx);
                            
                            if !options.sweep_area.is_empty() {
                                if let Some(capture) = cx.fingers.get_digit_capture(digit_id) {
                                    if capture.switch_capture.is_none()
                                        && Margin::rect_contains_with_margin(&rect, t.abs, &options.margin) {
                                        if t.handled.get().is_empty() {
                                            t.handled.set(area);
                                            if capture.area == area {
                                                return Hit::FingerMove(FingerMoveEvent {
                                                    window_id: e.window_id,
                                                    abs: t.abs,
                                                    digit_id,
                                                    device,
                                                    tap_count,
                                                    modifiers: e.modifiers.clone(),
                                                    time: e.time,
                                                    abs_start: capture.abs_start,
                                                    rect,
                                                    is_over: true,
                                                })
                                            }
                                            else if capture.sweep_area == options.sweep_area { capture.switch_capture = Some(area);
                                                return Hit::FingerDown(FingerDownEvent {
                                                    window_id: e.window_id,
                                                    abs: t.abs,
                                                    digit_id,
                                                    device,
                                                    tap_count: cx.fingers.get_tap_count(),
                                                    modifiers: e.modifiers.clone(),
                                                    time: e.time,
                                                    rect: rect,
                                                })
                                            }
                                        }
                                    }
                                    else if capture.area == area { if capture.switch_capture.is_none() {
                                            capture.switch_capture = Some(Area::Empty);
                                        }
                                        return Hit::FingerUp(FingerUpEvent {
                                            abs_start: capture.abs_start,
                                            rect: rect,
                                            window_id: e.window_id,
                                            abs: t.abs,
                                            digit_id,
                                            device,
                                            tap_count,
                                            capture_time: capture.time,
                                            modifiers: e.modifiers.clone(),
                                            time: e.time,
                                            is_sweep: true,
                                            is_over: false,
                                        });
                                    }
                                }
                            }
                            else if let Some(capture) = cx.fingers.get_area_capture(area) {
                                return Hit::FingerMove(FingerMoveEvent {
                                    window_id: e.window_id,
                                    abs: t.abs,
                                    digit_id,
                                    device,
                                    tap_count,
                                    modifiers: e.modifiers.clone(),
                                    time: e.time,
                                    abs_start: capture.abs_start,
                                    rect,
                                    is_over: Margin::rect_contains_with_margin(&rect, t.abs, &options.margin),
                                })
                            }
                        }
                        TouchState::Stable => {}
                    }
                }
            }
            Event::MouseMove(e) => { if cx.fingers.test_sweep_lock(options.sweep_area) {
                    return Hit::Nothing
                }
                
                let digit_id = live_id!(mouse).into();
                
                let tap_count = cx.fingers.get_tap_count();
                let hover_last = cx.fingers.get_hover_area(digit_id);
                let rect = area.get_clipped_rect(&cx);
                
                if let Some(button) = cx.fingers.first_mouse_button {
                    let device = DigitDevice::Mouse {
                        button,
                    };
                    if !options.sweep_area.is_empty() {
                        if let Some(capture) = cx.fingers.get_digit_capture(digit_id) {
                            if capture.switch_capture.is_none()
                                && Margin::rect_contains_with_margin(&rect, e.abs, &options.margin) {
                                if e.handled.get().is_empty() {
                                    e.handled.set(area);
                                    if capture.area == area {
                                        return Hit::FingerMove(FingerMoveEvent {
                                            window_id: e.window_id,
                                            abs: e.abs,
                                            digit_id,
                                            device,
                                            tap_count,
                                            modifiers: e.modifiers.clone(),
                                            time: e.time,
                                            abs_start: capture.abs_start,
                                            rect,
                                            is_over: true,
                                        })
                                    }
                                    else if capture.sweep_area == options.sweep_area { capture.switch_capture = Some(area);
                                        cx.fingers.new_hover_area(digit_id, area);
                                        return Hit::FingerDown(FingerDownEvent {
                                            window_id: e.window_id,
                                            abs: e.abs,
                                            digit_id,
                                            device,
                                            tap_count: cx.fingers.get_tap_count(),
                                            modifiers: e.modifiers.clone(),
                                            time: e.time,
                                            rect,
                                        })
                                    }
                                }
                            }
                            else if capture.area == area { if capture.switch_capture.is_none() {
                                    capture.switch_capture = Some(Area::Empty);
                                }
                                return Hit::FingerUp(FingerUpEvent {
                                    abs_start: capture.abs_start,
                                    rect,
                                    window_id: e.window_id,
                                    abs: e.abs,
                                    digit_id,
                                    device,
                                    tap_count,
                                    capture_time: capture.time,
                                    modifiers: e.modifiers.clone(),
                                    time: e.time,
                                    is_sweep: true,
                                    is_over: false,
                                });
                                
                            }
                        }
                    }
                    else if let Some(capture) = cx.fingers.get_area_capture(area) {
                        let event = Hit::FingerMove(FingerMoveEvent {
                            window_id: e.window_id,
                            abs: e.abs,
                            digit_id,
                            device,
                            tap_count,
                            modifiers: e.modifiers.clone(),
                            time: e.time,
                            abs_start: capture.abs_start,
                            rect,
                            is_over: Margin::rect_contains_with_margin(&rect, e.abs, &options.margin),
                        });
                        cx.fingers.new_hover_area(digit_id, area);
                        return event
                    }
                }
                else {
                    let device = DigitDevice::Mouse {
                        button: 0,
                    };
                    
                    let handled_area = e.handled.get();
                    
                    let fhe = FingerHoverEvent {
                        window_id: e.window_id,
                        abs: e.abs,
                        digit_id,
                        device,
                        modifiers: e.modifiers.clone(),
                        time: e.time,
                        rect,
                    };
                    
                    if hover_last == area {
                        if handled_area.is_empty() && Margin::rect_contains_with_margin(&rect, e.abs, &options.margin) {
                            e.handled.set(area);
                            cx.fingers.new_hover_area(digit_id, area);
                            return Hit::FingerHoverOver(fhe)
                        }
                        else {
                            return Hit::FingerHoverOut(fhe)
                        }
                    }
                    else {
                        if handled_area.is_empty() && Margin::rect_contains_with_margin(&rect, e.abs, &options.margin) {
                            cx.fingers.new_hover_area(digit_id, area);
                            e.handled.set(area);
                            return Hit::FingerHoverIn(fhe)
                        }
                    }
                }
            },
            Event::MouseDown(e) => {
                if cx.fingers.test_sweep_lock(options.sweep_area) {
                    return Hit::Nothing
                }
                
                let digit_id = live_id!(mouse).into();
                
                if !options.capture_overload && !e.handled.get().is_empty() {
                    return Hit::Nothing
                }
                
                if cx.fingers.first_mouse_button != Some(e.button) {
                    return Hit::Nothing
                }
                
                let rect = area.get_clipped_rect(&cx);
                if !Margin::rect_contains_with_margin(&rect, e.abs, &options.margin) {
                    return Hit::Nothing
                }
                
                let device = DigitDevice::Mouse {
                    button: e.button,
                };
                
                if cx.fingers.get_digit_for_captured_area(area).is_some() {
                    return Hit::Nothing;
                }
                
                cx.fingers.capture_digit(digit_id, area, options.sweep_area, e.time, e.abs);
                e.handled.set(area);
                cx.fingers.new_hover_area(digit_id, area);
                return Hit::FingerDown(FingerDownEvent {
                    window_id: e.window_id,
                    abs: e.abs,
                    digit_id,
                    device,
                    tap_count: cx.fingers.get_tap_count(),
                    modifiers: e.modifiers.clone(),
                    time: e.time,
                    rect: rect,
                })
            },
            Event::MouseUp(e) => {
                if cx.fingers.test_sweep_lock(options.sweep_area) {
                    return Hit::Nothing
                }
                
                if cx.fingers.first_mouse_button != Some(e.button) {
                    return Hit::Nothing
                }
                
                let digit_id = live_id!(mouse).into();
                
                let device = DigitDevice::Mouse {
                    button: e.button,
                };
                let tap_count = cx.fingers.get_tap_count();
                let rect = area.get_clipped_rect(&cx);
                
                if let Some(capture) = cx.fingers.get_area_capture(area) {
                    let is_over = rect.contains(e.abs);
                    let event = Hit::FingerUp(FingerUpEvent {
                        abs_start: capture.abs_start,
                        rect: rect,
                        window_id: e.window_id,
                        abs: e.abs,
                        digit_id,
                        device,
                        tap_count,
                        capture_time: capture.time,
                        modifiers: e.modifiers.clone(),
                        time: e.time,
                        is_over,
                        is_sweep: false,
                    });
                    if is_over {
                        cx.fingers.new_hover_area(digit_id, area);
                    }
                    return event
                }
            },
            _ => ()
        };
        Hit::Nothing
    }
}