//! Internal / private prelude

#![allow(unused_imports)]

#[rustfmt::skip]
pub(crate) use {
    ::ffi_sdk::{
        ffi_utils::{c_slice, char_p, repr_c, Blocking, NonOpaque},
    },
    ::safer_ffi::{
        prelude::Out,
    },
    ::serde::{
        de::DeserializeOwned, Deserialize, Serialize,
    },
    ::std::{
        any::Any,
        borrow::Borrow,
        collections::HashMap,
        ops::Not,
        os::raw::c_void,
        path::{Path, PathBuf},
        pin::Pin,
        sync::Arc,
    },
    ::to_method::{
        To,
    },
    crate::{
        ditto::Ditto,
        error::{DittoError, ErrorKind, Result},
        prelude::*,
        store::Store,
        utils::{extension_traits::*, Str},
    },
};

#[rustfmt::skip]
#[allow(deprecated)]
pub(crate) use {
    crate::store::collection::type_traits::MutableValue,
};

pub(crate) mod arc {
    pub use std::sync::Weak;
}

pub(crate) mod marker {
    pub(crate) use crate::utils::InvariantLifetimeMarker as InvariantLifetime;
}

/// Since this is a convenience extension trait, we don't really care about coherence issues.
/// We *know* we won't have overlaps.
///
/// Alas, the blanket impls below will overlap.
/// We work around this limitation by adding a phony generic parameter so as to technically
/// be dealing with impls of distinct traits which therefore do not overlap.
///
/// Should an overlap ever happen, we'd pay the price with type inference errors (we'd be expected
/// to disambiguate the generic parameter).
pub(crate) trait IntoRustResult<CoherenceDisambiguator = ()>: Sized {
    type Ok;
    fn into_rust_result(self) -> Result<Self::Ok, ::core::num::NonZeroI32>;
    fn ok_or(self, error_kind: ErrorKind) -> Result<Self::Ok, DittoError> {
        self.into_rust_result()
            .map_err(|_| DittoError::from_ffi(error_kind))
    }
    fn ok_or_else<F>(self, err: F) -> Result<Self::Ok, DittoError>
    where
        F: FnOnce() -> ErrorKind,
    {
        self.into_rust_result()
            .map_err(|_| DittoError::from_ffi(err()))
    }
    fn ok(self) -> Result<Self::Ok, DittoError> {
        self.ok_or(ErrorKind::Internal)
    }
}

impl IntoRustResult for i32 {
    type Ok = ();

    fn into_rust_result(self: i32) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        if let Some(err) = ::core::num::NonZeroI32::new(self) {
            Err(err)
        } else {
            Ok(())
        }
    }
}

impl<T> IntoRustResult for repr_c::Result_Box<T>
where
    T: ::ffi_sdk::ffi_utils::FfiDropBox,
{
    type Ok = repr_c::Box<T>;
    fn into_rust_result(self) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        if let Some(err_code) = ::core::num::NonZeroI32::new(self.status_code) {
            Err(err_code)
        } else {
            Ok(self.ok_value.unwrap())
        }
    }
}

impl<T: NonOpaque> IntoRustResult for repr_c::Result_Vec<T> {
    type Ok = repr_c::Vec<T>;
    fn into_rust_result(self) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        if let Some(err_code) = ::core::num::NonZeroI32::new(self.status_code) {
            Err(err_code)
        } else {
            Ok(self.c_vec.unwrap())
        }
    }
}

impl<T: NonOpaque> IntoRustResult for repr_c::Result_BoxedSlice<T> {
    type Ok = c_slice::Box<T>;
    fn into_rust_result(self) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        if let Some(err_code) = ::core::num::NonZeroI32::new(self.status_code) {
            Err(err_code)
        } else {
            match self.c_slice {
                Some(slice) => Ok(slice),
                None => {
                    // FIXME : status code 0 without slice
                    Err(::core::num::NonZeroI32::new(-1).unwrap())
                }
            }
        }
    }
}

impl<T: NonOpaque> IntoRustResult for repr_c::Result_OptionBoxedSlice<T> {
    type Ok = Option<c_slice::Box<T>>;
    fn into_rust_result(self) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        if let Some(err_code) = ::core::num::NonZeroI32::new(self.status_code) {
            Err(err_code)
        } else {
            Ok(self.c_slice)
        }
    }
}

impl<T: NonOpaque> IntoRustResult for repr_c::Result<T> {
    type Ok = T;
    fn into_rust_result(self) -> Result<T, ::core::num::NonZeroI32> {
        if let Some(err_code) = ::core::num::NonZeroI32::new(self.status_code) {
            Err(err_code)
        } else {
            Ok(self.ok_value)
        }
    }
}

impl<T> IntoRustResult<((),)> for T
where
    Self: ::ffi_sdk::IntoRustResult,
{
    type Ok = <T as ::ffi_sdk::IntoRustResult>::Ok;

    fn into_rust_result(self: T) -> Result<Self::Ok, ::core::num::NonZeroI32> {
        ::ffi_sdk::IntoRustResult::into_rust_result(self)
    }
}
