use crate::{
    graphs::{
        AndFilter, ObjectFilter, OrFilter, PredicateFilter, SubjectFilter, TripleFilter, TrueFilter,
    },
    terms::Term,
};

use super::QuadRef;

pub trait QuadFilter: Clone + Send + Sync {
    fn filter(&self, quad: QuadRef<'_>) -> bool;
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TripleToQuadFiter<F>(pub F);

impl<F> QuadFilter for TripleToQuadFiter<F>
where
    F: TripleFilter,
{
    fn filter(&self, quad: QuadRef) -> bool {
        self.0.filter(quad.triple())
    }
}

impl<F> QuadFilter for F
where
    F: for<'a> Fn(QuadRef<'a>) -> bool + Clone + Send + Sync,
{
    fn filter(&self, quad: QuadRef) -> bool {
        (self)(quad)
    }
}

impl QuadFilter for TrueFilter {
    fn filter(&self, _quad: QuadRef) -> bool {
        true
    }
}

impl<F1, F2> QuadFilter for AndFilter<F1, F2>
where
    F1: QuadFilter,
    F2: QuadFilter,
{
    fn filter(&self, quad: QuadRef) -> bool {
        self.f.filter(quad) && self.g.filter(quad)
    }
}

impl<F1, F2> QuadFilter for OrFilter<F1, F2>
where
    F1: QuadFilter,
    F2: QuadFilter,
{
    fn filter(&self, quad: QuadRef) -> bool {
        self.f.filter(quad) || self.g.filter(quad)
    }
}

impl QuadFilter for SubjectFilter<'_> {
    fn filter(&self, quad: QuadRef) -> bool {
        self.subject == quad.subject()
    }
}

impl QuadFilter for PredicateFilter<'_> {
    fn filter(&self, quad: QuadRef) -> bool {
        self.predicate == quad.predicate()
    }
}

impl QuadFilter for ObjectFilter<'_> {
    fn filter(&self, quad: QuadRef) -> bool {
        self.object == quad.object()
    }
}

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

impl<'a> GraphNameFilter<'a> {
    pub fn new(graph_name: Option<&'a Term>) -> Self {
        Self { graph_name }
    }
}

impl QuadFilter for GraphNameFilter<'_> {
    fn filter(&self, quad: QuadRef<'_>) -> bool {
        self.graph_name == quad.graph_name()
    }
}
