use iced::{Element, Length, advanced::Widget};
use std::sync::Arc;

use crate::shared_string::SharedString;

#[derive(Clone)]
pub struct BlurHash {
    hash: SharedString,
    handle: Arc<iced::advanced::image::Handle>,
    width: iced::Length,
    height: iced::Length,
    punch: f32,
}

impl core::fmt::Debug for BlurHash {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("BlurHash")
            .field("hash", &self.hash)
            .field("width", &self.width)
            .field("height", &self.height)
            .field("punch", &self.punch)
            .finish()
    }
}

impl BlurHash {
    pub fn recompute(&mut self, width: u32, height: u32, punch: f32) {
        let pixels = blurhash::decode(&self.hash, width, height, punch)
            .unwrap_or_else(|_| vec![0; (width * height * 4) as usize]);
        let handle = iced::advanced::image::Handle::from_rgba(width, height, pixels);
        self.handle = Arc::new(handle);
    }

    pub fn new(hash: impl AsRef<str>) -> Self {
        let hash = SharedString::from(hash.as_ref().to_string());
        let pixels = blurhash::decode(&hash, 32, 32, 1.0).unwrap_or_else(|_| vec![0; 32 * 32 * 4]);
        let handle = iced::advanced::image::Handle::from_rgba(32, 32, pixels);
        let handle = Arc::new(handle);
        BlurHash {
            hash,
            handle,
            width: 32.into(),
            height: 32.into(),
            punch: 1.0,
        }
    }

    pub fn width(mut self, width: impl Into<Length>) -> Self {
        self.width = width.into();
        self
    }

    pub fn height(mut self, height: impl Into<Length>) -> Self {
        self.height = height.into();
        self
    }

    pub fn punch(mut self, punch: f32) -> Self {
        self.punch = punch;
        self
    }
}

impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for BlurHash
where
    Renderer: iced::advanced::image::Renderer<Handle = iced::advanced::image::Handle>,
{
    fn size(&self) -> iced::Size<iced::Length> {
        iced::Size {
            width: self.width,
            height: self.height,
        }
    }

    fn layout(
        &mut self,
        _tree: &mut iced::advanced::widget::Tree,
        renderer: &Renderer,
        limits: &iced::advanced::layout::Limits,
    ) -> iced::advanced::layout::Node {
        let layout = iced::widget::image::layout(
            renderer,
            limits,
            &self.handle,
            self.width,
            self.height,
            None,
            iced::ContentFit::default(),
            iced::Rotation::default(),
            false,
        );
        let height = layout.bounds().height;
        let width = layout.bounds().width;
        self.recompute(width as u32, height as u32, self.punch);
        layout
    }

    fn draw(
        &self,
        _state: &iced::advanced::widget::Tree,
        renderer: &mut Renderer,
        _theme: &Theme,
        _style: &iced::advanced::renderer::Style,
        layout: iced::advanced::Layout<'_>,
        _cursor: iced::advanced::mouse::Cursor,
        _viewport: &iced::Rectangle,
    ) {
        iced::widget::image::draw(
            renderer,
            layout,
            &self.handle,
            None,
            iced::border::Radius::default(),
            iced::ContentFit::default(),
            iced::widget::image::FilterMethod::default(),
            iced::Rotation::default(),
            1.0,
            1.0,
        );
    }
}

impl<'a, Message, Theme, Renderer> From<BlurHash> for iced::Element<'a, Message, Theme, Renderer>
where
    Renderer: iced::advanced::image::Renderer<Handle = iced::advanced::image::Handle>,
{
    fn from(blur_hash: BlurHash) -> Element<'a, Message, Theme, Renderer> {
        iced::Element::new(blur_hash)
    }
}
