// Fragments - torrent_page.rs
// Copyright (C) 2022-2024  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 adw::subclass::prelude::*;
use glib::{clone, subclass};
use gtk::prelude::*;
use gtk::{glib, CompositeTemplate, FilterListModel};
use transmission_gobject::{TrTorrent, TrTorrentStatus};

use crate::backend::FrgConnectionManager;
use crate::model::{FrgTorrentFilter, FrgTorrentSorter, FrgTorrentSorting};
use crate::ui::FrgTorrentGroup;
use crate::FrgApplication;

mod imp {
    use super::*;

    #[derive(Default, Debug, CompositeTemplate)]
    #[template(resource = "/de/haeckerfelix/Fragments/gtk/torrent_page.ui")]
    pub struct FrgTorrentPage {
        #[template_child]
        downloading_group: TemplateChild<FrgTorrentGroup>,
        #[template_child]
        queued_group: TemplateChild<FrgTorrentGroup>,
        #[template_child]
        seeding_group: TemplateChild<FrgTorrentGroup>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for FrgTorrentPage {
        const NAME: &'static str = "FrgTorrentPage";
        type ParentType = adw::PreferencesPage;
        type Type = super::FrgTorrentPage;

        fn class_init(klass: &mut Self::Class) {
            klass.bind_template();
        }

        fn instance_init(obj: &subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    impl ObjectImpl for FrgTorrentPage {
        fn constructed(&self) {
            self.parent_constructed();

            // Downloading group
            self.downloading_group
                .add_model(self.model(TrTorrentStatus::Download, FrgTorrentSorting::Name));

            // Queued group
            self.queued_group
                .add_model(self.model(TrTorrentStatus::DownloadWait, FrgTorrentSorting::Queue));
            self.queued_group
                .add_model(self.model(TrTorrentStatus::Stopped, FrgTorrentSorting::Name));
            self.queued_group
                .add_model(self.model(TrTorrentStatus::Check, FrgTorrentSorting::Name));
            self.queued_group
                .add_model(self.model(TrTorrentStatus::CheckWait, FrgTorrentSorting::Name));

            // Seeding group
            self.seeding_group
                .add_model(self.model(TrTorrentStatus::Seed, FrgTorrentSorting::Name));
            self.seeding_group
                .add_model(self.model(TrTorrentStatus::SeedWait, FrgTorrentSorting::Name));
        }
    }

    impl WidgetImpl for FrgTorrentPage {}

    impl PreferencesPageImpl for FrgTorrentPage {}

    impl FrgTorrentPage {
        fn model(&self, status: TrTorrentStatus, sorting: FrgTorrentSorting) -> gtk::SortListModel {
            let model = FrgConnectionManager::default().client().torrents();
            let app = FrgApplication::default();

            // Filter by group
            let group_filter = FrgTorrentFilter::new();
            group_filter.set_status(status);

            // Filter by search
            let search_filter = gtk::StringFilter::new(Some(&gtk::PropertyExpression::new(
                TrTorrent::static_type(),
                None::<&gtk::Expression>,
                "name",
            )));
            app.bind_property("search", &search_filter, "search")
                .build();

            // Sort by name
            let sorter = FrgTorrentSorter::new();
            sorter.set_sorting(sorting);

            // Update group and search filter when torrents model changes
            model.connect_local(
                "status-changed",
                false,
                clone!(@weak group_filter, @weak sorter => @default-return None, move |_| {
                    group_filter.changed(gtk::FilterChange::Different);
                    sorter.changed(gtk::SorterChange::Different);
                    None
                }),
            );

            let group_model = FilterListModel::new(Some(model), Some(group_filter));
            let search_model = FilterListModel::new(Some(group_model), Some(search_filter));

            gtk::SortListModel::new(Some(search_model), Some(sorter))
        }
    }
}

glib::wrapper! {
    pub struct FrgTorrentPage(
        ObjectSubclass<imp::FrgTorrentPage>)
        @extends gtk::Widget, adw::PreferencesPage;
}

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

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