use super::{
    super::{problem::*, result::*},
    receiver::*,
};

//
// ProblemReceiverResult
//

/// Problem receiver result.
pub trait ProblemReceiverResult<OkT, ProblemReceiverT>
where
    ProblemReceiverT: ProblemReceiver,
{
    /// Like [Result::ok] but gives [Err] to a [ProblemReceiver].
    fn give_ok(self, receiver: &mut ProblemReceiverT) -> Result<Option<OkT>, Problem>;

    /// Like [Result::unwrap_or] but gives [Err] to a [ProblemReceiver].
    fn give_unwrap_or(self, receiver: &mut ProblemReceiverT, default: OkT) -> Result<OkT, Problem>;

    /// Like [Result::unwrap_or_else] but gives [Err] to a [ProblemReceiver].
    fn give_unwrap_or_else<DefaultT>(
        self,
        receiver: &mut ProblemReceiverT,
        default: DefaultT,
    ) -> Result<OkT, Problem>
    where
        DefaultT: FnOnce() -> OkT;

    /// Like [Result::unwrap_or_default] but gives [Err] to a [ProblemReceiver].
    fn give_unwrap_or_default(self, receiver: &mut ProblemReceiverT) -> Result<OkT, Problem>
    where
        OkT: Default;
}

impl<ResultT, OkT, ProblemReceiverT> ProblemReceiverResult<OkT, ProblemReceiverT> for ResultT
where
    ProblemReceiverT: ProblemReceiver,
    ResultT: IntoProblemResult<OkT>,
{
    #[track_caller]
    fn give_ok(self, receiver: &mut ProblemReceiverT) -> Result<Option<OkT>, Problem> {
        match self.into_problem() {
            Ok(ok) => Ok(Some(ok)),
            Err(problem) => {
                receiver.give(problem)?;
                Ok(None)
            }
        }
    }

    #[track_caller]
    fn give_unwrap_or(self, receiver: &mut ProblemReceiverT, default: OkT) -> Result<OkT, Problem> {
        match self.into_problem() {
            Ok(ok) => Ok(ok),
            Err(problem) => {
                receiver.give(problem)?;
                Ok(default)
            }
        }
    }

    #[track_caller]
    fn give_unwrap_or_else<DefaultT>(
        self,
        receiver: &mut ProblemReceiverT,
        default: DefaultT,
    ) -> Result<OkT, Problem>
    where
        DefaultT: FnOnce() -> OkT,
    {
        match self.into_problem() {
            Ok(ok) => Ok(ok),
            Err(problem) => {
                receiver.give(problem)?;
                Ok(default())
            }
        }
    }

    #[track_caller]
    fn give_unwrap_or_default(self, receiver: &mut ProblemReceiverT) -> Result<OkT, Problem>
    where
        OkT: Default,
    {
        match self.into_problem() {
            Ok(ok) => Ok(ok),
            Err(problem) => {
                receiver.give(problem)?;
                Ok(OkT::default())
            }
        }
    }
}
