// SPDX-FileCopyrightText: 2025 Andrey Kutejko <andy128k@gmail.com>
// SPDX-FileCopyrightText: 2026 Wladimir Palant https://palant.info/
//
// SPDX-License-Identifier: GPL-3.0-or-later

use super::item::FileListItem;
use super::list::apply_css;
use gtk::{glib, pango, prelude::*, subclass::prelude::*};

mod imp {
    use super::*;
    use std::cell::{Cell, RefCell};

    #[derive(glib::Properties, Default)]
    #[properties(wrapper_type = super::FileListCell)]
    pub struct FileListCell {
        #[property(get, set)]
        text: RefCell<String>,

        #[property(get, set = Self::set_item, nullable)]
        item: RefCell<Option<FileListItem>>,
        #[property(get, set)]
        xalign: Cell<f32>,
        #[property(get, set = Self::set_selected)]
        selected: Cell<bool>,
        #[property(get, set = Self::set_use_ls_colors)]
        use_ls_colors: Cell<bool>,

        pub label: gtk::Label,
        pub bindings: RefCell<Vec<glib::Binding>>,
    }

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

    #[glib::derived_properties]
    impl ObjectImpl for FileListCell {
        fn constructed(&self) {
            self.parent_constructed();

            let this = self.obj();
            this.set_layout_manager(Some(gtk::BinLayout::new()));

            this.set_hexpand(true);
            this.set_vexpand(true);
            this.add_css_class("cell");

            self.label.set_parent(&*this);
            self.label.set_hexpand(true);
            self.label.set_vexpand(true);
            self.label.set_ellipsize(pango::EllipsizeMode::Middle);

            this.bind_property("text", &self.label, "label")
                .sync_create()
                .build();
            this.bind_property("text", &self.label, "tooltip-text")
                .sync_create()
                .build();
            this.bind_property("xalign", &self.label, "xalign")
                .sync_create()
                .build();
        }

        fn dispose(&self) {
            while let Some(child) = self.obj().first_child() {
                child.unparent();
            }
        }
    }

    impl WidgetImpl for FileListCell {}

    impl FileListCell {
        fn set_item(&self, item: Option<FileListItem>) {
            for binding in self.bindings.borrow_mut().drain(..) {
                binding.unbind();
            }
            *self.item.borrow_mut() = item.clone();
            if let Some(ref item) = item {
                self.bindings.borrow_mut().push(
                    item.bind_property("selected", self.obj().as_ref(), "selected")
                        .sync_create()
                        .build(),
                );
            }
        }

        fn set_selected(&self, value: bool) {
            self.selected.set(value);
            self.apply_css();
        }

        fn set_use_ls_colors(&self, value: bool) {
            self.use_ls_colors.set(value);
            self.apply_css();
        }

        fn apply_css(&self) {
            if let Some(item) = &*self.item.borrow() {
                apply_css(
                    item,
                    self.use_ls_colors.get(),
                    self.obj().upcast_ref::<gtk::Widget>(),
                );
            }
        }
    }
}

glib::wrapper! {
    pub struct FileListCell(ObjectSubclass<imp::FileListCell>)
        @extends gtk::Widget,
        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}

impl Default for FileListCell {
    fn default() -> Self {
        glib::Object::builder().build()
    }
}

impl FileListCell {
    pub fn bind(&self, item: &FileListItem) {
        self.set_item(Some(item));
    }

    pub fn unbind(&self) {
        self.set_item(None::<FileListItem>);
    }

    pub fn add_binding(&self, binding: glib::Binding) {
        self.imp().bindings.borrow_mut().push(binding);
    }

    pub fn clear_bindings(&self) {
        for binding in self.imp().bindings.borrow_mut().drain(..) {
            binding.unbind();
        }
    }
}
