use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

use iced::widget::keyed;
use iced::{Element, Theme};

use crate::PlushieRenderer;
use crate::iced_convert;
use crate::message::Message;
use crate::protocol::TreeNode;
use crate::registry::PlushieWidget;
use crate::render_ctx::RenderCtx;
use crate::widget::helpers::*;

use plushie_core::types::{HorizontalAlignment, Length, Padding, PlushieType};

struct KeyedColumnProps {
    padding: Option<Padding>,
    width: Option<Length>,
    height: Option<Length>,
    max_width: Option<f32>,
    align_x: Option<HorizontalAlignment>,
}

impl KeyedColumnProps {
    fn from_node(node: &TreeNode) -> Self {
        let p = &node.props;
        Self {
            padding: Padding::extract(p, "padding"),
            width: Length::extract(p, "width"),
            height: Length::extract(p, "height"),
            max_width: f32::extract(p, "max_width"),
            align_x: HorizontalAlignment::extract(p, "align_x"),
        }
    }
}

pub(crate) struct KeyedColumnWidget;

impl<R: PlushieRenderer> PlushieWidget<R> for KeyedColumnWidget {
    fn type_names(&self) -> &[&str] {
        &["keyed_column"]
    }

    fn render<'a>(
        &'a self,
        node: &'a TreeNode,
        ctx: &RenderCtx<'a, R>,
    ) -> Element<'a, Message, Theme, R> {
        let kp = KeyedColumnProps::from_node(node);
        let spacing = prop_animated_f32(
            &ctx.caches.interpolated_props,
            &node.id,
            &node.props,
            "spacing",
        );
        let max_width = prop_animated_f32(
            &ctx.caches.interpolated_props,
            &node.id,
            &node.props,
            "max_width",
        )
        .or(kp.max_width);

        let keyed_children: Vec<(u64, Element<'a, Message, Theme, R>)> = node
            .children
            .iter()
            .map(|c| {
                let mut hasher = DefaultHasher::new();
                c.id.hash(&mut hasher);
                let key = hasher.finish();
                let elem = ctx.render_child(c);
                (key, elem)
            })
            .collect();

        let width = kp
            .width
            .as_ref()
            .map(iced_convert::length)
            .unwrap_or(iced::Length::Shrink);
        let height = kp
            .height
            .as_ref()
            .map(iced_convert::length)
            .unwrap_or(iced::Length::Shrink);

        let align_x = kp
            .align_x
            .map(iced_convert::horizontal_alignment)
            .map(iced::Alignment::from)
            .unwrap_or(iced::Alignment::Start);

        let mut kc = keyed::Column::with_children(keyed_children);
        kc = kc.width(width).height(height).align_items(align_x);

        if let Some(s) = spacing {
            kc = kc.spacing(s);
        }
        if let Some(ref p) = kp.padding {
            kc = kc.padding(iced_convert::padding(p));
        }
        if let Some(mw) = max_width {
            kc = kc.max_width(mw);
        }

        kc.into()
    }

    fn fresh_for_session(&self) -> Box<dyn PlushieWidget<R>> {
        Box::new(KeyedColumnWidget)
    }
}
