mod dropdown;
mod imp;
mod substate;
pub mod unit_prop_filter;

use adw::subclass::prelude::ObjectSubclassIsExt;
use gtk::{
    glib::{self},
    prelude::ObjectExt,
};

use strum::{EnumIter, IntoEnumIterator};

use super::UnitListPanel;
use crate::{
    systemd::{
        data::UnitInfo,
        enums::{ActiveState, UnitFileStatus, LoadState, Preset, UnitType},
    },
    widget::unit_list::{
        COL_ID_UNIT,
        filter::unit_prop_filter::{
            FilterBoolAssessor, FilterElementAssessor, FilterNumAssessor, FilterTextAssessor,
        },
    },
};
use base::enums::UnitDBusLevel;

// ANCHOR: mod
glib::wrapper! {
    pub struct UnitListFilterWindow(ObjectSubclass<imp::UnitListFilterWindowImp>)
    @extends adw::Window, gtk::Window, gtk::Widget,
    @implements gtk::Accessible,  gtk::Buildable,  gtk::ConstraintTarget,
    gtk::Native, gtk::Root, gtk::ShortcutManager;
}

impl UnitListFilterWindow {
    pub fn new(selected_filter: Option<String>, unit_list_panel: &UnitListPanel) -> Self {
        let selected_filter = if selected_filter.is_none() {
            Some(COL_ID_UNIT.to_owned())
        } else {
            selected_filter
        };

        let obj: UnitListFilterWindow = glib::Object::builder()
            .property("selected", selected_filter)
            .build();

        let _ = obj.imp().unit_list_panel.set(unit_list_panel.clone());

        obj
    }

    pub fn construct_filter_dialog(&self) {
        self.imp().get_filter()
    }
}

pub fn filter_unit_name(
    property_assessor: &FilterTextAssessor,
    unit: &UnitInfo,
    _key: glib::Quark,
) -> bool {
    let name = unit.display_name();
    (property_assessor.filter_unit_value_func)(property_assessor, Some(&name))
}

pub fn filter_bus_level(
    property_assessor: &FilterElementAssessor<UnitDBusLevel>,
    unit: &UnitInfo,
) -> bool {
    property_assessor.filter_unit_value(&unit.dbus_level())
}

pub fn filter_unit_type(
    property_assessor: &FilterElementAssessor<UnitType>,
    unit: &UnitInfo,
) -> bool {
    property_assessor.filter_unit_value(&unit.unit_type())
}

pub fn filter_preset(property_assessor: &FilterElementAssessor<Preset>, unit: &UnitInfo) -> bool {
    property_assessor.filter_unit_value(&unit.preset())
}

pub fn filter_enable_status(
    property_assessor: &FilterElementAssessor<UnitFileStatus>,
    unit: &UnitInfo,
) -> bool {
    property_assessor.filter_unit_value(&unit.enable_status())
}

pub fn filter_load_state(
    property_assessor: &FilterElementAssessor<LoadState>,
    unit: &UnitInfo,
) -> bool {
    property_assessor.filter_unit_value(&unit.load_state())
}

pub fn filter_active_state(
    property_assessor: &FilterElementAssessor<ActiveState>,
    unit: &UnitInfo,
) -> bool {
    let active_state = unit.active_state();
    property_assessor.filter_unit_value(&active_state)
}

pub fn filter_sub_state(
    property_assessor: &FilterElementAssessor<String>,
    unit: &UnitInfo,
) -> bool {
    property_assessor.filter_unit_value(&unit.sub_state())
}

pub fn filter_unit_description(
    property_assessor: &FilterTextAssessor,
    unit: &UnitInfo,
    _key: glib::Quark,
) -> bool {
    (property_assessor.filter_unit_value_func)(property_assessor, unit.description().as_ref())
}

pub fn custom_num<T>(
    property_assessor: &FilterNumAssessor<T>,
    unit: &UnitInfo,
    key: glib::Quark,
) -> bool
where
    T: std::fmt::Debug
        + std::default::Default
        + for<'a> TryFrom<&'a zvariant::Value<'a>>
        + 'static
        + Copy,
    for<'a> zvariant::Error:
        std::convert::From<<T as std::convert::TryFrom<&'a zvariant::Value<'a>>>::Error>,
{
    let value = unsafe { unit.qdata::<T>(key) }
        .map(|value_ptr| unsafe { value_ptr.as_ref() })
        .copied();
    (property_assessor.filter_unit_value_func)(property_assessor, value)
}

pub fn custom_str(
    property_assessor: &FilterTextAssessor,
    unit: &UnitInfo,
    key: glib::Quark,
) -> bool {
    let value = unsafe { unit.qdata::<String>(key) }.map(|value_ptr| unsafe { value_ptr.as_ref() });
    (property_assessor.filter_unit_value_func)(property_assessor, value)
}

pub fn custom_bool(
    property_assessor: &FilterBoolAssessor,
    unit: &UnitInfo,
    key: glib::Quark,
) -> bool {
    let value = unsafe { unit.qdata::<bool>(key) }.map(|value_ptr| unsafe { value_ptr.read() });
    (property_assessor.filter_unit_value_func)(property_assessor, value)
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumIter, Hash, glib::Enum, PartialOrd, Ord)]
#[enum_type(name = "ActiveState")]
pub enum BoolFilter {
    True,
    False,
    Set,
    Unset,
}

impl BoolFilter {
    fn code(&self) -> &str {
        match self {
            BoolFilter::True => "True",
            BoolFilter::False => "False",
            BoolFilter::Set => "Set",
            BoolFilter::Unset => "Unset",
        }
    }

    fn label(&self) -> String {
        match self {
            BoolFilter::True => "True".to_owned(),
            BoolFilter::False => "False".to_owned(),
            BoolFilter::Set => "<i>Set</i>".to_owned(),
            BoolFilter::Unset => "<i>Unset</i>".to_owned(),
        }
    }

    fn tooltip_info(&self) -> Option<String> {
        None
    }

    fn from_code(code: &str) -> Option<BoolFilter> {
        BoolFilter::iter().find(|bf| bf.code() == code)
    }
}
