use std::{u8, i16};
use std::io::{Read, Seek, SeekFrom};
use byteorder::{ReadBytesExt, BigEndian, LittleEndian};
use {Result as Res, Error, Button};
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum State {
Power(bool),
Idle {
sequence: u32,
},
Input {
sequence: u32,
buttons: Button,
trigger: Trigger,
pad: Pad,
orientation: Angles,
acceleration: Angles,
}
}
#[derive(Clone, Copy, PartialEq, Default, Debug)]
pub struct Trigger {
pub left: f32,
pub right: f32,
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub struct Pad {
pub left: Axis,
pub right: Axis,
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub struct Axis {
pub x: i16,
pub y: i16,
}
impl Axis {
pub fn is_empty(&self) -> bool {
self.x == 0 && self.y == 0
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub struct Angles {
pub pitch: i16,
pub roll: i16,
pub yaw: i16,
}
impl State {
pub fn parse<R: Read + Seek>(id: u8, mut buffer: R) -> Res<State> {
match id {
0x01 => {
let sequence = try!(buffer.read_u32::<LittleEndian>());
let buttons = try!(buffer.read_u32::<BigEndian>());
let ltrig = buttons & 0xff;
let buttons = buttons >> 8;
let rtrig = try!(buffer.read_u8());
try!(buffer.seek(SeekFrom::Current(3)));
let lpad_x = try!(buffer.read_i16::<LittleEndian>());
let lpad_y = try!(buffer.read_i16::<LittleEndian>());
let rpad_x = try!(buffer.read_i16::<LittleEndian>());
let rpad_y = try!(buffer.read_i16::<LittleEndian>());
let ltrigp = try!(buffer.read_u16::<LittleEndian>());
let rtrigp = try!(buffer.read_u16::<LittleEndian>());
try!(buffer.seek(SeekFrom::Current(8)));
let apitch = try!(buffer.read_i16::<LittleEndian>());
let ayaw = try!(buffer.read_i16::<LittleEndian>());
let aroll = try!(buffer.read_i16::<LittleEndian>());
let opitch = try!(buffer.read_i16::<LittleEndian>());
let oyaw = try!(buffer.read_i16::<LittleEndian>());
let oroll = try!(buffer.read_i16::<LittleEndian>());
Ok(State::Input {
sequence: sequence,
buttons: try!(Button::from_bits(buttons).ok_or(Error::InvalidParameter)),
trigger: Trigger {
left: if ltrigp != 0 {
ltrigp as f32 / i16::max_value() as f32
}
else {
ltrig as f32 / u8::max_value() as f32
},
right: if rtrigp != 0 {
rtrigp as f32 / i16::max_value() as f32
}
else {
rtrig as f32 / u8::max_value() as f32
},
},
pad: Pad {
left: Axis {
x: lpad_x,
y: lpad_y,
},
right: Axis {
x: rpad_x,
y: rpad_y,
},
},
orientation: Angles {
roll: oroll,
pitch: opitch,
yaw: oyaw,
},
acceleration: Angles {
roll: aroll,
pitch: apitch,
yaw: ayaw,
}
})
}
0x03 => {
let mode = try!(buffer.read_u8());
Ok(State::Power(match mode {
0x01 => false,
0x02 => true,
_ =>
return Err(Error::InvalidParameter)
}))
}
0x04 => {
let sequence = try!(buffer.read_u32::<LittleEndian>());
Ok(State::Idle {
sequence: sequence,
})
}
_ =>
Err(Error::InvalidParameter)
}
}
}