// Take a look at the license at the top of the repository in the LICENSE file.

// rustdoc-stripper-ignore-next
//! Traits intended for implementing the [`Location`](crate::Location) interface.

use crate::{prelude::*, Location};
use glib::subclass::prelude::*;
use glib::translate::*;

pub trait LocationImpl: ObjectImpl {
    fn latitude(&self) -> f64 {
        self.parent_latitude()
    }

    fn longitude(&self) -> f64 {
        self.parent_longitude()
    }

    fn set_location(&self, latitude: f64, longitude: f64) {
        self.parent_set_location(latitude, longitude)
    }
}

mod sealed {
    pub trait Sealed {}
    impl<T: super::LocationImplExt> Sealed for T {}
}

pub trait LocationImplExt: sealed::Sealed + ObjectSubclass {
    fn parent_latitude(&self) -> f64 {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<Location>()
                as *const ffi::ShumateLocationInterface;

            let func = (*parent_iface)
                .get_latitude
                .expect("no parent \"get_latitude\" implementation");

            func(self.obj().unsafe_cast_ref::<Location>().to_glib_none().0)
        }
    }

    fn parent_longitude(&self) -> f64 {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<Location>()
                as *const ffi::ShumateLocationInterface;

            let func = (*parent_iface)
                .get_longitude
                .expect("no parent \"get_longitude\" implementation");

            func(self.obj().unsafe_cast_ref::<Location>().to_glib_none().0)
        }
    }

    fn parent_set_location(&self, latitude: f64, longitude: f64) {
        unsafe {
            let type_data = Self::type_data();
            let parent_iface = type_data.as_ref().parent_interface::<Location>()
                as *const ffi::ShumateLocationInterface;

            let func = (*parent_iface)
                .set_location
                .expect("no parent \"set_location\" implementation");

            func(
                self.obj().unsafe_cast_ref::<Location>().to_glib_none().0,
                latitude,
                longitude,
            )
        }
    }
}

impl<T: LocationImpl> LocationImplExt for T {}

unsafe impl<T: LocationImpl> IsImplementable<T> for Location {
    fn interface_init(iface: &mut glib::Interface<Self>) {
        let iface = iface.as_mut();

        iface.get_latitude = Some(location_get_latitude::<T>);
        iface.get_longitude = Some(location_get_longitude::<T>);
        iface.set_location = Some(location_set_location::<T>);
    }
}

unsafe extern "C" fn location_get_latitude<T: LocationImpl>(
    location: *mut ffi::ShumateLocation,
) -> f64 {
    let instance = &*(location as *mut T::Instance);
    let imp = instance.imp();

    imp.latitude()
}

unsafe extern "C" fn location_get_longitude<T: LocationImpl>(
    location: *mut ffi::ShumateLocation,
) -> f64 {
    let instance = &*(location as *mut T::Instance);
    let imp = instance.imp();

    imp.longitude()
}

unsafe extern "C" fn location_set_location<T: LocationImpl>(
    location: *mut ffi::ShumateLocation,
    latitude: f64,
    longitude: f64,
) {
    let instance = &*(location as *mut T::Instance);
    let imp = instance.imp();

    imp.set_location(latitude, longitude)
}
