///|
/// 3D Vector for sensor data
pub(all) struct Vec3 {
  x : Double
  y : Double
  z : Double
} derive(Show, Eq)

///|
/// Create a new 3D vector
pub fn Vec3::new(x : Double, y : Double, z : Double) -> Vec3 {
  { x, y, z }
}

///|
/// Zero vector
pub fn Vec3::zero() -> Vec3 {
  { x: 0.0, y: 0.0, z: 0.0 }
}

///|
/// Vector magnitude
pub fn Vec3::magnitude(self : Vec3) -> Double {
  (self.x * self.x + self.y * self.y + self.z * self.z).sqrt()
}

///|
/// Normalize vector
pub fn Vec3::normalize(self : Vec3) -> Vec3 {
  let mag = self.magnitude()
  if mag == 0.0 {
    Vec3::zero()
  } else {
    { x: self.x / mag, y: self.y / mag, z: self.z / mag }
  }
}

///|
/// Vector addition
pub fn Vec3::add(self : Vec3, other : Vec3) -> Vec3 {
  { x: self.x + other.x, y: self.y + other.y, z: self.z + other.z }
}

///|
/// Vector subtraction
pub fn Vec3::sub(self : Vec3, other : Vec3) -> Vec3 {
  { x: self.x - other.x, y: self.y - other.y, z: self.z - other.z }
}

///|
/// Scalar multiplication
pub fn Vec3::scale(self : Vec3, scalar : Double) -> Vec3 {
  { x: self.x * scalar, y: self.y * scalar, z: self.z * scalar }
}

///|
/// Dot product
pub fn Vec3::dot(self : Vec3, other : Vec3) -> Double {
  self.x * other.x + self.y * other.y + self.z * other.z
}

///|
/// Cross product
pub fn Vec3::cross(self : Vec3, other : Vec3) -> Vec3 {
  {
    x: self.y * other.z - self.z * other.y,
    y: self.z * other.x - self.x * other.z,
    z: self.x * other.y - self.y * other.x,
  }
}

///|
/// Quaternion for 3D rotation
pub(all) struct Quaternion {
  w : Double
  x : Double
  y : Double
  z : Double
} derive(Show, Eq)

///|
/// Create identity quaternion
pub fn Quaternion::identity() -> Quaternion {
  { w: 1.0, x: 0.0, y: 0.0, z: 0.0 }
}

///|
/// Create quaternion from axis and angle (in radians)
pub fn Quaternion::from_axis_angle(axis : Vec3, angle : Double) -> Quaternion {
  let half_angle = angle / 2.0
  let s = @math.sin(half_angle)
  let normalized = axis.normalize()
  {
    w: @math.cos(half_angle),
    x: normalized.x * s,
    y: normalized.y * s,
    z: normalized.z * s,
  }
}

///|
/// Quaternion multiplication
pub fn Quaternion::multiply(
  self : Quaternion,
  other : Quaternion,
) -> Quaternion {
  {
    w: self.w * other.w - self.x * other.x - self.y * other.y - self.z * other.z,
    x: self.w * other.x + self.x * other.w + self.y * other.z - self.z * other.y,
    y: self.w * other.y - self.x * other.z + self.y * other.w + self.z * other.x,
    z: self.w * other.z + self.x * other.y - self.y * other.x + self.z * other.w,
  }
}

///|
/// Normalize quaternion
pub fn Quaternion::normalize(self : Quaternion) -> Quaternion {
  let mag = (self.w * self.w +
  self.x * self.x +
  self.y * self.y +
  self.z * self.z).sqrt()
  if mag == 0.0 {
    Quaternion::identity()
  } else {
    { w: self.w / mag, x: self.x / mag, y: self.y / mag, z: self.z / mag }
  }
}

///|
/// Convert quaternion to Euler angles (roll, pitch, yaw in radians)
pub fn Quaternion::to_euler(self : Quaternion) -> (Double, Double, Double) {
  // Roll (x-axis rotation)
  let sinr_cosp = 2.0 * (self.w * self.x + self.y * self.z)
  let cosr_cosp = 1.0 - 2.0 * (self.x * self.x + self.y * self.y)
  let roll = @math.atan2(sinr_cosp, cosr_cosp)

  // Pitch (y-axis rotation)
  let sinp = 2.0 * (self.w * self.y - self.z * self.x)
  let pitch = if sinp.abs() >= 1.0 {
    // Use 90 degrees if out of range, with sign of sinp
    if sinp < 0.0 {
      -@math.PI / 2.0
    } else {
      @math.PI / 2.0
    }
  } else {
    @math.asin(sinp)
  }

  // Yaw (z-axis rotation)
  let siny_cosp = 2.0 * (self.w * self.z + self.x * self.y)
  let cosy_cosp = 1.0 - 2.0 * (self.y * self.y + self.z * self.z)
  let yaw = @math.atan2(siny_cosp, cosy_cosp)
  (roll, pitch, yaw)
}

///|
/// Accelerometer reading (m/s²)
pub(all) struct AccelReading {
  data : Vec3
  timestamp : Int64
} derive(Show, Eq)

///|
/// Gyroscope reading (rad/s)
pub(all) struct GyroReading {
  data : Vec3
  timestamp : Int64
} derive(Show, Eq)

///|
/// Magnetometer reading (μT - microtesla)
pub(all) struct MagReading {
  data : Vec3
  timestamp : Int64
} derive(Show, Eq)

///|
/// Motion state combining all sensor data
pub(all) struct MotionState {
  acceleration : Vec3
  angular_velocity : Vec3
  magnetic_field : Vec3
  orientation : Quaternion
  timestamp : Int64
} derive(Show)

///|
/// Create initial motion state
pub fn MotionState::new() -> MotionState {
  {
    acceleration: Vec3::zero(),
    angular_velocity: Vec3::zero(),
    magnetic_field: Vec3::zero(),
    orientation: Quaternion::identity(),
    timestamp: 0L,
  }
}

///|
/// Gesture types for motion detection
pub enum Gesture {
  Stationary
  Shaking
  Tilting(Double, Double) // pitch, roll
  Rotating(Double) // angular velocity magnitude
  FreeFall
  Impact(Double) // impact force
} derive(Show, Eq)

///|
/// Sensor configuration
pub(all) struct SensorConfig {
  accel_range : Double // ±g
  gyro_range : Double // ±rad/s
  mag_range : Double // ±μT
  sample_rate : Int // Hz
} derive(Show, Eq)

///|
/// Default sensor configuration
pub fn SensorConfig::default() -> SensorConfig {
  {
    accel_range: 16.0, // ±16g
    gyro_range: 34.9, // ±2000 deg/s = ±34.9 rad/s
    mag_range: 4800.0, // ±4800μT
    sample_rate: 100,
  } // 100Hz
}