// Fragments - file_filter.rs
// Copyright (C) 2022-2023  Felix Häcker <haeckerfelix@gnome.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use std::cell::RefCell;

use glib::Properties;
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::filter::FilterImpl;
use gtk::subclass::prelude::*;
use transmission_gobject::TrFile;

mod imp {
    use super::*;

    #[derive(Debug, Default, Properties)]
    #[properties(wrapper_type = super::FrgFileFilter)]
    pub struct FrgFileFilter {
        #[property(get, set = Self::set_folder, nullable)]
        folder: RefCell<Option<TrFile>>,
        #[property(get, set = Self::set_search)]
        search: RefCell<String>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for FrgFileFilter {
        const NAME: &'static str = "FrgFileFilter";
        type Type = super::FrgFileFilter;
        type ParentType = gtk::Filter;
    }

    #[glib::derived_properties]
    impl ObjectImpl for FrgFileFilter {}

    impl FilterImpl for FrgFileFilter {
        fn match_(&self, item: &glib::Object) -> bool {
            let file = item.downcast_ref::<TrFile>().unwrap();
            let file_name = file.name();
            let search = self.obj().search().to_lowercase();

            // Filter by folder name
            if let Some(folder) = self.obj().folder() {
                let folder_name = format!("{}/", folder.name());

                // First check if the file is a child of `folder`
                if file_name.starts_with(&folder_name) {
                    // Get the rest of the path
                    //
                    // For example
                    // filter: this/is/a/long/nested/path/
                    // item:   this/is/a/
                    // result:           long/nested/path
                    let path_suffix = file_name.trim_start_matches(&folder_name);

                    if search.is_empty() {
                        // Path suffix contains another slash -> another nested subfolder,
                        // but which isn't a *direct* child of the filtered folder
                        return !path_suffix.contains('/');
                    }

                    return path_suffix.to_lowercase().contains(&search);
                } else {
                    return false;
                }
            }

            true
        }
    }

    impl FrgFileFilter {
        fn set_folder(&self, folder: Option<&TrFile>) {
            *self.folder.borrow_mut() = folder.cloned();
            self.obj().changed(gtk::FilterChange::Different);
        }

        fn set_search(&self, search: String) {
            *self.search.borrow_mut() = search;
            self.obj().changed(gtk::FilterChange::Different);
        }
    }
}

glib::wrapper! {
    pub struct FrgFileFilter(ObjectSubclass<imp::FrgFileFilter>) @extends gtk::Filter;
}

impl Default for FrgFileFilter {
    fn default() -> Self {
        Self::new()
    }
}

impl FrgFileFilter {
    pub fn new() -> Self {
        glib::Object::new()
    }
}
