/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::interfaces::{nsIInterfaceRequestor, nsISupports};
use crate::{GetterAddrefs, RefCounted, RefPtr};

#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq)]
/// A "unique identifier". This is modeled after OSF DCE UUIDs.
pub struct nsID(pub u32, pub u16, pub u16, pub [u8; 8]);

/// Interface IDs
pub type nsIID = nsID;
/// Class IDs
pub type nsCID = nsID;

/// A type which implements XpCom must follow the following rules:
///
/// * It must be a legal XPCOM interface.
/// * The result of a QueryInterface or similar call, passing IID, must return a
///   valid reference to an object of the given type.
/// * It must be valid to cast a &self reference to a &nsISupports reference.
pub unsafe trait XpCom: RefCounted {
    const IID: nsIID;

    /// Perform a QueryInterface call on this object, attempting to dynamically
    /// cast it to the requested interface type. Returns Some(RefPtr<T>) if the
    /// cast succeeded, and None otherwise.
    fn query_interface<T: XpCom>(&self) -> Option<RefPtr<T>> {
        let mut ga = GetterAddrefs::<T>::new();
        unsafe {
            if (*(self as *const Self as *const nsISupports))
                .QueryInterface(&T::IID, ga.void_ptr())
                .succeeded()
            {
                ga.refptr()
            } else {
                None
            }
        }
    }

    /// Perform a `GetInterface` call on this object, returning `None` if the
    /// object doesn't implement `nsIInterfaceRequestor`, or can't access the
    /// interface `T`.
    fn get_interface<T: XpCom>(&self) -> Option<RefPtr<T>> {
        let ireq = self.query_interface::<nsIInterfaceRequestor>()?;

        let mut ga = GetterAddrefs::<T>::new();
        unsafe {
            if ireq.GetInterface(&T::IID, ga.void_ptr()).succeeded() {
                ga.refptr()
            } else {
                None
            }
        }
    }
}
