//! The memory module contains structures for storing information about the execution
//! of the rules. We have Memory structures that represent the information about a field,
//! and Work Memory Entities that store information about a matching fact.

use std::cmp::Ordering;

use crate::condition::{ConditionField, ConditionBody};

/// A memory stores the fact that we have matched a field, or the field didn't need matching.
///
/// Exact matching in alphamemories using hash functions is done against all permutations of
/// either the bound or unbound value. In case it is unbound, it can by Any value. For bound
/// values, we require a fixed Value.
#[derive(Hash, Eq, PartialEq, Debug, Clone)]
pub enum Memory {
    /// A constant value, must match exactly
    Value(String),
    /// Anything will match
    Any,
}

impl Memory {
    pub fn new(c: &ConditionField) -> Memory {
        match c {
            ConditionField::Const(s) => Memory::Value(s.clone()),
            _ => Memory::Any,
        }
    }
}

impl From<&ConditionField> for Memory {
    fn from(field: &ConditionField) -> Self {
        match field {
            ConditionField::Const(s) => Memory::Value(s.clone()),
            _ => Memory::Any,
        }
    }
}

/// Since we use Memory for hashing, we need partial ordering.
impl PartialOrd for Memory {
    /// Compare Value against Value, ignoring Any types
    ///
    fn partial_cmp(&self, other: &Memory) -> Option<Ordering> {
        match self {
            Memory::Value(value) =>
                if let Memory::Value(other_value) = &other {
                    value.partial_cmp(other_value)
                } else {
                    None
                },
            _ => None
        }
    }
}

/// A WmeMatch represents a combination of bound and unbound fields that matches a Wme for a rule.
///
/// We use WmeMatches as hash-keys to find the AlphaMemories for which the constant-conditionss
/// match the WME we add to the network.
#[derive(Hash, Eq, PartialEq, Debug, Clone, PartialOrd)]
pub(crate) struct WmeMatch {
    pub(crate) f1: Memory,
    pub(crate) f2: Memory,
    pub(crate) f3: Memory,
}

impl WmeMatch {
    pub(crate) fn new(f1: Memory, f2: Memory, f3: Memory) -> WmeMatch {
        WmeMatch { f1, f2, f3 }
    }
}

impl From<&ConditionBody> for WmeMatch {
    fn from(body: &ConditionBody) -> Self {
        WmeMatch{
            f1: Memory::from(&body.f1 ),
            f2: Memory::from(&body.f2 ),
            f3: Memory::from(&body.f3 ),
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_memory() {
        let v1 = Memory::Value("1".to_string());
        let v2 = Memory::Value("2".to_string());
        let a = Memory::Any;

        assert!(v1 < v2);
        assert!(!(v2 < v1));
        assert_eq!(v1, v1);
        assert_ne!(v1, v2);

        assert!(!(v1 < a));
        assert!(!(a < v1));

        assert_ne!(v1, a);
        assert_ne!(v2, a);
    }
}
