use super::{super::cause::*, problem::*};

use std::cmp::*;

//
// WeaklyComparableProblem
//

/// Allows for weak problem comparison.
///
/// Supports [PartialEq], [Eq], and [PartialOrd].
///
/// Comparison is done using the first [CauseEquality] or [CauseComparison] found in each problem.
/// This is in contrast to the "strong" default comparison, which considers all causes.
pub struct WeaklyComparableProblem(pub Problem);

impl PartialEq for WeaklyComparableProblem {
    fn eq(&self, other: &Self) -> bool {
        WeaklyComparableProblemRef(&self.0).eq(&WeaklyComparableProblemRef(&other.0))
    }
}

impl Eq for WeaklyComparableProblem {}

impl PartialOrd for WeaklyComparableProblem {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        WeaklyComparableProblemRef(&self.0).partial_cmp(&WeaklyComparableProblemRef(&other.0))
    }
}

//
// WeaklyComparableProblemRef
//

/// Allows for weak problem reference comparison.
///
/// Supports [PartialEq], [Eq], and [PartialOrd].
///
/// Comparison is done using the first [CauseEquality] or [CauseComparison] found in each problem.
/// This is in contrast to the "strong" default comparison, which considers all causes.
pub struct WeaklyComparableProblemRef<'problem>(pub &'problem Problem);

impl<'problem> PartialEq for WeaklyComparableProblemRef<'problem> {
    fn eq(&self, other: &Self) -> bool {
        if let Some((cause, equality)) = self.0.cause_with_attachment_type::<CauseEquality>()
            && let Some((other_cause, _other_equality)) =
                other.0.cause_with_attachment_type::<CauseEquality>()
        {
            equality.eq(cause, other_cause)
        } else {
            self.partial_cmp(other) == Some(Ordering::Equal)
        }
    }
}

impl<'problem> Eq for WeaklyComparableProblemRef<'problem> {}

impl<'problem> PartialOrd for WeaklyComparableProblemRef<'problem> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if let Some((cause, comparison)) = self.0.cause_with_attachment_type::<CauseComparison>()
            && let Some((other_cause, _other_comparison)) =
                other.0.cause_with_attachment_type::<CauseComparison>()
        {
            comparison.compare(cause, other_cause)
        } else {
            None
        }
    }
}
