use super::{
    super::{attachment::*, error::*, implementation::*, problem::*},
    cause::*,
    ref_iterator::*,
};

//
// CauseRef
//

/// Reference to a [Cause].
pub struct CauseRef<'problem, ErrorT> {
    /// Containing problem.
    pub problem: &'problem Problem,

    /// Depth in causation chain.
    pub depth: usize,

    /// Error.
    ///
    /// This error could be either on the causation chain or nested in
    /// [source](std::error::Error::source).
    pub error: &'problem ErrorT,

    /// Attachments.
    pub attachments: &'problem Vector<CapturedAttachment>,
}

impl<'problem, ErrorT> CauseRef<'problem, ErrorT> {
    /// Constructor.
    pub fn new(
        problem: &'problem Problem,
        depth: usize,
        error: &'problem ErrorT,
        attachments: &'problem Vector<CapturedAttachment>,
    ) -> Self {
        CauseRef {
            problem,
            depth,
            error,
            attachments,
        }
    }

    /// The cause.
    pub fn cause(&self) -> &'problem Cause {
        self.problem
            .get(self.depth)
            .expect("depth < causation chain length")
    }

    /// Iterate the causation chain starting from *under* this cause ending at the root.
    ///
    /// Note that this will skip over [source](std::error::Error::source).
    pub fn iter_under(&self) -> CauseRefIterator<'problem> {
        CauseRefIterator::new(self.problem, self.depth + 1)
    }

    /// The cause under this one in the causation chain.
    ///
    /// It will be [None] if we are the root cause.
    ///
    /// Note that this will skip over [source](std::error::Error::source).
    pub fn under(&self) -> Option<CauseRef<'problem, CapturedError>> {
        self.iter_under().next()
    }

    /// Whether we are the top cause of the chain.
    pub fn is_top(&self) -> bool {
        self.depth == 0
    }

    /// Whether we are the root cause of the chain.
    pub fn is_root(&self) -> bool {
        let length = self.problem.depth();
        assert!(self.depth < length, "depth < causation chain length");
        self.depth == length - 1
    }
}

impl<'problem, ErrorT> Attachments for CauseRef<'problem, ErrorT> {
    fn attachments(&self) -> impl Iterator<Item = &CapturedAttachment> {
        self.attachments.iter()
    }
}
