// Fragments - torrent_sorter.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::subclass::prelude::*;
use glib::{Enum, Properties};
use gtk::glib;
use gtk::prelude::*;
use gtk::subclass::sorter::SorterImpl;
use transmission_gobject::TrTorrent;

mod imp {
    use super::*;

    #[derive(Debug, Default, Properties)]
    #[properties(wrapper_type = super::FrgTorrentSorter)]
    pub struct FrgTorrentSorter {
        #[property(get, set = Self::set_descending)]
        descending: RefCell<bool>,
        #[property(get, set = Self::set_sorting, builder(Default::default()))]
        sorting: RefCell<FrgTorrentSorting>,
    }

    impl FrgTorrentSorter {
        pub fn set_sorting(&self, sorting: FrgTorrentSorting) {
            *self.sorting.borrow_mut() = sorting;
            self.obj().changed(gtk::SorterChange::Different);
        }

        pub fn set_descending(&self, descending: bool) {
            *self.descending.borrow_mut() = descending;
            self.obj().changed(gtk::SorterChange::Different);
        }
    }

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

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

    impl SorterImpl for FrgTorrentSorter {
        fn order(&self) -> gtk::SorterOrder {
            gtk::SorterOrder::Total
        }

        fn compare(&self, item1: &glib::Object, item2: &glib::Object) -> gtk::Ordering {
            let a = item1.downcast_ref::<TrTorrent>().unwrap();
            let b = item2.downcast_ref::<TrTorrent>().unwrap();

            Self::torrent_cmp(a, b, *self.sorting.borrow(), *self.descending.borrow()).into()
        }
    }

    impl FrgTorrentSorter {
        fn torrent_cmp(
            a: &TrTorrent,
            b: &TrTorrent,
            sorting: FrgTorrentSorting,
            descending: bool,
        ) -> std::cmp::Ordering {
            let mut torrent_a = a.clone();
            let mut torrent_b = b.clone();

            if descending {
                std::mem::swap(&mut torrent_a, &mut torrent_b);
            }

            match sorting {
                FrgTorrentSorting::Default => std::cmp::Ordering::Equal,
                FrgTorrentSorting::Name => torrent_a.name().cmp(&torrent_b.name()),
                FrgTorrentSorting::Queue => {
                    torrent_a.queue_position().cmp(&torrent_b.queue_position())
                }
            }
        }
    }
}

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

impl FrgTorrentSorter {
    pub fn new() -> Self {
        Self::default()
    }
}

impl Default for FrgTorrentSorter {
    fn default() -> Self {
        glib::Object::new()
    }
}

#[derive(Default, Display, Copy, Debug, Clone, EnumString, PartialEq, Enum, Eq)]
#[repr(u32)]
#[enum_type(name = "FrgTorrentSorting")]
pub enum FrgTorrentSorting {
    #[default]
    Default,
    Name,
    Queue,
}
