// Fragments - file_row.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 adw::subclass::prelude::*;
use glib::{clone, subclass, Properties};
use gtk::prelude::*;
use gtk::{gio, glib, CompositeTemplate};
use transmission_gobject::TrFile;

use crate::i18n::{i18n_f, ni18n_f};
use crate::{actions, utils};

mod imp {
    use super::*;

    #[derive(Debug, Default, CompositeTemplate, Properties)]
    #[template(resource = "/de/haeckerfelix/Fragments/gtk/file_row.ui")]
    #[properties(wrapper_type = super::FrgFileRow)]
    pub struct FrgFileRow {
        #[template_child]
        mimetype_image: TemplateChild<gtk::Image>,
        #[template_child]
        stack: TemplateChild<gtk::Stack>,
        #[template_child]
        title_label: TemplateChild<gtk::Label>,
        #[template_child]
        subtitle_label: TemplateChild<gtk::Label>,
        #[template_child]
        progressbar: TemplateChild<gtk::ProgressBar>,
        #[template_child]
        checkbutton: TemplateChild<gtk::CheckButton>,
        #[template_child]
        folder_arrow_image: TemplateChild<gtk::Image>,

        #[property(get, set=Self::set_file)]
        file: RefCell<Option<TrFile>>,

        bindings: RefCell<Vec<glib::Binding>>,
        signal_handlers: RefCell<Vec<glib::SignalHandlerId>>,
    }

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

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

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

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

    impl WidgetImpl for FrgFileRow {}

    impl BinImpl for FrgFileRow {}

    impl FrgFileRow {
        pub fn set_file(&self, file: Option<&TrFile>) {
            // Drop all bindings from the previous file
            while let Some(binding) = self.bindings.borrow_mut().pop() {
                binding.unbind();
            }
            while let Some(id) = self.signal_handlers.borrow_mut().pop() {
                if let Some(file) = self.obj().file() {
                    file.disconnect(id);
                }
            }

            self.file.replace(file.cloned());
            self.update_widgets();
        }

        fn update_widgets(&self) {
            if let Some(file) = self.obj().file() {
                // Actions
                actions::install_file_actions(&file, self.obj().clone().upcast());

                // Title
                self.title_label.set_label(&file.title());
                self.obj().set_tooltip_text(Some(&file.name()));

                // Row icon
                if file.is_folder() {
                    self.mimetype_image.set_icon_name(Some("folder-symbolic"));
                    self.folder_arrow_image.set_visible(true);
                } else {
                    let mimetype = gio::content_type_guess(Some(&file.name()), &[])
                        .0
                        .to_string();
                    utils::set_mimetype_image(&mimetype, &self.mimetype_image.get())
                }

                // Wanted checkbox
                let binding = file
                    .bind_property("wanted", &self.checkbutton.get(), "active")
                    .sync_create()
                    .bidirectional()
                    .build();
                self.bindings.borrow_mut().push(binding);

                let binding = file
                    .bind_property("bytes-completed", &self.checkbutton.get(), "sensitive")
                    .transform_to(|b, bytes: i64| {
                        Some(bytes != b.source().unwrap().downcast::<TrFile>().unwrap().length())
                    })
                    .sync_create()
                    .build();
                self.bindings.borrow_mut().push(binding);

                let binding = file
                    .bind_property(
                        "wanted-inconsistent",
                        &self.checkbutton.get(),
                        "inconsistent",
                    )
                    .sync_create()
                    .build();
                self.bindings.borrow_mut().push(binding);

                // Progress & Subtitle
                let id = file.connect_notify_local(
                    Some("bytes-completed"),
                    clone!(@weak self as this => move|_,_|{
                        this.update_progressbar();
                        this.update_subtitle();
                    }),
                );
                self.signal_handlers.borrow_mut().push(id);

                let id = file.connect_notify_local(
                    Some("wanted"),
                    clone!(@weak self as this => move|_,_|{
                        this.update_progressbar();
                        this.update_subtitle();
                    }),
                );
                self.signal_handlers.borrow_mut().push(id);

                self.update_progressbar();
                self.update_subtitle();
            }
        }

        fn update_progressbar(&self) {
            let file = self.obj().file().unwrap();
            let progressbar = &self.progressbar;

            let mut percentage = file.bytes_completed() as f64 / file.length() as f64;
            if !percentage.is_normal() {
                percentage = 0.0;
            }

            if percentage > 0.0 || file.wanted() {
                self.stack.set_visible_child_name("progress");
                progressbar.set_fraction(percentage);
            } else {
                self.stack.set_visible_child_name("no-progress");
            }

            if file.wanted() {
                self.obj().remove_css_class("inactive");
            } else {
                self.obj().add_css_class("inactive");
            }
        }

        fn update_subtitle(&self) {
            let file = self.obj().file().unwrap();

            let length = utils::format_size(file.length());
            let bytes_completed = utils::format_size(file.bytes_completed());
            let items = file.related().n_items();

            #[rustfmt::skip]
            let subtitle = if file.bytes_completed() > 0 || file.wanted() {
                if file.is_folder() {
                    ni18n_f("{} / {}, {} item", "{} / {}, {} items",items, &[&bytes_completed, &length, &items.to_string()])
                } else {
                    i18n_f("{} / {}", &[&bytes_completed, &length])
                }
            } else if file.is_folder() {
                ni18n_f("{}, {} item", "{}, {} items", items, &[&length, &items.to_string()])
            } else {
                length
            };

            self.subtitle_label.set_text(&subtitle);
        }
    }
}

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

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

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