mod basic;

use crate::terms::Term;

pub use self::basic::*;

use super::{Triple, TripleRef};

pub trait TripleFilter: Clone + Send + Sync {
    fn filter(&self, triple: TripleRef) -> bool;
}

impl<F> TripleFilter for F
where
    F: Fn(TripleRef) -> bool + Clone + Send + Sync,
{
    fn filter(&self, triple: TripleRef) -> bool {
        (self)(triple)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TrueFilter;

impl TripleFilter for TrueFilter {
    fn filter(&self, _triple: TripleRef) -> bool {
        true
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FalseFilter;
impl TripleFilter for FalseFilter {
    fn filter(&self, _triple: TripleRef) -> bool {
        false
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AndFilter<F1, F2> {
    pub(crate) f: F1,
    pub(crate) g: F2,
}

impl<F1, F2> AndFilter<F1, F2> {
    pub fn new(f: F1, g: F2) -> Self {
        Self { f, g }
    }
}

impl<F1, F2> TripleFilter for AndFilter<F1, F2>
where
    F1: TripleFilter + Clone + Send + Sync,
    F2: TripleFilter + Clone + Send + Sync,
{
    fn filter(&self, triple: TripleRef) -> bool {
        self.f.filter(triple) && self.g.filter(triple)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OrFilter<F1, F2> {
    pub(crate) f: F1,
    pub(crate) g: F2,
}

impl<F1, F2> OrFilter<F1, F2> {
    pub fn new(f: F1, g: F2) -> Self {
        Self { f, g }
    }
}

impl<F1, F2> TripleFilter for OrFilter<F1, F2>
where
    F1: TripleFilter + Clone + Send + Sync,
    F2: TripleFilter + Clone + Send + Sync,
{
    fn filter(&self, triple: TripleRef) -> bool {
        self.f.filter(triple) || self.g.filter(triple)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SubjectFilter<'a> {
    pub(crate) subject: &'a Term,
}

impl<'a> SubjectFilter<'a> {
    pub fn new(subject: &'a Term) -> Self {
        Self { subject }
    }
}

impl TripleFilter for SubjectFilter<'_> {
    fn filter(&self, triple: TripleRef) -> bool {
        self.subject == triple.subject()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct PredicateFilter<'a> {
    pub(crate) predicate: &'a Term,
}

impl<'a> PredicateFilter<'a> {
    pub fn new(predicate: &'a Term) -> Self {
        Self { predicate }
    }
}

impl TripleFilter for PredicateFilter<'_> {
    fn filter(&self, triple: TripleRef) -> bool {
        self.predicate == triple.predicate()
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ObjectFilter<'a> {
    pub(crate) object: &'a Term,
}

impl<'a> ObjectFilter<'a> {
    pub fn new(object: &'a Term) -> Self {
        Self { object }
    }
}

impl TripleFilter for ObjectFilter<'_> {
    fn filter(&self, triple: TripleRef) -> bool {
        self.object == triple.object()
    }
}

pub struct FilterIter<I, F> {
    iter: I,
    filter: F,
}

impl<I, F, E> Iterator for FilterIter<I, F>
where
    I: Iterator<Item = E>,
    E: AsRef<Triple>,
    F: TripleFilter,
{
    type Item = E;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.find(|e| {
            self.filter.filter(e.as_ref().as_triple_ref())
        })
    }
}

impl<I, F, E> FilterIter<I, F>
where
    I: Iterator<Item = E>,
    E: AsRef<Triple>,
    F: TripleFilter,
{
    pub fn new(iter: I, filter: F) -> Self {
        Self { iter, filter }
    }
}
