use tracing::debug;

use crate::{
    systemd::{
        data::UnitInfo,
        enums::{NumMatchType, StrMatchType},
    },
    widget::unit_list::{UnitListPanel, filter::BoolFilter},
};
use std::{
    any::Any,
    borrow::Cow,
    collections::HashSet,
    fmt::{self, Debug},
    hash::Hash,
};
#[derive(Debug, Copy, Clone)]
pub enum UnitPropertyFilterType {
    Text,
    Element,
    NumU64,
    NumI32,
    NumU16,
    NumU32,
    NumI64,
    Bool,
}
pub trait UnitPropertyFilter: Debug {
    fn set_on_filter_apply_ui_func(&mut self, on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>);
    fn unset_on_filter_apply_ui_func(&mut self) {
        self.set_on_filter_apply_ui_func(None);
    }

    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
    fn ftype(&self) -> UnitPropertyFilterType;

    fn text(&self) -> Cow<'_, str> {
        Cow::from("")
    }

    fn match_type(&self) -> StrMatchType {
        StrMatchType::default()
    }

    fn num_match_type(&self) -> NumMatchType {
        NumMatchType::default()
    }

    fn unit_list_panel(&self) -> &UnitListPanel;

    fn id(&self) -> &String;

    fn clear_n_apply_filter(&mut self) {
        self.clear_filter();
        self.unit_list_panel().filter_assessor_change(
            self.id().as_str(),
            None,
            Some(gtk::FilterChange::LessStrict),
            true,
        );
    }

    fn clear_filter(&mut self);
    /*     fn clear_widget_dependancy(&mut self) {
        let lambda = |_: bool| {};
        self.set_on_change(Box::new(lambda));
    } */

    fn is_filter_applies(&self) -> bool;
}

pub fn get_filter_element<T>(prop_filter: &dyn UnitPropertyFilter) -> &FilterElement<T>
where
    T: Eq + Hash + Debug + 'static,
{
    match prop_filter.as_any().downcast_ref::<FilterElement<T>>() {
        Some(a) => a,
        None => {
            panic!("Type of prop_filter, Expect: FilterElement",);
        }
    }
    //.expect("downcast_mut to FilterElement")
}

pub fn get_filter_element_mut<T>(prop_filter: &mut dyn UnitPropertyFilter) -> &mut FilterElement<T>
where
    T: Eq + Hash + Debug + 'static,
{
    prop_filter
        .as_any_mut()
        .downcast_mut::<FilterElement<T>>()
        .expect("downcast_mut to FilterElement")
}

pub struct FilterElement<T>
where
    T: Eq + Hash + Debug,
{
    filter_elements: HashSet<T>,
    on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>,
    unit_property_getter_func: fn(&FilterElementAssessor<T>, &UnitInfo) -> bool,
    id: String,
    unit_list_panel: UnitListPanel,
}

impl<T> fmt::Debug for FilterElement<T>
where
    T: Eq + Hash + Debug + Clone + 'static,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let t = std::any::TypeId::of::<T>();

        f.debug_struct("FilterElement")
            .field("SUB_TYPE", &t)
            .field("filter_elements", &self.filter_elements)
            .field("id", &self.id)
            .finish()
    }
}

impl<T> FilterElement<T>
where
    T: Eq + Hash + Debug + Clone + 'static,
{
    pub fn new(
        id: &str,
        unit_property_getter_func: fn(&FilterElementAssessor<T>, &UnitInfo) -> bool,
        unit_list_panel: &UnitListPanel,
    ) -> Self {
        Self {
            filter_elements: Default::default(),
            on_filter_apply_ui_func: Default::default(),
            unit_property_getter_func,
            id: id.to_owned(),
            unit_list_panel: unit_list_panel.clone(),
        }
    }

    pub(crate) fn elements(&self) -> &HashSet<T> {
        &self.filter_elements
    }

    pub(crate) fn contains(&self, value: &T) -> bool {
        self.filter_elements.contains(value)
    }

    pub(crate) fn set_filter_elem(&mut self, f_element: T, add_or_remove: bool) {
        let old_is_empty = self.filter_elements.is_empty();

        let has_changed = if add_or_remove {
            self.filter_elements.insert(f_element)
        } else {
            self.filter_elements.remove(&f_element)
        };

        if !has_changed {
            return;
        }

        let new_is_empty = self.filter_elements.is_empty();

        let change_type = match (add_or_remove, old_is_empty, new_is_empty) {
            (true, true, _) => Some(gtk::FilterChange::MoreStrict),
            (true, false, _) => Some(gtk::FilterChange::LessStrict),
            (false, _, false) => Some(gtk::FilterChange::MoreStrict),
            (false, _, true) => Some(gtk::FilterChange::LessStrict),
        };

        if old_is_empty != new_is_empty {
            self.on_filter_apply_ui(!new_is_empty);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if new_is_empty {
            None
        } else {
            Some(Box::new(FilterElementAssessor {
                filter_elements: self.filter_elements.clone(),
                filter_unit_func: self.unit_property_getter_func,
                id: self.id.clone(),
            }))
        };

        self.unit_list_panel
            .filter_assessor_change(&self.id, assessor, change_type, false);
    }

    fn on_filter_apply_ui(&self, filter_applies: bool) {
        if let Some(ref lambda) = self.on_filter_apply_ui_func {
            lambda(filter_applies)
        }
    }
}

impl<T> UnitPropertyFilter for FilterElement<T>
where
    T: Eq + Hash + Debug + Clone + 'static,
{
    fn set_on_filter_apply_ui_func(&mut self, lambda: Option<Box<dyn Fn(bool)>>) {
        self.on_filter_apply_ui_func = lambda
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn clear_n_apply_filter(&mut self) {
        self.filter_elements.clear();

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            None,
            Some(gtk::FilterChange::LessStrict),
            true,
        );
    }

    fn clear_filter(&mut self) {
        self.filter_elements.clear();
        self.on_filter_apply_ui(false);
    }

    fn is_filter_applies(&self) -> bool {
        !self.filter_elements.is_empty()
    }

    fn ftype(&self) -> UnitPropertyFilterType {
        UnitPropertyFilterType::Element
    }

    fn unit_list_panel(&self) -> &UnitListPanel {
        &self.unit_list_panel
    }

    fn id(&self) -> &String {
        &self.id
    }
}

pub struct FilterText {
    filter_text: String,
    match_type: StrMatchType,
    case_insensitive: bool,
    filter_unset: bool,
    filter_invert: bool,
    on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>,
    filter_unit_func:
        fn(property_assessor: &FilterTextAssessor, unit: &UnitInfo, key: glib::Quark) -> bool,
    id: String,
    unit_list_panel: UnitListPanel,
    key: glib::Quark,
}

impl fmt::Debug for FilterText {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FilterText")
            .field("filter_text", &self.filter_text)
            .field("id", &self.id)
            .finish()
    }
}

impl FilterText {
    pub fn new(
        id: &str,
        filter_unit_func: fn(
            property_assessor: &FilterTextAssessor,
            unit: &UnitInfo,
            key: glib::Quark,
        ) -> bool,
        unit_list_panel: &UnitListPanel,
        case_incensitive_default: bool,
    ) -> Self {
        Self::newq(
            id,
            filter_unit_func,
            unit_list_panel,
            case_incensitive_default,
            glib::Quark::from_str("default"),
        )
        // Self {
        //     filter_text: Default::default(),
        //     match_type: StrMatchType::default(),
        //     on_filter_apply_ui_func: Default::default(),
        //     filter_unit_func,
        //     id: id.to_owned(),
        //     unit_list_panel: unit_list_panel.clone(),
        //     key: glib::Quark::from_str("default"),
        //     filter_unset: false,
        //     filter_invert: false,
        //     case_insensitive: case_incensitive_default,
        // }
    }

    pub fn newq(
        id: &str,
        filter_unit_func: fn(
            property_assessor: &FilterTextAssessor,
            unit: &UnitInfo,
            key: glib::Quark,
        ) -> bool,
        unit_list_panel: &UnitListPanel,
        case_incensitive_default: bool,
        key: glib::Quark,
    ) -> Self {
        Self {
            filter_text: Default::default(),
            match_type: StrMatchType::default(),
            on_filter_apply_ui_func: Default::default(),
            filter_unit_func,
            id: id.to_owned(),
            unit_list_panel: unit_list_panel.clone(),
            key,
            filter_unset: false,
            filter_invert: false,
            case_insensitive: case_incensitive_default,
        }
    }

    pub fn set_filter_elem(&mut self, f_element: &str, update_widget: bool) {
        if f_element == self.filter_text {
            return;
        }

        let old_is_empty = self.filter_text.is_empty();
        let new_is_empty = f_element.is_empty();

        let change_type = if new_is_empty {
            gtk::FilterChange::LessStrict
        } else if f_element.len() > self.filter_text.len() && f_element.contains(&self.filter_text)
        {
            gtk::FilterChange::MoreStrict
        } else if f_element.len() < self.filter_text.len() && self.filter_text.contains(f_element) {
            gtk::FilterChange::LessStrict
        } else {
            gtk::FilterChange::Different
        };

        self.set_filter_text(f_element);

        let filter_applies = self.is_filter_applies();

        if old_is_empty != new_is_empty {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterTextAssessor::new(self)))
        } else {
            None
        };

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    fn set_filter_text(&mut self, f_element: &str) {
        self.filter_text.replace_range(.., f_element);
    }

    pub fn set_filter_match_type(&mut self, match_type: StrMatchType, update_widget: bool) {
        debug!("Match type new {match_type:?} old {:?}", self.match_type);
        if match_type == self.match_type {
            debug!("exit same");
            return;
        }

        let change_type = match (match_type, self.match_type) {
            (StrMatchType::Contains, StrMatchType::StartWith) => gtk::FilterChange::LessStrict,
            (StrMatchType::Contains, StrMatchType::EndWith) => gtk::FilterChange::LessStrict,
            (StrMatchType::StartWith, StrMatchType::Contains) => gtk::FilterChange::MoreStrict,
            (StrMatchType::EndWith, StrMatchType::Contains) => gtk::FilterChange::MoreStrict,

            (_, _) => gtk::FilterChange::Different,
        };

        debug!("change_type {change_type:?}");

        self.match_type = match_type;

        if !self.is_filter_applies() {
            debug!("exit filter_text doesn't apply");
            return;
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> =
            Some(Box::new(FilterTextAssessor::new(self)));

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    pub fn set_filter_match_case_insensitive(
        &mut self,
        case_insensitive: bool,
        update_widget: bool,
    ) {
        let different = self.case_insensitive != case_insensitive;
        debug!(
            "set case insensitive old: {} new: {}",
            self.case_insensitive, case_insensitive
        );

        self.set_case_insensitive(case_insensitive);

        let filter_applies = self.is_filter_applies();

        if different {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterTextAssessor::new(self)))
        } else {
            None
        };

        let change_type = if case_insensitive {
            gtk::FilterChange::LessStrict
        } else {
            gtk::FilterChange::MoreStrict
        };

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    fn set_case_insensitive(&mut self, case_insensitive: bool) {
        self.case_insensitive = case_insensitive;
    }

    pub(crate) fn set_filter_unset(&mut self, filter_unset: bool) {
        let different = self.filter_unset != filter_unset;

        self.filter_unset = filter_unset;

        let filter_applies = self.is_filter_applies();

        if different {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterTextAssessor::new(self)))
        } else {
            None
        };

        let change_type = gtk::FilterChange::Different;
        let update_widget = true;

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    pub(crate) fn set_filter_invert(&mut self, filter_invert: bool) {
        let different = self.filter_invert != filter_invert;

        self.filter_invert = filter_invert;

        let filter_applies = self.is_filter_applies();

        if different {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterTextAssessor::new(self)))
        } else {
            None
        };

        let change_type = gtk::FilterChange::Different;

        let update_widget = true;
        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    pub(crate) fn is_case_insensitive(&self) -> bool {
        self.case_insensitive
    }

    pub(crate) fn is_invert(&self) -> bool {
        self.filter_invert
    }

    pub(crate) fn is_unset(&self) -> bool {
        self.filter_unset
    }

    fn on_filter_apply_ui(&self, filter_applies: bool) {
        if let Some(ref lambda) = self.on_filter_apply_ui_func {
            lambda(filter_applies)
        }
    }
}

impl UnitPropertyFilter for FilterText {
    fn set_on_filter_apply_ui_func(&mut self, lambda: Option<Box<dyn Fn(bool)>>) {
        self.on_filter_apply_ui_func = lambda
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn text(&self) -> Cow<'_, str> {
        Cow::from(&self.filter_text)
    }

    fn match_type(&self) -> StrMatchType {
        self.match_type
    }

    fn clear_filter(&mut self) {
        self.filter_text.clear();
        self.match_type = StrMatchType::default();
        self.filter_invert = false;
        self.filter_unset = false;

        self.on_filter_apply_ui(false);
    }

    fn is_filter_applies(&self) -> bool {
        !self.filter_text.is_empty() || self.filter_unset || self.filter_invert
    }

    fn ftype(&self) -> UnitPropertyFilterType {
        UnitPropertyFilterType::Text
    }

    fn unit_list_panel(&self) -> &UnitListPanel {
        &self.unit_list_panel
    }

    fn id(&self) -> &String {
        &self.id
    }
}

pub struct FilterNum<T>
where
    T: Debug,
{
    filter_num: Option<T>,
    match_type: NumMatchType,
    on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>,
    filter_unit_func: fn(&FilterNumAssessor<T>, &UnitInfo, glib::Quark) -> bool,
    id: String,
    unit_list_panel: UnitListPanel,
    key: glib::Quark,
    ftype: UnitPropertyFilterType,
}

impl<T> fmt::Debug for FilterNum<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FilterNum")
            .field("filter_num", &self.filter_num)
            .field("id", &self.id)
            .finish()
    }
}

impl<T> FilterNum<T>
where
    T: Debug + Default + PartialEq + PartialOrd + Copy + ToString + 'static,
{
    pub fn new(
        id: &str,
        filter_unit_func: fn(&FilterNumAssessor<T>, &UnitInfo, glib::Quark) -> bool,
        unit_list_panel: &UnitListPanel,
        key: glib::Quark,
        ftype: UnitPropertyFilterType,
    ) -> Self {
        Self {
            filter_num: Default::default(),
            match_type: NumMatchType::default(),
            on_filter_apply_ui_func: Default::default(),
            filter_unit_func,
            id: id.to_string(),
            unit_list_panel: unit_list_panel.clone(),
            key,
            ftype,
        }
    }

    pub fn set_filter_elem(&mut self, f_element: Option<T>, update_widget: bool) {
        if f_element == self.filter_num {
            return;
        }

        let new_is_none = f_element.is_none();
        let filter_applies = !new_is_none;
        let old_is_none = self.filter_num.is_none();

        let change_type = if new_is_none {
            gtk::FilterChange::LessStrict
        } else {
            gtk::FilterChange::Different
        };

        self.filter_num = f_element;

        if old_is_none != new_is_none {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterNumAssessor::new(self)))
        } else {
            None
        };

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    pub fn set_filter_match_type(&mut self, match_type: NumMatchType, update_widget: bool) {
        debug!("Match type new {match_type:?} old {:?}", self.match_type);
        if match_type == self.match_type {
            debug!("exit same");
            return;
        }

        let change_type = match (self.match_type, match_type) {
            (NumMatchType::Equals, NumMatchType::GreaterEquals) => gtk::FilterChange::LessStrict,
            (NumMatchType::Equals, NumMatchType::SmallerEquals) => gtk::FilterChange::LessStrict,
            (NumMatchType::SmallerEquals, NumMatchType::Smaller) => gtk::FilterChange::MoreStrict,
            (NumMatchType::GreaterEquals, NumMatchType::Greater) => gtk::FilterChange::MoreStrict,
            (NumMatchType::Smaller, NumMatchType::SmallerEquals) => gtk::FilterChange::LessStrict,
            (NumMatchType::Greater, NumMatchType::GreaterEquals) => gtk::FilterChange::LessStrict,
            (_, _) => gtk::FilterChange::Different,
        };

        debug!("change_type {change_type:?}");

        self.match_type = match_type;

        //FIXME self.filter_text.is_empty()

        /*         if self.filter_text.is_empty() {
            debug!("exit filter_text empty");
            return;
        } */

        let assessor: Option<Box<dyn UnitPropertyAssessor>> =
            Some(Box::new(FilterNumAssessor::new(self)));

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            update_widget,
        );
    }

    fn on_filter_apply_ui(&self, filter_applies: bool) {
        if let Some(ref lambda) = self.on_filter_apply_ui_func {
            lambda(filter_applies)
        }
    }
}

impl<T> UnitPropertyFilter for FilterNum<T>
where
    T: Debug + Default + PartialEq + PartialOrd + Copy + ToString + 'static,
{
    fn set_on_filter_apply_ui_func(&mut self, on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>) {
        self.on_filter_apply_ui_func = on_filter_apply_ui_func
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn text(&self) -> Cow<'_, str> {
        match &self.filter_num {
            Some(val) => {
                let s = val.to_string();
                Cow::from(s)
            }
            None => Cow::from(""),
        }
    }

    fn match_type(&self) -> StrMatchType {
        StrMatchType::default()
    }

    fn num_match_type(&self) -> NumMatchType {
        self.match_type
    }

    fn clear_filter(&mut self) {
        self.match_type = NumMatchType::default();

        self.on_filter_apply_ui(false);
    }

    fn is_filter_applies(&self) -> bool {
        self.filter_num.is_some()
    }

    fn ftype(&self) -> UnitPropertyFilterType {
        self.ftype
    }

    fn unit_list_panel(&self) -> &UnitListPanel {
        &self.unit_list_panel
    }

    fn id(&self) -> &String {
        &self.id
    }
}

pub trait UnitPropertyAssessor: Debug {
    fn filter_unit(&self, unit: &UnitInfo) -> bool;
    //  fn filter_unit_value(&self, unit_value: &str) -> bool;
    fn id(&self) -> &str;
    fn text(&self) -> &str {
        ""
    }

    fn match_type(&self) -> StrMatchType {
        StrMatchType::default()
    }
}

pub struct FilterBool {
    filter_bool: Option<BoolFilter>,
    on_filter_apply_ui_func: Option<Box<dyn Fn(bool)>>,
    filter_unit_func: fn(&FilterBoolAssessor, &UnitInfo, glib::Quark) -> bool,
    id: String,
    unit_list_panel: UnitListPanel,
    key: glib::Quark,
}

impl Debug for FilterBool {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FilterNum")
            .field("filter_bool", &self.filter_bool)
            .field("id", &self.id)
            .finish()
    }
}

impl FilterBool {
    pub fn new(
        id: &str,
        filter_unit_func: fn(&FilterBoolAssessor, &UnitInfo, glib::Quark) -> bool,
        unit_list_panel: &UnitListPanel,
        key: glib::Quark,
    ) -> Self {
        Self {
            filter_bool: Default::default(),
            on_filter_apply_ui_func: Default::default(),
            filter_unit_func,
            id: id.to_string(),
            unit_list_panel: unit_list_panel.clone(),
            key,
        }
    }

    pub fn set_filter_elem(&mut self, f_element: Option<BoolFilter>) {
        if f_element == self.filter_bool {
            return;
        }

        let new_is_none = f_element.is_none();
        let filter_applies = !new_is_none;
        let old_is_none = self.filter_bool.is_none();

        let change_type = gtk::FilterChange::Different;

        self.filter_bool = f_element;

        if old_is_none != new_is_none {
            self.on_filter_apply_ui(filter_applies);
        }

        let assessor: Option<Box<dyn UnitPropertyAssessor>> = if filter_applies {
            Some(Box::new(FilterBoolAssessor::new(self)))
        } else {
            None
        };

        self.unit_list_panel.filter_assessor_change(
            &self.id,
            assessor,
            Some(change_type),
            true, //TODO check cases
        );
    }

    pub fn filter_value(&self) -> Option<BoolFilter> {
        self.filter_bool
    }

    fn on_filter_apply_ui(&self, filter_applies: bool) {
        if let Some(ref lambda) = self.on_filter_apply_ui_func {
            lambda(filter_applies)
        }
    }
}

impl UnitPropertyFilter for FilterBool {
    fn set_on_filter_apply_ui_func(&mut self, lambda: Option<Box<dyn Fn(bool)>>) {
        self.on_filter_apply_ui_func = lambda
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn text(&self) -> Cow<'_, str> {
        match &self.filter_bool {
            Some(val) => {
                let s = val.code();
                Cow::from(s)
            }
            None => Cow::from(""),
        }
    }

    fn match_type(&self) -> StrMatchType {
        StrMatchType::default()
    }

    fn clear_filter(&mut self) {
        self.filter_bool = Default::default();
        self.on_filter_apply_ui(false);
    }

    fn is_filter_applies(&self) -> bool {
        self.filter_bool.is_some()
    }

    fn ftype(&self) -> UnitPropertyFilterType {
        UnitPropertyFilterType::Bool
    }

    fn unit_list_panel(&self) -> &UnitListPanel {
        &self.unit_list_panel
    }

    fn id(&self) -> &String {
        &self.id
    }
}

#[derive(Debug)]
pub struct FilterElementAssessor<T>
where
    T: Eq + Hash + Debug,
{
    filter_elements: HashSet<T>,
    filter_unit_func: fn(&FilterElementAssessor<T>, &UnitInfo) -> bool,
    id: String,
}

impl<T> FilterElementAssessor<T>
where
    T: Eq + Hash + Debug,
{
    pub(crate) fn filter_unit_value(&self, unit_value: &T) -> bool {
        self.filter_elements.contains(unit_value)
    }
}

impl<T> UnitPropertyAssessor for FilterElementAssessor<T>
where
    T: Eq + Hash + Debug,
{
    fn filter_unit(&self, unit: &UnitInfo) -> bool {
        (self.filter_unit_func)(self, unit)
    }

    fn id(&self) -> &str {
        &self.id
    }
}

#[derive(Debug)]
pub struct FilterTextAssessor {
    filter_text: String,
    filter_unit_func: fn(&FilterTextAssessor, &UnitInfo, glib::Quark) -> bool,
    id: String,
    key: glib::Quark,
    pub(crate) filter_unit_value_func:
        fn(filter_text: &FilterTextAssessor, unit_value: Option<&String>) -> bool,
}

impl FilterTextAssessor {
    fn new(filter_text: &FilterText) -> Self {
        let filter_unit_value_func = if filter_text.filter_unset {
            if filter_text.filter_invert {
                Self::filter_unit_value_func_unset_inv
            } else {
                Self::filter_unit_value_func_unset
            }
        } else {
            match (
                filter_text.match_type,
                filter_text.filter_invert,
                filter_text.case_insensitive,
            ) {
                (StrMatchType::Contains, false, false) => Self::filter_unit_value_func_contains,
                (StrMatchType::Contains, false, true) => {
                    Self::filter_unit_value_func_contains_case_insensitive
                }
                (StrMatchType::Contains, true, false) => {
                    Self::filter_unit_value_func_contains_inv_case_insensitive
                }
                (StrMatchType::Contains, true, true) => Self::filter_unit_value_func_contains_inv,
                (StrMatchType::StartWith, false, false) => Self::filter_unit_value_func_start_with,
                (StrMatchType::StartWith, false, true) => {
                    Self::filter_unit_value_func_start_with_insensitive
                }
                (StrMatchType::StartWith, true, false) => {
                    Self::filter_unit_value_func_start_with_inv
                }
                (StrMatchType::StartWith, true, true) => {
                    Self::filter_unit_value_func_start_with_inv_insensitive
                }
                (StrMatchType::EndWith, false, false) => Self::filter_unit_value_func_end_with,
                (StrMatchType::EndWith, false, true) => {
                    Self::filter_unit_value_func_end_with_insensitive
                }
                (StrMatchType::EndWith, true, false) => Self::filter_unit_value_func_end_with_inv,
                (StrMatchType::EndWith, true, true) => {
                    Self::filter_unit_value_func_end_with_inv_insensitive
                }
                (StrMatchType::Equals, false, false) => Self::filter_unit_value_func_equals,
                (StrMatchType::Equals, false, true) => {
                    Self::filter_unit_value_func_equals_insensitive
                }
                (StrMatchType::Equals, true, false) => Self::filter_unit_value_func_equals_inv,
                (StrMatchType::Equals, true, true) => {
                    Self::filter_unit_value_func_equals_inv_insensitive
                }
            }
        };

        FilterTextAssessor {
            filter_text: if filter_text.case_insensitive {
                filter_text.filter_text.to_ascii_lowercase()
            } else {
                filter_text.filter_text.clone()
            },
            filter_unit_func: filter_text.filter_unit_func,
            id: filter_text.id.clone(),
            filter_unit_value_func,
            key: filter_text.key,
        }
    }

    fn filter_unit_value_func_unset(&self, unit_value: Option<&String>) -> bool {
        unit_value.is_none()
    }

    fn filter_unit_value_func_unset_inv(&self, unit_value: Option<&String>) -> bool {
        unit_value.is_some()
    }

    fn filter_unit_value_func_contains_inv(&self, unit_value: Option<&String>) -> bool {
        !self.filter_unit_value_func_contains(unit_value)
    }

    fn filter_unit_value_func_contains_inv_case_insensitive(
        &self,
        unit_value: Option<&String>,
    ) -> bool {
        !self.filter_unit_value_func_contains_case_insensitive(unit_value)
    }

    fn filter_unit_value_func_contains(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.contains(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_contains_case_insensitive(
        &self,
        unit_value: Option<&String>,
    ) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.to_ascii_lowercase().contains(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_start_with_inv(&self, unit_value: Option<&String>) -> bool {
        !self.filter_unit_value_func_start_with(unit_value)
    }

    fn filter_unit_value_func_start_with_inv_insensitive(
        &self,
        unit_value: Option<&String>,
    ) -> bool {
        !self.filter_unit_value_func_start_with_insensitive(unit_value)
    }

    fn filter_unit_value_func_start_with(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.starts_with(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_start_with_insensitive(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value
                .to_ascii_lowercase()
                .starts_with(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_end_with_inv(&self, unit_value: Option<&String>) -> bool {
        !self.filter_unit_value_func_end_with(unit_value)
    }

    fn filter_unit_value_func_end_with_inv_insensitive(&self, unit_value: Option<&String>) -> bool {
        !self.filter_unit_value_func_end_with_insensitive(unit_value)
    }

    fn filter_unit_value_func_end_with(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.ends_with(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_end_with_insensitive(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.to_ascii_lowercase().ends_with(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_equals_inv(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.ne(&self.filter_text)
        } else {
            true
        }
    }

    fn filter_unit_value_func_equals_inv_insensitive(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.to_ascii_lowercase().ne(&self.filter_text)
        } else {
            true
        }
    }

    fn filter_unit_value_func_equals(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.eq(&self.filter_text)
        } else {
            false
        }
    }

    fn filter_unit_value_func_equals_insensitive(&self, unit_value: Option<&String>) -> bool {
        if let Some(unit_value) = unit_value {
            unit_value.to_ascii_lowercase().eq(&self.filter_text)
        } else {
            false
        }
    }
}

impl UnitPropertyAssessor for FilterTextAssessor {
    fn filter_unit(&self, unit: &UnitInfo) -> bool {
        (self.filter_unit_func)(self, unit, self.key)
    }

    fn id(&self) -> &str {
        &self.id
    }

    fn text(&self) -> &str {
        &self.filter_text
    }
}
pub struct FilterNumAssessor<T>
where
    T: Debug,
{
    filter_num: Option<T>,
    //match_type: MatchType, //TODO make distintive struct to avoid runtime if
    filter_unit_func: fn(&FilterNumAssessor<T>, &UnitInfo, glib::Quark) -> bool,
    id: String,
    pub(crate) filter_unit_value_func:
        fn(filter_text: &FilterNumAssessor<T>, unit_value: Option<T>) -> bool,
    key: glib::Quark,
}

impl<T> Debug for FilterNumAssessor<T>
where
    T: Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FilterNumAssessor")
            .field("id", &self.id)
            .field("key", &self.key)
            .field("filter_num", &self.filter_num)
            .finish()
    }
}

impl<T> FilterNumAssessor<T>
where
    T: Debug + Copy + PartialEq + PartialOrd,
{
    fn new(filter_num: &FilterNum<T>) -> Self {
        let filter_unit_value_func = match (filter_num.filter_num, filter_num.match_type) {
            (None, _) => Self::filter_unit_value_func_empty,

            (Some(_), NumMatchType::Equals) => Self::filter_unit_value_func_equals,
            (Some(_), NumMatchType::Greater) => Self::filter_unit_value_func_greater,
            (Some(_), NumMatchType::Smaller) => Self::filter_unit_value_func_smaller,
            (Some(_), NumMatchType::GreaterEquals) => Self::filter_unit_value_func_greater_equals,
            (Some(_), NumMatchType::SmallerEquals) => Self::filter_unit_value_func_smaller_equals,
        };

        Self {
            filter_num: filter_num.filter_num,
            filter_unit_func: filter_num.filter_unit_func,
            id: filter_num.id.clone(),
            filter_unit_value_func,
            key: filter_num.key,
        }
    }

    fn filter_unit_value_func_empty(&self, _unit_value: Option<T>) -> bool {
        true
    }

    fn filter_unit_value_func_equals(&self, unit_value: Option<T>) -> bool {
        unit_value == self.filter_num
    }

    fn filter_unit_value_func_smaller(&self, unit_value: Option<T>) -> bool {
        unit_value < self.filter_num
    }

    fn filter_unit_value_func_smaller_equals(&self, unit_value: Option<T>) -> bool {
        unit_value <= self.filter_num
    }

    fn filter_unit_value_func_greater(&self, unit_value: Option<T>) -> bool {
        unit_value > self.filter_num
    }

    fn filter_unit_value_func_greater_equals(&self, unit_value: Option<T>) -> bool {
        unit_value >= self.filter_num
    }
}

impl<T> UnitPropertyAssessor for FilterNumAssessor<T>
where
    T: Debug,
{
    fn filter_unit(&self, unit: &UnitInfo) -> bool {
        (self.filter_unit_func)(self, unit, self.key)
    }

    fn id(&self) -> &str {
        &self.id
    }

    fn text(&self) -> &str {
        ""
    }
}

pub struct FilterBoolAssessor {
    //match_type: MatchType, //TODO make distintive struct to avoid runtime if
    filter_unit_func: fn(&FilterBoolAssessor, &UnitInfo, glib::Quark) -> bool,
    id: String,
    pub(crate) filter_unit_value_func:
        fn(filter_text: &FilterBoolAssessor, unit_value: Option<bool>) -> bool,
    key: glib::Quark,
}

impl Debug for FilterBoolAssessor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("FilterBoolAssessor")
            .field("id", &self.id)
            .field("key", &self.key)
            .finish()
        //The comparator is in the function pointer
    }
}

impl FilterBoolAssessor {
    fn new(filter_bool: &FilterBool) -> Self {
        let filter_unit_value_func = match filter_bool.filter_bool {
            Some(BoolFilter::True) => Self::compare_true,
            Some(BoolFilter::False) => Self::compare_false,
            Some(BoolFilter::Set) => Self::is_set,
            Some(BoolFilter::Unset) => Self::is_not_set,
            None => Self::empty,
        };

        Self {
            filter_unit_func: filter_bool.filter_unit_func,
            id: filter_bool.id.clone(),
            filter_unit_value_func,
            key: filter_bool.key,
        }
    }

    fn empty(&self, _unit_value: Option<bool>) -> bool {
        true
    }

    fn is_set(&self, unit_value: Option<bool>) -> bool {
        unit_value.is_some()
    }

    fn is_not_set(&self, unit_value: Option<bool>) -> bool {
        unit_value.is_none()
    }

    fn compare_true(&self, unit_value: Option<bool>) -> bool {
        Some(true) == unit_value
    }

    fn compare_false(&self, unit_value: Option<bool>) -> bool {
        Some(false) == unit_value
    }
}

impl UnitPropertyAssessor for FilterBoolAssessor {
    fn filter_unit(&self, unit: &UnitInfo) -> bool {
        (self.filter_unit_func)(self, unit, self.key)
    }

    fn id(&self) -> &str {
        &self.id
    }

    fn text(&self) -> &str {
        ""
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn test_comp() {
        let a = 4;
        let b = None;

        let c = Some(a) < b;

        println!("c {c}");

        let d = Some(a) > b;

        println!("d {d}");
    }
}
