use std::ops::Mul;
use freya_common::{
    CachedParagraph,
    CursorLayoutResponse,
};
use freya_engine::prelude::*;
use freya_native_core::{
    prelude::{
        ElementNode,
        NodeType,
    },
    real_dom::NodeImmutable,
    tags::TagName,
};
use freya_node_state::{
    CursorState,
    FontStyleState,
};
use torin::{
    geometry::Area,
    prelude::{
        AreaModel,
        CursorPoint,
        LayoutNode,
        Length,
        Size2D,
    },
};
use super::utils::ElementUtils;
use crate::{
    dom::DioxusNode,
    prelude::TextGroupMeasurement,
    render::{
        align_main_align_paragraph,
        create_paragraph,
        draw_cursor,
        draw_cursor_highlights,
        ParagraphData,
    },
};
pub struct ParagraphElement;
impl ParagraphElement {
    pub fn measure_paragraph(
        node: &DioxusNode,
        layout_node: &LayoutNode,
        text_measurement: &TextGroupMeasurement,
        scale_factor: f64,
    ) {
        let paragraph = &layout_node
            .data
            .as_ref()
            .unwrap()
            .get::<CachedParagraph>()
            .unwrap()
            .0;
        let cursor_state = node.get::<CursorState>().unwrap();
        if cursor_state.cursor_id != Some(text_measurement.cursor_id) {
            return;
        }
        let y = align_main_align_paragraph(node, &layout_node.area, paragraph);
        if let Some(cursor_reference) = &cursor_state.cursor_ref {
            if let Some(cursor_position) = text_measurement.cursor_position {
                let position = CursorPoint::new(cursor_position.x, cursor_position.y - y as f64);
                let char_position = paragraph.get_glyph_position_at_coordinate(
                    position.mul(scale_factor).to_i32().to_tuple(),
                );
                cursor_reference
                    .cursor_sender
                    .send(CursorLayoutResponse::CursorPosition {
                        position: char_position.position as usize,
                        id: text_measurement.cursor_id,
                    })
                    .ok();
            }
            if let Some((origin, dist)) = text_measurement.cursor_selection {
                let origin_position = CursorPoint::new(origin.x, origin.y - y as f64);
                let dist_position = CursorPoint::new(dist.x, dist.y - y as f64);
                let origin_char = paragraph.get_glyph_position_at_coordinate(
                    origin_position.mul(scale_factor).to_i32().to_tuple(),
                );
                let dist_char = paragraph.get_glyph_position_at_coordinate(
                    dist_position.mul(scale_factor).to_i32().to_tuple(),
                );
                cursor_reference
                    .cursor_sender
                    .send(CursorLayoutResponse::TextSelection {
                        from: origin_char.position as usize,
                        to: dist_char.position as usize,
                        id: text_measurement.cursor_id,
                    })
                    .ok();
            }
        }
    }
}
impl ElementUtils for ParagraphElement {
    fn render(
        self,
        layout_node: &torin::prelude::LayoutNode,
        node_ref: &DioxusNode,
        canvas: &Canvas,
        font_collection: &mut FontCollection,
        _font_manager: &FontMgr,
        default_fonts: &[String],
        scale_factor: f32,
    ) {
        let area = layout_node.visible_area();
        let node_cursor_state = &*node_ref.get::<CursorState>().unwrap();
        let paint = |paragraph: &Paragraph| {
            let x = area.min_x();
            let y = area.min_y() + align_main_align_paragraph(node_ref, &area, paragraph);
            draw_cursor_highlights(&area, paragraph, canvas, node_ref);
            draw_cursor(&area, paragraph, canvas, node_ref);
            paragraph.paint(canvas, (x, y));
        };
        if node_cursor_state.position.is_some() {
            let ParagraphData { paragraph, .. } = create_paragraph(
                node_ref,
                &area.size,
                font_collection,
                true,
                default_fonts,
                scale_factor,
            );
            paint(¶graph);
        } else {
            let paragraph = &layout_node
                .data
                .as_ref()
                .unwrap()
                .get::<CachedParagraph>()
                .unwrap()
                .0;
            paint(paragraph);
        };
    }
    fn element_needs_cached_area(&self, node_ref: &DioxusNode) -> bool {
        for text_span in node_ref.children() {
            if let NodeType::Element(ElementNode {
                tag: TagName::Text, ..
            }) = &*text_span.node_type()
            {
                let font_style = text_span.get::<FontStyleState>().unwrap();
                if !font_style.text_shadows.is_empty() {
                    return true;
                }
            }
        }
        false
    }
    fn element_drawing_area(
        &self,
        layout_node: &LayoutNode,
        node_ref: &DioxusNode,
        scale_factor: f32,
    ) -> Area {
        let paragraph_font_height = &layout_node
            .data
            .as_ref()
            .unwrap()
            .get::<CachedParagraph>()
            .unwrap()
            .1;
        let mut area = layout_node.visible_area();
        area.size.height = area.size.height.max(*paragraph_font_height);
        for text_span in node_ref.children() {
            if let NodeType::Element(ElementNode {
                tag: TagName::Text, ..
            }) = &*text_span.node_type()
            {
                let font_style = text_span.get::<FontStyleState>().unwrap();
                let mut text_shadow_area = area;
                for text_shadow in &font_style.text_shadows {
                    if text_shadow.color != Color::TRANSPARENT {
                        text_shadow_area.move_with_offsets(
                            &Length::new(text_shadow.offset.x),
                            &Length::new(text_shadow.offset.y),
                        );
                        let expanded_size = text_shadow.blur_sigma.ceil() as f32 * scale_factor;
                        text_shadow_area.expand(&Size2D::new(expanded_size, expanded_size))
                    }
                }
                area = area.union(&text_shadow_area);
            }
        }
        area
    }
}