use libc::c_char;
use std::ffi::{CString, CStr};
use GameControllerSubsystem;
use SdlResult;
use get_error;
use joystick;
use util::CStringExt;
use sys::controller as ll;
use sys::event::{SDL_QUERY, SDL_ENABLE};
impl GameControllerSubsystem {
        pub fn num_joysticks(&self) -> SdlResult<u32> {
        let result = unsafe { ::sys::joystick::SDL_NumJoysticks() };
        if result >= 0 {
            Ok(result as u32)
        } else {
            Err(get_error())
        }
    }
        #[inline]
    pub fn is_game_controller(&self, id: u32) -> bool {
        match u32_to_int!(id) {
            Ok(id) => unsafe { ll::SDL_IsGameController(id) != 0 },
            Err(..) => false
        }
    }
                    pub fn open(&self, id: u32) -> SdlResult<GameController> {
        let id = try!(u32_to_int!(id));
        let controller = unsafe { ll::SDL_GameControllerOpen(id) };
        if controller.is_null() {
            Err(get_error())
        } else {
            Ok(GameController {
                subsystem: self.clone(),
                raw: controller
            })
        }
    }
        pub fn name_for_index(&self, id: u32) -> SdlResult<String> {
        let id = try!(u32_to_int!(id));
        let name = unsafe { ll::SDL_GameControllerNameForIndex(id) };
        c_str_to_string_or_err(name)
    }
            pub fn set_event_state(&self, state: bool) {
        unsafe { ll::SDL_GameControllerEventState(state as i32) };
    }
        pub fn event_state(&self) -> bool {
        unsafe { ll::SDL_GameControllerEventState(SDL_QUERY as i32)
                 == SDL_ENABLE as i32 }
    }
        pub fn add_mapping(&self, mapping: &str) -> SdlResult<MappingStatus> {
        let mapping = try!(CString::new(mapping).unwrap_or_sdlresult());
        let result = unsafe { ll::SDL_GameControllerAddMapping(mapping.as_ptr() as *const c_char) };
        match result {
            1 => Ok(MappingStatus::Added),
            0 => Ok(MappingStatus::Updated),
            _ => Err(get_error()),
        }
    }
    pub fn mapping_for_guid(&self, guid: joystick::Guid) -> SdlResult<String> {
        let c_str = unsafe { ll::SDL_GameControllerMappingForGUID(guid.raw()) };
        c_str_to_string_or_err(c_str)
    }
    #[inline]
        pub fn update(&self) {
        unsafe { ll::SDL_GameControllerUpdate() };
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Axis {
    LeftX        = ll::SDL_CONTROLLER_AXIS_LEFTX,
    LeftY        = ll::SDL_CONTROLLER_AXIS_LEFTY,
    RightX       = ll::SDL_CONTROLLER_AXIS_RIGHTX,
    RightY       = ll::SDL_CONTROLLER_AXIS_RIGHTY,
    TriggerLeft  = ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TriggerRight = ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
}
impl Axis {
            pub fn from_string(axis: &str) -> Option<Axis> {
        let id = match CString::new(axis) {
            Ok(axis) => unsafe { ll::SDL_GameControllerGetAxisFromString(axis.as_ptr() as *const c_char) },
                        Err(_) => ll::SDL_CONTROLLER_AXIS_INVALID
        };
        Axis::from_ll(id)
    }
            pub fn string(self) -> String {
        let axis = self as ll::SDL_GameControllerAxis;
        let string = unsafe { ll::SDL_GameControllerGetStringForAxis(axis) };
        c_str_to_string(string)
    }
    pub fn from_ll(bitflags: ll::SDL_GameControllerAxis) -> Option<Axis> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_AXIS_INVALID      => return None,
            ll::SDL_CONTROLLER_AXIS_LEFTX        => Axis::LeftX,
            ll::SDL_CONTROLLER_AXIS_LEFTY        => Axis::LeftY,
            ll::SDL_CONTROLLER_AXIS_RIGHTX       => Axis::RightX,
            ll::SDL_CONTROLLER_AXIS_RIGHTY       => Axis::RightY,
            ll::SDL_CONTROLLER_AXIS_TRIGGERLEFT  => Axis::TriggerLeft,
            ll::SDL_CONTROLLER_AXIS_TRIGGERRIGHT => Axis::TriggerRight,
            _ => panic!("unhandled controller axis")
        })
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Button {
    A             = ll::SDL_CONTROLLER_BUTTON_A,
    B             = ll::SDL_CONTROLLER_BUTTON_B,
    X             = ll::SDL_CONTROLLER_BUTTON_X,
    Y             = ll::SDL_CONTROLLER_BUTTON_Y,
    Back          = ll::SDL_CONTROLLER_BUTTON_BACK,
    Guide         = ll::SDL_CONTROLLER_BUTTON_GUIDE,
    Start         = ll::SDL_CONTROLLER_BUTTON_START,
    LeftStick     = ll::SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RightStick    = ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LeftShoulder  = ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RightShoulder = ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPadUp        = ll::SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPadDown      = ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPadLeft      = ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPadRight     = ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
}
impl Button {
            pub fn from_string(button: &str) -> Option<Button> {
        let id = match CString::new(button) {
            Ok(button) => unsafe { ll::SDL_GameControllerGetButtonFromString(button.as_ptr() as *const c_char) },
                        Err(_) => ll::SDL_CONTROLLER_BUTTON_INVALID
        };
        Button::from_ll(id)
    }
            pub fn string(self) -> String {
        let button = self as ll::SDL_GameControllerButton;
        let string = unsafe { ll::SDL_GameControllerGetStringForButton(button) };
        c_str_to_string(string)
    }
    pub fn from_ll(bitflags: ll::SDL_GameControllerButton) -> Option<Button> {
        Some(match bitflags {
            ll::SDL_CONTROLLER_BUTTON_INVALID       => return None,
            ll::SDL_CONTROLLER_BUTTON_A             => Button::A,
            ll::SDL_CONTROLLER_BUTTON_B             => Button::B,
            ll::SDL_CONTROLLER_BUTTON_X             => Button::X,
            ll::SDL_CONTROLLER_BUTTON_Y             => Button::Y,
            ll::SDL_CONTROLLER_BUTTON_BACK          => Button::Back,
            ll::SDL_CONTROLLER_BUTTON_GUIDE         => Button::Guide,
            ll::SDL_CONTROLLER_BUTTON_START         => Button::Start,
            ll::SDL_CONTROLLER_BUTTON_LEFTSTICK     => Button::LeftStick,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSTICK    => Button::RightStick,
            ll::SDL_CONTROLLER_BUTTON_LEFTSHOULDER  => Button::LeftShoulder,
            ll::SDL_CONTROLLER_BUTTON_RIGHTSHOULDER => Button::RightShoulder,
            ll::SDL_CONTROLLER_BUTTON_DPAD_UP       => Button::DPadUp,
            ll::SDL_CONTROLLER_BUTTON_DPAD_DOWN     => Button::DPadDown,
            ll::SDL_CONTROLLER_BUTTON_DPAD_LEFT     => Button::DPadLeft,
            ll::SDL_CONTROLLER_BUTTON_DPAD_RIGHT    => Button::DPadRight,
            _ => panic!("unhandled controller button")
        })
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum MappingStatus {
    Added   = 1,
    Updated = 0,
}
pub struct GameController {
    subsystem: GameControllerSubsystem,
    raw: *mut ll::SDL_GameController
}
impl GameController {
    #[inline]
    pub fn subsystem(&self) -> &GameControllerSubsystem { &self.subsystem }
            pub fn name(&self) -> String {
        let name = unsafe { ll::SDL_GameControllerName(self.raw) };
        c_str_to_string(name)
    }
            pub fn mapping(&self) -> String {
        let mapping = unsafe { ll::SDL_GameControllerMapping(self.raw) };
        c_str_to_string(mapping)
    }
            pub fn attached(&self) -> bool {
        unsafe { ll::SDL_GameControllerGetAttached(self.raw) != 0 }
    }
        pub fn axis(&self, axis: Axis) -> i16 {
                                
        let axis = axis as ll::SDL_GameControllerAxis;
        unsafe { ll::SDL_GameControllerGetAxis(self.raw, axis) }
    }
        pub fn button(&self, button: Button) -> bool {
                                
        let button = button as ll::SDL_GameControllerButton;
        unsafe { ll::SDL_GameControllerGetButton(self.raw, button) != 0 }
    }
}
impl Drop for GameController {
    fn drop(&mut self) {
        unsafe { ll::SDL_GameControllerClose(self.raw) }
    }
}
fn c_str_to_string(c_str: *const c_char) -> String {
    if c_str.is_null() {
        String::new()
    } else {
        let bytes = unsafe { CStr::from_ptr(c_str as *const _).to_bytes() };
        String::from_utf8_lossy(bytes).to_string()
    }
}
fn c_str_to_string_or_err(c_str: *const c_char) -> SdlResult<String> {
    if c_str.is_null() {
        Err(get_error())
    } else {
        let bytes = unsafe { CStr::from_ptr(c_str as *const _).to_bytes() };
        Ok(String::from_utf8_lossy(bytes).to_string())
    }
}