use crate::{
  base_objects::{marker, player},
  helpers,
  rgba::Rgba,
  sdk,
  vector::Vector3,
  SomeResult, VoidResult,
};

use autocxx::prelude::*;

/// # **`Marker implementation`**
impl marker::Marker {
  pub fn new(
    marker_type: altv_sdk::MarkerType,
    pos: impl Into<Vector3>,
    color: impl Into<Rgba>,
  ) -> marker::MarkerContainer {
    let pos = pos.into();
    let color = color.into();

    helpers::create_base_object!(
      marker,
      sdk::ICore::CreateMarker(
        std::ptr::null_mut(),
        marker_type as u32,
        pos.x(),
        pos.y(),
        pos.z(),
        color.r(),
        color.g(),
        color.b(),
        color.a(),
        std::ptr::null_mut(),
      ),
      panic!("Failed to create Marker")
    )
  }

  pub fn destroy(&self) -> VoidResult {
    marker::remove_from_pool!(self)?;
    self.internal_destroy()
  }

  pub fn global(&self) -> SomeResult<bool> {
    Ok(unsafe { sdk::IMarker::IsGlobal(self.raw_ptr()?) })
  }

  pub fn target(&self) -> SomeResult<Option<player::PlayerContainer>> {
    helpers::get_any_option_base_object!(sdk::IMarker::GetTarget(self.raw_ptr()?), player)
  }

  pub fn color(&self) -> SomeResult<Rgba> {
    Ok(helpers::read_cpp_rgba(
      unsafe { sdk::IMarker::GetColor(self.raw_ptr()?) }.within_unique_ptr(),
    ))
  }

  pub fn set_color(&self, color: impl Into<Rgba>) -> VoidResult {
    let color = color.into();

    unsafe { sdk::IMarker::SetColor(self.raw_ptr()?, color.r(), color.g(), color.b(), color.a()) }
    Ok(())
  }

  pub fn visible(&self) -> SomeResult<bool> {
    Ok(unsafe { sdk::IMarker::IsVisible(self.raw_ptr()?) })
  }

  pub fn set_visible(&self, visible: bool) -> VoidResult {
    unsafe { sdk::IMarker::SetVisible(self.raw_ptr()?, visible) }
    Ok(())
  }

  pub fn marker_type(&self) -> SomeResult<altv_sdk::MarkerType> {
    let raw = unsafe { sdk::IMarker::GetMarkerType(self.raw_ptr()?) };
    Ok(altv_sdk::MarkerType::try_from(raw).unwrap())
  }

  pub fn set_marker_type(&self, marker_type: altv_sdk::MarkerType) -> VoidResult {
    unsafe { sdk::IMarker::SetMarkerType(self.raw_ptr()?, marker_type as u32) };
    Ok(())
  }

  pub fn scale(&self) -> SomeResult<Vector3> {
    Ok(helpers::read_cpp_vector3(unsafe {
      sdk::IMarker::GetScale(self.raw_ptr()?).within_unique_ptr()
    }))
  }

  pub fn set_scale(&self, scale: impl Into<Vector3>) -> VoidResult {
    let scale = scale.into();

    unsafe { sdk::IMarker::SetScale(self.raw_ptr()?, scale.x(), scale.y(), scale.z()) };
    Ok(())
  }

  pub fn rot(&self) -> SomeResult<Vector3> {
    Ok(helpers::read_cpp_vector3(unsafe {
      sdk::IMarker::GetRotation(self.raw_ptr()?).within_unique_ptr()
    }))
  }

  pub fn set_rot(&self, rot: impl Into<Vector3>) -> VoidResult {
    let rot = rot.into();
    unsafe { sdk::IMarker::SetRotation(self.raw_ptr()?, rot.x(), rot.y(), rot.z()) };

    Ok(())
  }

  pub fn dir(&self) -> SomeResult<Vector3> {
    Ok(helpers::read_cpp_vector3(unsafe {
      sdk::IMarker::GetDirection(self.raw_ptr()?).within_unique_ptr()
    }))
  }

  pub fn set_dir(&self, dir: impl Into<Vector3>) -> VoidResult {
    let dir = dir.into();
    unsafe { sdk::IMarker::SetDirection(self.raw_ptr()?, dir.x(), dir.y(), dir.z()) };

    Ok(())
  }

  pub fn face_camera(&self) -> SomeResult<bool> {
    Ok(unsafe { sdk::IMarker::IsFaceCamera(self.raw_ptr()?) })
  }

  pub fn set_face_camera(&self, face_camera: bool) -> VoidResult {
    unsafe { sdk::IMarker::SetFaceCamera(self.raw_ptr()?, face_camera) }
    Ok(())
  }

  pub fn streaming_distance(&self) -> SomeResult<u32> {
    Ok(unsafe { sdk::IMarker::GetStreamingDistance(self.raw_ptr()?) })
  }

  pub fn rotating(&self) -> SomeResult<bool> {
    Ok(unsafe { sdk::IMarker::IsRotating(self.raw_ptr()?) })
  }

  pub fn set_rotating(&self, toggle: bool) -> VoidResult {
    unsafe { sdk::IMarker::SetRotating(self.raw_ptr()?, toggle) }
    Ok(())
  }

  pub fn bobs_up_down(&self) -> SomeResult<bool> {
    Ok(unsafe { sdk::IMarker::IsBobUpDown(self.raw_ptr()?) })
  }

  pub fn set_bob_up_down(&self, toggle: bool) -> VoidResult {
    unsafe { sdk::IMarker::SetBobUpDown(self.raw_ptr()?, toggle) }
    Ok(())
  }
}
