use crate::util::constants;
use glib::{Properties, prelude::*, subclass::prelude::*};
use html2gtk::{FontStyle, Justification};
use serde::{Deserialize, Serialize};
use std::cell::Cell;
use std::default::Default;

mod imp {
    use super::*;

    #[derive(Default, Debug, Serialize, Deserialize, Properties)]
    #[properties(wrapper_type = super::ArticleViewSettings)]
    pub struct ArticleViewSettings {
        #[property(get, set, name = "font-style", builder(FontStyle::SansSerif))]
        #[serde(default)]
        pub font_style: Cell<FontStyle>,

        #[property(get, set, name = "justification", builder(Justification::Left))]
        #[serde(default)]
        pub justification: Cell<Justification>,

        #[property(get, set, name = "font-size", default = constants::DEFAULT_FONT_SIZE)]
        #[serde(default)]
        pub font_size: Cell<u32>,

        #[property(get, set, name = "content-width", default = constants::DEFAULT_ARTICLE_CONTENT_WIDTH)]
        #[serde(default)]
        pub content_width: Cell<u32>,

        #[property(get, set, name = "line-height", default = constants::DEFAULT_ARTICLE_LINE_HEIGHT)]
        #[serde(default)]
        pub line_height: Cell<f32>,

        #[property(get, set, default = constants::DEFAULT_ARTICLE_SELECTABLE_TEXT)]
        #[serde(default)]
        pub selectable: Cell<bool>,
    }

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

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

glib::wrapper! {
    pub struct ArticleViewSettings(ObjectSubclass<imp::ArticleViewSettings>);
}

impl Serialize for ArticleViewSettings {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.imp().serialize(serializer)
    }
}

impl<'de> Deserialize<'de> for ArticleViewSettings {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let inner = imp::ArticleViewSettings::deserialize(deserializer)?;
        Ok(inner.into())
    }
}

impl From<imp::ArticleViewSettings> for ArticleViewSettings {
    fn from(inner: imp::ArticleViewSettings) -> Self {
        let line_height = inner.line_height.get();
        let content_width = inner.content_width.get();
        let font_size = inner.font_size.get();

        let line_height = if line_height == 0.0 {
            constants::DEFAULT_ARTICLE_LINE_HEIGHT
        } else {
            line_height
        };
        let content_width = if content_width == 0 {
            constants::DEFAULT_ARTICLE_CONTENT_WIDTH
        } else {
            content_width
        };
        let font_size = if font_size == 0 {
            constants::DEFAULT_FONT_SIZE
        } else {
            font_size
        };

        glib::Object::builder()
            .property("font-style", inner.font_style.get())
            .property("justification", inner.justification.get())
            .property("font-size", font_size)
            .property("content-width", content_width)
            .property("line-height", line_height)
            .property("selectable", inner.selectable.get())
            .build()
    }
}

impl Default for ArticleViewSettings {
    fn default() -> Self {
        imp::ArticleViewSettings::default().into()
    }
}
