use crate::{builder::validator::iconinfo::IconInfo, prelude::*};
use anyhow::Result;
use utilities::prelude::*;
use vulkan_rs::prelude::*;

use std::sync::{
    atomic::{AtomicBool, Ordering::SeqCst},
    Arc, RwLock,
};

use super::{fill_type::FillType, IconBuilderType, IconizableWrapper, TextableWrapper};

pub struct IconBuilder {
    content: Option<IconBuilderType>,
    background: Option<FillTypeInfo>,
    margin: u32,

    text: Option<String>,
    text_color: Color,
    text_ratio: f32,
}

impl IconBuilder {
    pub fn set_margin(mut self, margin: u32) -> Self {
        self.margin = margin;

        self
    }

    pub fn set_content(mut self, content: impl Into<IconBuilderType>) -> Self {
        self.content = Some(content.into());

        self
    }

    pub fn set_background(mut self, background: FillTypeInfo) -> Self {
        self.background = Some(background);

        self
    }

    pub fn set_text(mut self, text: &str) -> Self {
        self.text = Some(text.to_string());

        self
    }

    pub fn set_text_color(mut self, color: Color) -> Self {
        self.text_color = color;

        self
    }

    pub fn set_text_ratio(mut self, ratio: f32) -> Self {
        self.text_ratio = ratio;

        self
    }

    pub fn build(self, gui_handler: Arc<GuiHandler>) -> Result<Arc<Icon>> {
        let framable = Framable::new(gui_handler, false)?;

        let iconizable_wrapper =
            IconizableWrapper::new(framable.clone(), self.content, self.margin)?;

        let background = RwLock::new(
            self.background
                .map(|info| FillType::new(framable.clone(), info))
                .transpose()?,
        );

        let textable_wrapper = TextableWrapper::new(
            framable.clone(),
            self.text_color,
            self.text_ratio,
            TextAlignment::Center,
        );

        if let Some(text) = self.text {
            textable_wrapper.set_text(&text, false)?;
        }

        Ok(Arc::new(Icon {
            framable,
            background,
            iconizable_wrapper,
            textable_wrapper,

            visible: AtomicBool::new(false),
        }))
    }
}

pub struct Icon {
    framable: Arc<Framable>,
    background: RwLock<Option<FillType>>,
    iconizable_wrapper: IconizableWrapper,
    textable_wrapper: TextableWrapper,

    visible: AtomicBool,
}

impl Icon {
    pub fn builder() -> IconBuilder {
        IconBuilder {
            content: None,
            background: None,
            margin: 0,

            text: None,
            text_color: Color::Black,
            text_ratio: 0.7,
        }
    }

    pub fn clear_icon(&self) -> Result<()> {
        self.iconizable_wrapper.clear_icon(self.visible())
    }

    pub fn set_icon(&self, icon: &Arc<Image>) -> Result<()> {
        self.iconizable_wrapper.set_icon(icon, self.visible())
    }

    pub fn set_margin(&self, margin: u32) -> Result<()> {
        self.iconizable_wrapper.set_margin(margin)
    }

    pub fn set_background(&self, background: impl Into<FillTypeInfo>) -> Result<()> {
        let background = FillType::new(self.framable.clone(), background.into())?;

        if self.framable.is_framed() {
            background.update_frame()?;
        }

        if self.visible() {
            background.enable()?;
        }

        *self.background.write().unwrap() = Some(background);

        Ok(())
    }

    pub fn clear_background(&self) {
        *self.background.write().unwrap() = None;
    }

    pub fn set_text(&self, text: impl ToString) -> Result<()> {
        self.textable_wrapper.set_text(text, self.visible())
    }

    pub fn clear_text(&self) -> Result<()> {
        self.textable_wrapper.set_text("", self.visible())
    }

    pub fn set_text_color(&self, color: Color) -> Result<()> {
        self.textable_wrapper.set_text_color(color)
    }

    pub fn try_from(icon_info: &IconInfo, gui_handler: &Arc<GuiHandler>) -> Result<Arc<Self>> {
        let mut icon_builder = Icon::builder().set_text_color(icon_info.text_color);

        if let Some(text_ratio) = icon_info.text_ratio {
            icon_builder = icon_builder.set_text_ratio(text_ratio);
        }

        if let Some(margin) = icon_info.margin {
            icon_builder = icon_builder.set_margin(margin);
        }

        if let Some(icon) = &icon_info.icon {
            icon_builder = icon_builder.set_content(icon.clone());
        }

        if let Some(fill_type_info) = icon_info.background_type.clone() {
            icon_builder = icon_builder.set_background(fill_type_info);
        }

        {
            let t = icon_info.text.read().unwrap();
            let text = t.as_str();

            if !text.is_empty() {
                icon_builder = icon_builder.set_text(text);
            }
        }

        icon_builder.build(gui_handler.clone())
    }

    pub fn icon(&self) -> Result<Option<Arc<Image>>> {
        self.iconizable_wrapper.icon()
    }

    pub fn extent(&self) -> (i32, i32) {
        (
            self.framable.right() - self.framable.left(),
            self.framable.bottom() - self.framable.top(),
        )
    }

    fn disable_base(&self) -> Result<()> {
        self.framable.delete()?;

        self.iconizable_wrapper.disable()?;
        self.textable_wrapper.disable()?;

        if let Some(background) = self.background.read().unwrap().as_ref() {
            background.disable()?;
        }

        Ok(())
    }
}

impl GuiElementTraits for Icon {
    fn gridable(&self) -> Option<&dyn Gridable> {
        Some(self)
    }

    fn visibility(&self) -> Option<&dyn Visibility> {
        Some(self)
    }

    fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
        Some(GuiElement::Icon(self))
    }
}

impl Visibility for Icon {
    fn visible(&self) -> bool {
        self.visible.load(SeqCst)
    }

    fn set_visibility(&self, visibility: bool) -> Result<()> {
        if visibility != self.visible.load(SeqCst) {
            self.visible.store(visibility, SeqCst);

            if visibility {
                self.framable.add()?;

                self.iconizable_wrapper.enable()?;
                self.textable_wrapper.enable()?;

                if let Some(background) = self.background.read().unwrap().as_ref() {
                    background.enable()?;
                }
            } else {
                self.disable_base()?;
            }
        }

        Ok(())
    }
}

impl Gridable for Icon {
    fn set_frame(
        &self,
        x: i32,
        y: i32,
        w: u32,
        h: u32,
        vert_align: VerticalAlign,
        hori_align: HorizontalAlign,
    ) -> Result<()> {
        self.framable.set_frame(x, y, w, h, vert_align, hori_align);

        self.iconizable_wrapper.update_frame()?;
        self.textable_wrapper.update()?;

        if let Some(background) = self.background.read().unwrap().as_ref() {
            background.update_frame()?;
        }

        Ok(())
    }

    fn selectable(&self) -> Option<&Arc<Selectable>> {
        None
    }

    fn type_name(&self) -> &str {
        "Icon"
    }

    fn set_layer(&self, layer: i32) -> Result<()> {
        self.framable.set_ui_layer(layer);
        self.iconizable_wrapper.set_ui_layer(layer)?;
        self.textable_wrapper.set_ui_layer(layer)?;

        if let Some(background) = self.background.read().unwrap().as_ref() {
            background.set_ui_layer(layer);
        }

        Ok(())
    }
}

impl Drop for Icon {
    fn drop(&mut self) {
        if self.visible.load(SeqCst) {
            self.disable_base().unwrap();
        }
    }
}