use freya_native_core::{
    prelude::NodeType,
    real_dom::NodeImmutable,
    tree::TreeRef,
    NodeId,
};
use freya_node_state::LayoutState;
use rustc_hash::FxHashMap;
use torin::prelude::*;
use crate::dom::DioxusDOM;
pub struct DioxusDOMAdapter<'a> {
    pub rdom: &'a DioxusDOM,
    pub scale_factor: f32,
    cache: FxHashMap<NodeId, bool>,
}
impl<'a> DioxusDOMAdapter<'a> {
    pub fn new(rdom: &'a DioxusDOM, scale_factor: f32) -> Self {
        Self {
            rdom,
            scale_factor,
            cache: FxHashMap::default(),
        }
    }
}
impl DOMAdapter<NodeId> for DioxusDOMAdapter<'_> {
    fn get_node(&self, node_id: &NodeId) -> Option<Node> {
        let node = self.rdom.get(*node_id)?;
        let contains_text = node
            .node_type()
            .tag()
            .map(|t| t.contains_text())
            .unwrap_or_default();
        let mut layout = node.get::<LayoutState>()?.clone();
        if *node_id == self.rdom.root_id() {
            layout.width = Size::Percentage(Length::new(100.0));
            layout.height = Size::Percentage(Length::new(100.0));
        }
        let mut node = Node {
            width: layout.width,
            height: layout.height,
            minimum_width: layout.minimum_width,
            minimum_height: layout.minimum_height,
            maximum_width: layout.maximum_width,
            maximum_height: layout.maximum_height,
            direction: layout.direction,
            padding: layout.padding,
            margin: layout.margin,
            main_alignment: layout.main_alignment,
            cross_alignment: layout.cross_alignment,
            offset_x: layout.offset_x,
            offset_y: layout.offset_y,
            has_layout_references: layout.node_ref.is_some(),
            position: layout.position,
            content: layout.content,
            contains_text,
            spacing: layout.spacing,
        };
        node.scale(self.scale_factor);
        Some(node)
    }
    fn height(&self, node_id: &NodeId) -> Option<u16> {
        self.rdom.tree_ref().height(*node_id)
    }
    fn parent_of(&self, node_id: &NodeId) -> Option<NodeId> {
        self.rdom.tree_ref().parent_id(*node_id)
    }
    fn children_of(&mut self, node_id: &NodeId) -> Vec<NodeId> {
        let mut children = self.rdom.tree_ref().children_ids(*node_id);
        children.retain(|id| is_node_valid(self.rdom, &mut self.cache, id));
        children
    }
    fn is_node_valid(&mut self, node_id: &NodeId) -> bool {
        is_node_valid(self.rdom, &mut self.cache, node_id)
    }
    fn root_id(&self) -> NodeId {
        self.rdom.root_id()
    }
}
fn is_node_valid(rdom: &DioxusDOM, cache: &mut FxHashMap<NodeId, bool>, node_id: &NodeId) -> bool {
    if let Some(is_valid) = cache.get(node_id) {
        return *is_valid;
    }
    let node = rdom.get(*node_id);
    let is_valid = 'validation: {
        if let Some(node) = node {
            let is_placeholder = matches!(*node.node_type(), NodeType::Placeholder);
            if is_placeholder {
                break 'validation false;
            }
            let tree = rdom.tree_ref();
            let mut current = *node_id;
            loop {
                let height = tree.height(current);
                if let Some(height) = height {
                    if height == 0 {
                        break;
                    }
                }
                let parent_current = tree.parent_id(current);
                if let Some(parent_current) = parent_current {
                    current = parent_current;
                }
            }
            current == rdom.root_id()
        } else {
            false
        }
    };
    cache.insert(*node_id, is_valid);
    is_valid
}