use std::ffi::CStr;
use std::mem;
use libc::{c_int, c_void, uint32_t};
use num::FromPrimitive;
use std::ptr;
use std::borrow::ToOwned;
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::collections::HashMap;
use std::sync::Mutex;
use controller;
use controller::{Axis, Button};
use joystick;
use joystick::HatState;
use keyboard;
use keyboard::Mod;
use sys::keycode::SDL_Keymod;
use keyboard::Keycode;
use mouse;
use mouse::{Mouse, MouseState};
use keyboard::Scancode;
use get_error;
use SdlResult;
use ErrorMessage;
use sys::event as ll;
struct CustomEventTypeMaps {
    sdl_id_to_type_id: HashMap<u32, ::std::any::TypeId>,
    type_id_to_sdl_id: HashMap<::std::any::TypeId, u32>
}
impl CustomEventTypeMaps {
    fn new() -> Self {
        CustomEventTypeMaps {
            sdl_id_to_type_id: HashMap::new(),
            type_id_to_sdl_id: HashMap::new()
        }
    }
}
lazy_static! {
    static ref CUSTOM_EVENT_TYPES : Mutex<CustomEventTypeMaps> = { Mutex::new(CustomEventTypeMaps::new()) };
}
impl ::EventSubsystem {
        pub fn flush_event(&self, event_type: EventType) {
        unsafe { ll::SDL_FlushEvent(event_type as uint32_t) };
    }
        pub fn flush_events(&self, min_type: u32, max_type: u32) {
        unsafe { ll::SDL_FlushEvents(min_type, max_type) };
    }
                                                                                    pub fn peek_events<B>(&self, max_amount: u32) -> B
    where B: FromIterator<Event>
    {
        unsafe {
            let mut events = Vec::with_capacity(max_amount as usize);
            let result = {
                let events_ptr = events.as_mut_ptr();
                ll::SDL_PeepEvents(
                    events_ptr,
                    max_amount as c_int,
                    ll::SDL_PEEKEVENT,
                    ll::SDL_FIRSTEVENT,
                    ll::SDL_LASTEVENT
                )
            };
            if result < 0 {
                                panic!(get_error());
            } else {
                events.set_len(result as usize);
                events.into_iter().map(|event_raw| {
                    Event::from_ll(event_raw)
                }).collect()
            }
        }
    }
        pub fn push_event(&self, event: Event) -> SdlResult<()> {
        match event.to_ll() {
            Some(mut raw_event) => {
                let ok = unsafe { ll::SDL_PushEvent(&mut raw_event) == 1 };
                if ok { Ok(()) }
                else { Err(get_error()) }
            },
            None => {
                Err(ErrorMessage("Cannot push unsupported event type to the queue".into()))
            }
        }
    }
                                                                                                                #[inline(always)]
    pub unsafe fn register_event(&self) -> SdlResult<u32> {
        Ok(*try!(self.register_events(1)).first().unwrap())
    }
                pub unsafe fn register_events(&self, nr: u32) -> SdlResult<Vec<u32>> {
        let result = unsafe { ll::SDL_RegisterEvents(nr as ::libc::c_int) };
        const ERR_NR:u32 = ::std::u32::MAX - 1;
        match result {
            ERR_NR => { Err(ErrorMessage("No more user events can be created; SDL_LASTEVENT reached".to_owned())) },
            _ => {
                let event_ids = (result..(result+nr)).collect();
                Ok(event_ids)
            }
        }
    }
                            #[inline(always)]
    pub fn register_custom_event<T: ::std::any::Any>(&self) -> SdlResult<()> {
        use ::std::any::TypeId;
        let event_id = *try!(unsafe { self.register_events(1) }).first().unwrap();
        let mut cet = CUSTOM_EVENT_TYPES.lock().unwrap();
        let type_id = TypeId::of::<Box<T>>();
        if cet.type_id_to_sdl_id.contains_key(&type_id) {
            return Err(ErrorMessage("Can not register the same event type twice!".into()));
        }
        cet.sdl_id_to_type_id.insert(event_id, type_id);
        cet.type_id_to_sdl_id.insert(type_id, event_id);
        Ok(())
    }
                                                                                                                    pub fn push_custom_event<T: ::std::any::Any>(&self, event:T) -> SdlResult<()> {
        use ::std::any::TypeId;
        let cet = CUSTOM_EVENT_TYPES.lock().unwrap();
        let type_id = TypeId::of::<Box<T>>();
        let user_event_id = *match cet.type_id_to_sdl_id.get(&type_id) {
            Some(id) => id,
            None => { return Err(ErrorMessage("Type is not registered as a custom event type!".into())); }
        };
        let event_box = Box::new(event);
        let type_id_box = Box::new(type_id);
        let event = Event::User {
           timestamp: 0,
           window_id: 0,
           type_: user_event_id,
           code: 0,
           data1: Box::into_raw(event_box) as *mut ::libc::c_void,
           data2: ::std::ptr::null_mut()
        };
        self.push_event(event);
        Ok(())
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(u32)]
pub enum EventType {
    First = ll::SDL_FIRSTEVENT,
    Quit = ll::SDL_QUIT,
    AppTerminating = ll::SDL_APP_TERMINATING,
    AppLowMemory = ll::SDL_APP_LOWMEMORY,
    AppWillEnterBackground = ll::SDL_APP_WILLENTERBACKGROUND,
    AppDidEnterBackground = ll::SDL_APP_DIDENTERBACKGROUND,
    AppWillEnterForeground = ll::SDL_APP_WILLENTERFOREGROUND,
    AppDidEnterForeground = ll::SDL_APP_DIDENTERFOREGROUND,
    Window = ll::SDL_WINDOWEVENT,
    
    KeyDown = ll::SDL_KEYDOWN,
    KeyUp = ll::SDL_KEYUP,
    TextEditing = ll::SDL_TEXTEDITING,
    TextInput = ll::SDL_TEXTINPUT,
    MouseMotion = ll::SDL_MOUSEMOTION,
    MouseButtonDown = ll::SDL_MOUSEBUTTONDOWN,
    MouseButtonUp = ll::SDL_MOUSEBUTTONUP,
    MouseWheel = ll::SDL_MOUSEWHEEL,
    JoyAxisMotion = ll::SDL_JOYAXISMOTION,
    JoyBallMotion = ll::SDL_JOYBALLMOTION,
    JoyHatMotion = ll::SDL_JOYHATMOTION,
    JoyButtonDown = ll::SDL_JOYBUTTONDOWN,
    JoyButtonUp = ll::SDL_JOYBUTTONUP,
    JoyDeviceAdded = ll::SDL_JOYDEVICEADDED,
    JoyDeviceRemoved = ll::SDL_JOYDEVICEREMOVED,
    ControllerAxisMotion = ll::SDL_CONTROLLERAXISMOTION,
    ControllerButtonDown = ll::SDL_CONTROLLERBUTTONDOWN,
    ControllerButtonUp = ll::SDL_CONTROLLERBUTTONUP,
    ControllerDeviceAdded = ll::SDL_CONTROLLERDEVICEADDED,
    ControllerDeviceRemoved = ll::SDL_CONTROLLERDEVICEREMOVED,
    ControllerDeviceRemapped = ll::SDL_CONTROLLERDEVICEREMAPPED,
    FingerDown = ll::SDL_FINGERDOWN,
    FingerUp = ll::SDL_FINGERUP,
    FingerMotion = ll::SDL_FINGERMOTION,
    DollarGesture = ll::SDL_DOLLARGESTURE,
    DollarRecord = ll::SDL_DOLLARRECORD,
    MultiGesture = ll::SDL_MULTIGESTURE,
    ClipboardUpdate = ll::SDL_CLIPBOARDUPDATE,
    DropFile = ll::SDL_DROPFILE,
    User = ll::SDL_USEREVENT,
    Last = ll::SDL_LASTEVENT,
}
impl FromPrimitive for EventType {
    fn from_i64(n: i64) -> Option<EventType> {
        use self::EventType::*;
        Some( match n as ll::SDL_EventType {
            ll::SDL_FIRSTEVENT => First,
            ll::SDL_QUIT => Quit,
            ll::SDL_APP_TERMINATING => AppTerminating,
            ll::SDL_APP_LOWMEMORY => AppLowMemory,
            ll::SDL_APP_WILLENTERBACKGROUND => AppWillEnterBackground,
            ll::SDL_APP_DIDENTERBACKGROUND => AppDidEnterBackground,
            ll::SDL_APP_WILLENTERFOREGROUND => AppWillEnterForeground,
            ll::SDL_APP_DIDENTERFOREGROUND => AppDidEnterForeground,
            ll::SDL_WINDOWEVENT => Window,
            ll::SDL_KEYDOWN => KeyDown,
            ll::SDL_KEYUP => KeyUp,
            ll::SDL_TEXTEDITING => TextEditing,
            ll::SDL_TEXTINPUT => TextInput,
            ll::SDL_MOUSEMOTION => MouseMotion,
            ll::SDL_MOUSEBUTTONDOWN => MouseButtonDown,
            ll::SDL_MOUSEBUTTONUP => MouseButtonUp,
            ll::SDL_MOUSEWHEEL => MouseWheel,
            ll::SDL_JOYAXISMOTION => JoyAxisMotion,
            ll::SDL_JOYBALLMOTION => JoyBallMotion,
            ll::SDL_JOYHATMOTION => JoyHatMotion,
            ll::SDL_JOYBUTTONDOWN => JoyButtonDown,
            ll::SDL_JOYBUTTONUP => JoyButtonUp,
            ll::SDL_JOYDEVICEADDED => JoyDeviceAdded,
            ll::SDL_JOYDEVICEREMOVED => JoyDeviceRemoved,
            ll::SDL_CONTROLLERAXISMOTION => ControllerAxisMotion,
            ll::SDL_CONTROLLERBUTTONDOWN => ControllerButtonDown,
            ll::SDL_CONTROLLERBUTTONUP => ControllerButtonUp,
            ll::SDL_CONTROLLERDEVICEADDED => ControllerDeviceAdded,
            ll::SDL_CONTROLLERDEVICEREMOVED => ControllerDeviceRemoved,
            ll::SDL_CONTROLLERDEVICEREMAPPED => ControllerDeviceRemapped,
            ll::SDL_FINGERDOWN => FingerDown,
            ll::SDL_FINGERUP => FingerUp,
            ll::SDL_FINGERMOTION => FingerMotion,
            ll::SDL_DOLLARGESTURE => DollarGesture,
            ll::SDL_DOLLARRECORD => DollarRecord,
            ll::SDL_MULTIGESTURE => MultiGesture,
            ll::SDL_CLIPBOARDUPDATE => ClipboardUpdate,
            ll::SDL_DROPFILE => DropFile,
            ll::SDL_USEREVENT => User,
            ll::SDL_LASTEVENT => Last,
            _ => return None,
        })
    }
    fn from_u64(n: u64) -> Option<EventType> { FromPrimitive::from_i64(n as i64) }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum WindowEventId {
    None,
    Shown,
    Hidden,
    Exposed,
    Moved,
    Resized,
    SizeChanged,
    Minimized,
    Maximized,
    Restored,
    Enter,
    Leave,
    FocusGained,
    FocusLost,
    Close,
}
impl WindowEventId {
    fn from_ll(id: u8) -> WindowEventId {
        match id {
            1  => WindowEventId::Shown,
            2  => WindowEventId::Hidden,
            3  => WindowEventId::Exposed,
            4  => WindowEventId::Moved,
            5  => WindowEventId::Resized,
            6  => WindowEventId::SizeChanged,
            7  => WindowEventId::Minimized,
            8  => WindowEventId::Maximized,
            9  => WindowEventId::Restored,
            10 => WindowEventId::Enter,
            11 => WindowEventId::Leave,
            12 => WindowEventId::FocusGained,
            13 => WindowEventId::FocusLost,
            14 => WindowEventId::Close,
            _  => WindowEventId::None
        }
    }
}
#[derive(Clone, PartialEq)]
pub enum Event {
    Quit { timestamp: u32 },
    AppTerminating { timestamp: u32 },
    AppLowMemory { timestamp: u32 },
    AppWillEnterBackground { timestamp: u32 },
    AppDidEnterBackground { timestamp: u32 },
    AppWillEnterForeground { timestamp: u32 },
    AppDidEnterForeground { timestamp: u32 },
    Window {
        timestamp: u32 ,
        window_id: u32,
        win_event_id: WindowEventId,
        data1: i32,
        data2: i32
    },
    
    KeyDown {
        timestamp: u32 ,
        window_id: u32,
        keycode: Option<Keycode>,
        scancode: Option<Scancode>,
        keymod: Mod,
        repeat: bool
    },
    KeyUp {
        timestamp: u32 ,
        window_id: u32,
        keycode: Option<Keycode>,
        scancode: Option<Scancode>,
        keymod: Mod,
        repeat: bool
    },
    TextEditing {
        timestamp: u32,
        window_id: u32,
        text: String,
        start: i32,
        length: i32
    },
    TextInput {
        timestamp: u32,
        window_id: u32,
        text: String
    },
    MouseMotion {
        timestamp: u32,
        window_id: u32,
        which: u32,
        mousestate: MouseState,
        x: i32,
        y: i32,
        xrel: i32,
        yrel: i32
    },
    MouseButtonDown {
        timestamp: u32,
        window_id: u32,
        which: u32,
        mouse_btn: Mouse,
        x: i32,
        y: i32
    },
    MouseButtonUp {
        timestamp: u32,
        window_id: u32,
        which: u32,
        mouse_btn: Mouse,
        x: i32,
        y: i32
    },
    MouseWheel {
        timestamp: u32,
        window_id: u32,
        which: u32,
        x: i32,
        y: i32
    },
    JoyAxisMotion {
        timestamp: u32,
        which: i32,
        axis_idx: u8,
        value: i16
    },
    JoyBallMotion {
        timestamp: u32,
        which: i32,
        ball_idx: u8,
        xrel: i16,
        yrel: i16
    },
    JoyHatMotion {
        timestamp: u32,
        which: i32,
        hat_idx: u8,
        state: HatState
    },
    JoyButtonDown {
        timestamp: u32,
        which: i32,
        button_idx: u8
    },
    JoyButtonUp {
        timestamp: u32,
        which: i32,
        button_idx: u8
    },
    JoyDeviceAdded {
        timestamp: u32,
        which: i32
    },
    JoyDeviceRemoved {
        timestamp: u32,
        which: i32
    },
    ControllerAxisMotion {
        timestamp: u32,
        which: i32,
        axis: Axis,
        value: i16
    },
    ControllerButtonDown {
        timestamp: u32,
        which: i32,
        button: Button
    },
    ControllerButtonUp {
        timestamp: u32,
        which: i32,
        button: Button
    },
    ControllerDeviceAdded {
        timestamp: u32,
        which: i32
    },
    ControllerDeviceRemoved {
        timestamp: u32,
        which: i32
    },
    ControllerDeviceRemapped {
        timestamp: u32,
        which: i32
    },
    FingerDown {
        timestamp: u32,
        touch_id: i64,
        finger_id: i64,
        x: f32,
        y: f32,
        dx: f32,
        dy: f32,
        pressure: f32
    },
    FingerUp {
        timestamp: u32,
        touch_id: i64,
        finger_id: i64,
        x: f32,
        y: f32,
        dx: f32,
        dy: f32,
        pressure: f32
    },
    FingerMotion {
        timestamp: u32,
        touch_id: i64,
        finger_id: i64,
        x: f32,
        y: f32,
        dx: f32,
        dy: f32,
        pressure: f32
    },
    DollarGesture {
        timestamp: u32,
        touch_id: i64,
        gesture_id: i64,
        num_fingers: u32,
        error: f32,
        x: f32,
        y: f32
    },
    DollarRecord {
        timestamp: u32,
        touch_id: i64,
        gesture_id: i64,
        num_fingers: u32,
        error: f32,
        x: f32,
        y: f32
    },
    MultiGesture {
        timestamp: u32,
        touch_id: i64,
        d_theta: f32,
        d_dist: f32,
        x: f32,
        y: f32,
        num_fingers: u16
    },
    ClipboardUpdate {
        timestamp: u32
    },
    DropFile {
        timestamp: u32,
        filename: String
    },
    User {
        timestamp: u32,
        window_id: u32,
        type_: u32,
        code: i32,
        data1: *mut c_void,
        data2: *mut c_void
    },
    Unknown {
        timestamp: u32,
        type_: u32
    }
}
impl ::std::fmt::Debug for Event {
    fn fmt(&self, out: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        out.write_str(match *self {
            Event::Quit{..} => "Event::Quit",
            Event::AppTerminating{..} => "Event::AppTerminating",
            Event::AppLowMemory{..} => "Event::AppLowMemory",
            Event::AppWillEnterBackground{..} => "Event::AppWillEnterBackground",
            Event::AppDidEnterBackground{..} => "Event::AppDidEnterBackground",
            Event::AppWillEnterForeground{..} => "Event::AppWillEnterForeground",
            Event::AppDidEnterForeground{..} => "Event::AppDidEnterForeground",
            Event::Window{..} => "Event::Window",
            Event::KeyDown{..} => "Event::KeyDown",
            Event::KeyUp{..} => "Event::KeyUp",
            Event::TextEditing{..} => "Event::TextEditing",
            Event::TextInput{..} => "Event::TextInput",
            Event::MouseMotion{..} => "Event::MouseMotion",
            Event::MouseButtonDown{..} => "Event::MouseButtonDown",
            Event::MouseButtonUp{..} => "Event::MouseButtonUp",
            Event::MouseWheel{..} => "Event::MouseWheel",
            Event::JoyAxisMotion{..} => "Event::JoyAxisMotion",
            Event::JoyBallMotion{..} => "Event::JoyBallMotion",
            Event::JoyHatMotion{..} => "Event::JoyHatMotion",
            Event::JoyButtonDown{..} => "Event::JoyButtonDown",
            Event::JoyButtonUp{..} => "Event::JoyButtonUp",
            Event::JoyDeviceAdded{..} => "Event::JoyDeviceAdded",
            Event::JoyDeviceRemoved{..} => "Event::JoyDeviceRemoved",
            Event::ControllerAxisMotion{..} => "Event::ControllerAxisMotion",
            Event::ControllerButtonDown{..} => "Event::ControllerButtonDown",
            Event::ControllerButtonUp{..} => "Event::ControllerButtonUp",
            Event::ControllerDeviceAdded{..} => "Event::ControllerDeviceAdded",
            Event::ControllerDeviceRemoved{..} => "Event::ControllerDeviceRemoved",
            Event::ControllerDeviceRemapped{..} => "Event::ControllerDeviceRemapped",
            Event::FingerDown{..} => "Event::FingerDown",
            Event::FingerUp{..} => "Event::FingerUp",
            Event::FingerMotion{..} => "Event::FingerMotion",
            Event::DollarGesture{..} => "Event::DollarGesture",
            Event::DollarRecord{..} => "Event::DollarRecord",
            Event::MultiGesture{..} => "Event::MultiGesture",
            Event::ClipboardUpdate{..} => "Event::ClipboardUpdate",
            Event::DropFile{..} => "Event::DropFile",
            Event::User{..} => "Event::User",
            Event::Unknown{..} => "Event::Unknown",
        })
    }
}
impl Event {
    fn to_ll(self) -> Option<ll::SDL_Event> {
        let mut ret = unsafe { mem::uninitialized() };
        match self {
            Event::User { window_id, type_, code, data1, data2, timestamp} => {
                let event = ll::SDL_UserEvent {
                    type_: type_ as uint32_t,
                    timestamp: timestamp,
                    windowID: window_id,
                    code: code as i32,
                    data1: data1,
                    data2: data2
                };
                unsafe {
                    ptr::copy(&event, &mut ret as *mut ll::SDL_Event as *mut ll::SDL_UserEvent, 1);
                }
                Some(ret)
            },
            _ => {
                                None
            }
        }
    }
    fn from_ll(mut raw: ll::SDL_Event) -> Event {
        let raw_type = raw.type_();
        let raw_type = if raw_type.is_null() {
            panic!("Event payload is null")
        } else {
            unsafe { *raw_type }
        };
                let event_type: EventType = FromPrimitive::from_usize(raw_type as usize).unwrap_or(EventType::User);
        unsafe { match event_type {
            EventType::Quit => {
                let ref event = *raw.quit();
                Event::Quit { timestamp: event.timestamp }
            }
            EventType::AppTerminating => {
                let ref event = *raw.common();
                Event::AppTerminating { timestamp: event.timestamp }
            }
            EventType::AppLowMemory => {
                let ref event = *raw.common();
                Event::AppLowMemory { timestamp: event.timestamp }
            }
            EventType::AppWillEnterBackground => {
                let ref event = *raw.common();
                Event::AppWillEnterBackground { timestamp: event.timestamp }
            }
            EventType::AppDidEnterBackground => {
                let ref event = *raw.common();
                Event::AppDidEnterBackground { timestamp: event.timestamp }
            }
            EventType::AppWillEnterForeground => {
                let ref event = *raw.common();
                Event::AppWillEnterForeground { timestamp: event.timestamp }
            }
            EventType::AppDidEnterForeground => {
                let ref event = *raw.common();
                Event::AppDidEnterForeground { timestamp: event.timestamp }
            }
            EventType::Window => {
                let ref event = *raw.window();
                Event::Window {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    win_event_id: WindowEventId::from_ll(event.event),
                    data1: event.data1,
                    data2: event.data2
                }
            }
            
            EventType::KeyDown => {
                let ref event = *raw.key();
                Event::KeyDown {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    keycode: FromPrimitive::from_i32(event.keysym.sym),
                    scancode: FromPrimitive::from_u32(event.keysym.scancode),
                    keymod: keyboard::Mod::from_bits(event.keysym._mod as SDL_Keymod).unwrap(),
                    repeat: event.repeat != 0
                }
            }
            EventType::KeyUp => {
                let ref event = *raw.key();
                Event::KeyUp {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    keycode: FromPrimitive::from_i32(event.keysym.sym),
                    scancode: FromPrimitive::from_u32(event.keysym.scancode),
                    keymod: keyboard::Mod::from_bits(event.keysym._mod as SDL_Keymod).unwrap(),
                    repeat: event.repeat != 0
                }
            }
            EventType::TextEditing => {
                let ref event = *raw.edit();
                let text = String::from_utf8_lossy(
                        &event.text.iter()
                            .take_while(|&b| (*b) != 0)
                            .map(|&b| b as u8)
                            .collect::<Vec<u8>>()
                    ).to_owned().into_owned();
                Event::TextEditing {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    text: text,
                    start: event.start,
                    length: event.length
                }
            }
            EventType::TextInput => {
                let ref event = *raw.text();
                let text = String::from_utf8_lossy(
                        &event.text.iter()
                            .take_while(|&b| (*b) != 0)
                            .map(|&b| b as u8)
                            .collect::<Vec<u8>>()
                    ).to_owned().into_owned();
                Event::TextInput {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    text: text
                }
            }
            EventType::MouseMotion => {
                let ref event = *raw.motion();
                Event::MouseMotion {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    which: event.which,
                    mousestate: mouse::MouseState::from_flags(event.state),
                    x: event.x,
                    y: event.y,
                    xrel: event.xrel,
                    yrel: event.yrel
                }
            }
            EventType::MouseButtonDown => {
                let ref event = *raw.button();
                Event::MouseButtonDown {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    which: event.which,
                    mouse_btn: mouse::Mouse::from_ll(event.button),
                    x: event.x,
                    y: event.y
                }
            }
            EventType::MouseButtonUp => {
                let ref event = *raw.button();
                Event::MouseButtonUp {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    which: event.which,
                    mouse_btn: mouse::Mouse::from_ll(event.button),
                    x: event.x,
                    y: event.y
                }
            }
            EventType::MouseWheel => {
                let ref event = *raw.wheel();
                Event::MouseWheel {
                    timestamp: event.timestamp,
                    window_id: event.windowID,
                    which: event.which,
                    x: event.x,
                    y: event.y
                }
            }
            EventType::JoyAxisMotion => {
                let ref event = *raw.jaxis();
                Event::JoyAxisMotion {
                    timestamp: event.timestamp,
                    which: event.which,
                    axis_idx: event.axis,
                    value: event.value
                }
            }
            EventType::JoyBallMotion => {
                let ref event = *raw.jball();
                Event::JoyBallMotion {
                    timestamp: event.timestamp,
                    which: event.which,
                    ball_idx: event.ball,
                    xrel: event.xrel,
                    yrel: event.yrel
                }
            }
            EventType::JoyHatMotion => {
                let ref event = *raw.jhat();
                Event::JoyHatMotion {
                    timestamp: event.timestamp,
                    which: event.which,
                    hat_idx: event.hat,
                    state: joystick::HatState::from_raw(event.value),
                }
            }
            EventType::JoyButtonDown => {
                let ref event = *raw.jbutton();
                Event::JoyButtonDown {
                    timestamp: event.timestamp,
                    which: event.which,
                    button_idx: event.button
                }
            }
            EventType::JoyButtonUp => {
                let ref event = *raw.jbutton();
                Event::JoyButtonUp {
                    timestamp: event.timestamp,
                    which: event.which,
                    button_idx: event.button
                }
            }
            EventType::JoyDeviceAdded => {
                let ref event = *raw.jdevice();
                Event::JoyDeviceAdded {
                    timestamp: event.timestamp,
                    which: event.which
                }
            }
            EventType::JoyDeviceRemoved => {
                let ref event = *raw.jdevice();
                Event::JoyDeviceRemoved {
                    timestamp: event.timestamp,
                    which: event.which
                }
            }
            EventType::ControllerAxisMotion => {
                let ref event = *raw.caxis();
                let axis = controller::Axis::from_ll(event.axis as ::sys::controller::SDL_GameControllerAxis).unwrap();
                Event::ControllerAxisMotion {
                    timestamp: event.timestamp,
                    which: event.which,
                    axis: axis,
                    value: event.value
                }
            }
            EventType::ControllerButtonDown => {
                let ref event = *raw.cbutton();
                let button = controller::Button::from_ll(event.button as ::sys::controller::SDL_GameControllerButton).unwrap();
                Event::ControllerButtonDown {
                    timestamp: event.timestamp,
                    which: event.which,
                    button: button
                }
            }
            EventType::ControllerButtonUp => {
                let ref event = *raw.cbutton();
                let button = controller::Button::from_ll(event.button as ::sys::controller::SDL_GameControllerButton).unwrap();
                Event::ControllerButtonUp {
                    timestamp: event.timestamp,
                    which: event.which,
                    button: button
                }
            }
            EventType::ControllerDeviceAdded => {
                let ref event = *raw.cdevice();
                Event::ControllerDeviceAdded {
                    timestamp: event.timestamp,
                    which: event.which
                }
            }
            EventType::ControllerDeviceRemoved => {
                let ref event = *raw.cdevice();
                Event::ControllerDeviceRemoved {
                    timestamp: event.timestamp,
                    which: event.which
                }
            }
            EventType::ControllerDeviceRemapped => {
                let ref event = *raw.cdevice();
                Event::ControllerDeviceRemapped {
                    timestamp: event.timestamp,
                    which: event.which
                }
            }
            EventType::FingerDown => {
                let ref event = *raw.tfinger();
                Event::FingerDown {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    finger_id: event.fingerId,
                    x: event.x,
                    y: event.y,
                    dx: event.dx,
                    dy: event.dy,
                    pressure: event.pressure
                }
            }
            EventType::FingerUp => {
                let ref event = *raw.tfinger();
                Event::FingerUp {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    finger_id: event.fingerId,
                    x: event.x,
                    y: event.y,
                    dx: event.dx,
                    dy: event.dy,
                    pressure: event.pressure
                }
            }
            EventType::FingerMotion => {
                let ref event = *raw.tfinger();
                Event::FingerMotion {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    finger_id: event.fingerId,
                    x: event.x,
                    y: event.y,
                    dx: event.dx,
                    dy: event.dy,
                    pressure: event.pressure
                }
            }
            EventType::DollarGesture => {
                let ref event = *raw.dgesture();
                Event::DollarGesture {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    gesture_id: event.gestureId,
                    num_fingers: event.numFingers,
                    error: event.error,
                    x: event.x,
                    y: event.y
                }
            }
            EventType::DollarRecord => {
                let ref event = *raw.dgesture();
                Event::DollarRecord {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    gesture_id: event.gestureId,
                    num_fingers: event.numFingers,
                    error: event.error,
                    x: event.x,
                    y: event.y
                }
            }
            EventType::MultiGesture => {
                let ref event = *raw.mgesture();
                Event::MultiGesture {
                    timestamp: event.timestamp,
                    touch_id: event.touchId,
                    d_theta: event.dTheta,
                    d_dist: event.dDist,
                    x: event.x,
                    y: event.y,
                    num_fingers: event.numFingers
                }
            }
            EventType::ClipboardUpdate => {
                let ref event = *raw.common();
                Event::ClipboardUpdate {
                    timestamp: event.timestamp
                }
            }
            EventType::DropFile => {
                let ref event = *raw.drop();
                let buf = CStr::from_ptr(event.file as *const _).to_bytes();
                let text = String::from_utf8_lossy(buf).to_string();
                ll::SDL_free(event.file as *mut c_void);
                Event::DropFile {
                    timestamp: event.timestamp,
                    filename: text
                }
            }
            EventType::First => panic!("Unused event, EventType::First, was encountered"),
            EventType::Last => panic!("Unusable event, EventType::Last, was encountered"),
                                    EventType::User => {
                if raw_type < 32768 {
                                                            let ref event = *raw.common();
                    Event::Unknown {
                        timestamp: event.timestamp,
                        type_: event.type_
                    }
                } else {
                    let ref event = *raw.user();
                    Event::User {
                        timestamp: event.timestamp,
                        window_id: event.windowID,
                        type_: raw_type,
                        code: event.code,
                        data1: event.data1,
                        data2: event.data2
                    }
                }
            }
        }}                          }
    pub fn is_user_event(&self) -> bool {
        match self {
            &Event::User { .. } => true,
            _ => false
        }
    }
    pub fn as_user_event_type<T: ::std::any::Any>(&self) -> Option<T> {
        use ::std::any::TypeId;
        let type_id = TypeId::of::<Box<T>>();
        let (event_id, event_box_ptr) = match self {
            &Event::User { type_, data1, .. } => { (type_, data1) },
            _ => { return None }
        };
        let mut cet = CUSTOM_EVENT_TYPES.lock().unwrap();
        let event_type_id = match cet.sdl_id_to_type_id.get(&event_id) {
            Some(id) => id,
            None => { panic!("internal error; could not find typeid") }
        };
        if &type_id != event_type_id {
            return None;
        }
        let event_box : Box<T> = unsafe { Box::from_raw(event_box_ptr as *mut T) };
        Some(*event_box)
    }
}
unsafe fn poll_event() -> Option<Event> {
    let mut raw = mem::uninitialized();
    let has_pending = ll::SDL_PollEvent(&mut raw) == 1;
    if has_pending { Some(Event::from_ll(raw)) }
    else { None }
}
unsafe fn wait_event() -> Event {
    let mut raw = mem::uninitialized();
    let success = ll::SDL_WaitEvent(&mut raw) == 1;
    if success { Event::from_ll(raw) }
    else { panic!(get_error()) }
}
unsafe fn wait_event_timeout(timeout: u32) -> Option<Event> {
    let mut raw = mem::uninitialized();
    let success = ll::SDL_WaitEventTimeout(&mut raw, timeout as c_int) == 1;
    if success { Some(Event::from_ll(raw)) }
    else { None }
}
impl ::EventPump {
        pub fn is_event_enabled(&self, event_type: EventType) -> bool {
        let result = unsafe { ll::SDL_EventState(event_type as u32, ll::SDL_QUERY) };
        result != ll::SDL_DISABLE
    }
        pub fn enable_event(&mut self, event_type: EventType) -> bool {
        let result = unsafe { ll::SDL_EventState(event_type as u32, ll::SDL_ENABLE) };
        result != ll::SDL_DISABLE
    }
        pub fn disable_event(&mut self, event_type: EventType) -> bool {
        let result = unsafe { ll::SDL_EventState(event_type as u32, ll::SDL_DISABLE) };
        result != ll::SDL_DISABLE
    }
                pub fn poll_event(&mut self) -> Option<Event> {
        unsafe { poll_event() }
    }
                                                                    pub fn poll_iter(&mut self) -> EventPollIterator {
        EventPollIterator {
            _marker: PhantomData
        }
    }
        pub fn pump_events(&mut self) {
        unsafe { ll::SDL_PumpEvents(); };
    }
        pub fn wait_event(&mut self) -> Event {
        unsafe { wait_event() }
    }
        pub fn wait_event_timeout(&mut self, timeout: u32) -> Option<Event> {
        unsafe { wait_event_timeout(timeout) }
    }
                pub fn wait_iter(&mut self) -> EventWaitIterator {
        EventWaitIterator {
            _marker: PhantomData
        }
    }
                    pub fn wait_timeout_iter(&mut self, timeout: u32) -> EventWaitTimeoutIterator {
        EventWaitTimeoutIterator {
            _marker: PhantomData,
            timeout: timeout
        }
    }
    #[inline]
    pub fn keyboard_state(&self) -> ::keyboard::KeyboardState {
        ::keyboard::KeyboardState::new(self)
    }
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct EventPollIterator<'a> {
    _marker: PhantomData<&'a ()>
}
impl<'a> Iterator for EventPollIterator<'a> {
    type Item = Event;
    fn next(&mut self) -> Option<Event> { unsafe { poll_event() } }
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct EventWaitIterator<'a> {
    _marker: PhantomData<&'a ()>
}
impl<'a> Iterator for EventWaitIterator<'a> {
    type Item = Event;
    fn next(&mut self) -> Option<Event> { unsafe { Some(wait_event()) } }
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct EventWaitTimeoutIterator<'a> {
    _marker: PhantomData<&'a ()>,
    timeout: u32
}
impl<'a> Iterator for EventWaitTimeoutIterator<'a> {
    type Item = Event;
    fn next(&mut self) -> Option<Event> { unsafe { wait_event_timeout(self.timeout) } }
}