use std::rc::Rc;

use clay_layout::{
    Declaration, fit, grow,
    layout::{
        Alignment, LayoutAlignmentX, LayoutAlignmentY,
        LayoutDirection::{self, TopToBottom},
        Padding,
    },
    text::{TextConfig, TextElementConfig},
};
use libopenbirch::node::NodeEnum;

use super::{Document, State};

pub struct Line {
    pub segments: Vec<LineSegment>,
}

impl From<String> for Line {
    fn from(value: String) -> Self {
        Line {
            segments: vec![LineSegment::Text(value, 20)],
        }
    }
}

impl Line {
    pub fn draw<'a>(&'a mut self, clay: &'a clay_layout::Clay, theme: &Theme, state: &'a State) {
        for segment in self.segments.iter_mut() {
            segment.draw(clay, theme, state);
        }
    }
}

pub struct Theme {
    pub text_color: clay_layout::color::Color,
    pub text_font: u16,
    pub math_color: clay_layout::color::Color,
    pub math_font: u16,
}

pub enum LineSegment {
    Text(String, u16),
    // Link(String, Link),
    Math(String, Rc<NodeEnum>, u16),
}

impl LineSegment {
    pub fn draw<'a>(&'a mut self, clay: &'a clay_layout::Clay, theme: &'a Theme, state: &'a State) {
        match self {
            LineSegment::Text(text, font_size) => clay.text(
                text.as_str(),
                TextConfig::new()
                    .font_id(theme.text_font)
                    .font_size(*font_size)
                    .color(theme.text_color)
                    .end(),
            ),
            LineSegment::Math(_source, node_enum, font_size) => {
                draw_math_enum(node_enum.clone(), clay, theme, *font_size, state);
            }
        }
    }
}
fn draw_binary(
    symbol: &'static str,
    left: Rc<NodeEnum>,
    right: Rc<NodeEnum>,
    font_size: u16,
    clay: &clay_layout::Clay,
    theme: &Theme,
    state: &State,
) {
    clay.with(
        &Declaration::new()
            .layout()
            .direction(LayoutDirection::LeftToRight)
            .width(fit!(0.))
            .height(fit!(0.))
            .child_alignment(Alignment::new(
                LayoutAlignmentX::Center,
                LayoutAlignmentY::Center,
            ))
            .padding(Padding::new(2, 2, 3, 3))
            .end(),
        |clay| {
            // Left side
            draw_math_enum(left, clay, theme, font_size, state);
            // add symbol
            clay.text(
                symbol,
                TextConfig::new()
                    .color(theme.math_color)
                    .font_size(font_size)
                    .font_id(theme.math_font)
                    .end(),
            );
            // Right side
            draw_math_enum(right, clay, theme, font_size, state);
        },
    )
}

fn draw_math_enum<'a>(
    node: Rc<NodeEnum>,
    clay: &'a clay_layout::Clay,
    theme: &'a Theme,
    font_size: u16,
    state: &'a State,
) {
    match node.as_ref() {
        NodeEnum::Constant(constant) => {
            let str = std::mem::ManuallyDrop::new(constant.get_value().to_string());
            clay.text(
                str.as_str(),
                TextConfig::new()
                    .color(theme.math_color)
                    .font_size(font_size)
                    .font_id(theme.math_font)
                    .end(),
            )
        }
        NodeEnum::Add(add) => draw_binary(
            " + ",
            add.get_left(),
            add.get_right(),
            font_size,
            clay,
            theme,
            state,
        ),

        NodeEnum::Subtract(sub) => draw_binary(
            " - ",
            sub.get_left(),
            sub.get_right(),
            font_size,
            clay,
            theme,
            state,
        ),

        NodeEnum::Multiply(mul) => draw_binary(
            "×  ",
            mul.get_left(),
            mul.get_right(),
            font_size,
            clay,
            theme,
            state,
        ),

        NodeEnum::Assign(assign) => draw_binary(
            " := ",
            assign.get_left(),
            assign.get_right(),
            font_size,
            clay,
            theme,
            state,
        ),

        NodeEnum::Divide(divide) => clay.with(
            &Declaration::new()
                .layout()
                .direction(TopToBottom)
                .width(fit!(0.))
                .height(fit!(0.))
                .child_alignment(Alignment::new(
                    LayoutAlignmentX::Center,
                    LayoutAlignmentY::Center,
                ))
                .padding(Padding::new(4, 4, 2, 2))
                .end(),
            |clay| {
                // Left side
                draw_math_enum(divide.get_left(), clay, theme, font_size, state);
                // Seperator bar
                clay.with(
                    &Declaration::new()
                        .layout()
                        .width(grow!(0.))
                        .height(fit!(1.))
                        .end()
                        .background_color(theme.math_color),
                    |_| {},
                );
                // Right side
                draw_math_enum(divide.get_right(), clay, theme, font_size, state);
            },
        ),
        NodeEnum::Symbol(symbol) => todo!(),
        NodeEnum::Function(function) => todo!(),
        NodeEnum::Call(call) => todo!(),

        NodeEnum::Empty(empty) => {}
    }
}
