ludusavi 0.18.0

Game save backup tool
use fuzzy_matcher::FuzzyMatcher;
use iced::Alignment;

use crate::{
    gui::{
        common::{Message, Screen, UndoSubject},
        shortcuts::TextHistories,
        widget::{Checkbox, Column, Element, IcedParentExt, PickList, Row, Text},
    },
    lang::TRANSLATOR,
    scan::{
        game_filter::{self, FilterKind},
        Duplication, ScanInfo,
    },
};

#[derive(Default, Clone, Eq, PartialEq)]
pub struct Filter<T> {
    active: bool,
    pub choice: T,
}

#[derive(Default)]
pub struct FilterComponent {
    pub show: bool,
    pub game_name: String,
    pub uniqueness: Filter<game_filter::Uniqueness>,
    pub completeness: Filter<game_filter::Completeness>,
    pub enablement: Filter<game_filter::Enablement>,
}

fn template<'a, T: 'static + Default + Copy + Eq + PartialEq + ToString>(
    filter: &'a Filter<T>,
    kind: FilterKind,
    options: &'a [T],
    message: fn(T) -> Message,
) -> Element<'a> {
    Row::new()
        .spacing(10)
        .align_items(Alignment::Center)
        .push(
            Checkbox::new("", filter.active, move |enabled| Message::ToggledSearchFilter {
                filter: kind,
                enabled,
            })
            .spacing(0),
        )
        .push(PickList::new(options, Some(filter.choice), message))
        .into()
}

impl FilterComponent {
    pub fn qualifies(
        &self,
        scan: &ScanInfo,
        enabled: bool,
        duplicated: Duplication,
        show_deselected_games: bool,
    ) -> bool {
        let fuzzy = self.game_name.is_empty()
            || fuzzy_matcher::skim::SkimMatcherV2::default()
                .fuzzy_match(&scan.game_name, &self.game_name)
                .is_some();
        let unique = !self.uniqueness.active || self.uniqueness.choice.qualifies(duplicated);
        let complete = !self.completeness.active || self.completeness.choice.qualifies(scan);
        let enable = !show_deselected_games || !self.enablement.active || self.enablement.choice.qualifies(enabled);

        fuzzy && unique && complete && enable
    }

    pub fn toggle_filter(&mut self, filter: FilterKind, enabled: bool) {
        match filter {
            FilterKind::Uniqueness => self.uniqueness.active = enabled,
            FilterKind::Completeness => self.completeness.active = enabled,
            FilterKind::Enablement => self.enablement.active = enabled,
        }
    }

    pub fn view(&self, screen: Screen, histories: &TextHistories, show_deselected_games: bool) -> Option<Element> {
        if !self.show {
            return None;
        }
        Some(
            Column::new()
                .push(
                    Row::new()
                        .padding([0, 20, 20, 20])
                        .spacing(20)
                        .align_items(Alignment::Center)
                        .push(Text::new(TRANSLATOR.filter_label()))
                        .push(histories.input(match screen {
                            Screen::Restore => UndoSubject::RestoreSearchGameName,
                            _ => UndoSubject::BackupSearchGameName,
                        }))
                        .push(template(
                            &self.uniqueness,
                            FilterKind::Uniqueness,
                            game_filter::Uniqueness::ALL,
                            Message::EditedSearchFilterUniqueness,
                        ))
                        .push(template(
                            &self.completeness,
                            FilterKind::Completeness,
                            game_filter::Completeness::ALL,
                            Message::EditedSearchFilterCompleteness,
                        ))
                        .push_if(
                            || show_deselected_games,
                            || {
                                template(
                                    &self.enablement,
                                    FilterKind::Enablement,
                                    game_filter::Enablement::ALL,
                                    Message::EditedSearchFilterEnablement,
                                )
                            },
                        ),
                )
                .into(),
        )
    }
}