// SPDX-License-Identifier: GPL-3.0-or-later
use iced::{
    alignment::{Horizontal, Vertical},
    widget::{Text, Button, Row, Column, Rule, Checkbox, Container, TextInput},
    Element,
};
use crate::{tree::*, Message, App};

macro_rules! apply {
    ($elem:expr, $attr:ident) => {
        if let Some(x) = $attr {
            $elem = $elem.$attr(x.into());
        }
    };
    ($elem:expr, *$attr:ident) => {
        if let Some(x) = *$attr {
            $elem = $elem.$attr(x.into());
        }
    };
    ($elem:expr, $attr:expr, $func:ident) => {
        if let Some(x) = $attr {
            $elem = $elem.$func(x.into());
        }
    };
}

macro_rules! apply_padding {
    ($elem:expr, $pad:expr) => {
        if let &Some(p) = $pad {
            let p: iced::Padding = p.into();
            $elem = $elem.padding(p);
        }
    };
}

impl Component {
    pub(crate) fn build(&self, app: &App) -> Element<'_, Message> {
        match self {
            Self::Text { text, font_size, height, halign, valign } => {
                let mut e = Text::new(text);
                apply!(e, *font_size, size);
                apply!(e, *height);
                apply!(e, *halign, horizontal_alignment);
                apply!(e, *valign, vertical_alignment);
                e.into()
            }
            Self::Button { label, name, width, height, padding } => {
                let mut e = Button::new(label.build(app));
                let name = name.clone().map(|name| Message::Button(name));
                apply!(e, name, on_press);
                apply!(e, *width);
                apply!(e, *height);
                apply_padding!(e, padding);
                e.into()
            },
            Self::Row { elements, width, height, align, spacing, padding } => {
                let mut e = Row::new();
                for sub in elements {
                    e = e.push(sub.build(app));
                }
                apply!(e, *width);
                apply!(e, *height);
                apply!(e, *align, align_items);
                apply!(e, *spacing);
                apply_padding!(e, padding);
                e.into()
            },
            Self::Column { elements, width, height, align, spacing, padding, max_width } => {
                let mut e = Column::new();
                for sub in elements {
                    e = e.push(sub.build(app));
                }
                apply!(e, *width);
                apply!(e, *height);
                apply!(e, *align, align_items);
                apply!(e, *spacing);
                apply_padding!(e, padding);
                apply!(e, *max_width);
                e.into()
            },
            &Self::VerticalRule { width } => Rule::vertical(width).into(),
            &Self::HorizontalRule { height } => Rule::horizontal(height).into(),
            Self::Checkbox { label, initial_state, name, width, spacing, text_size, override_state } => {
                let state = override_state.unwrap_or_else(|| app.get_box(name.clone(), *initial_state));
                let mut e = Checkbox::new(label, state, |s| Message::Checkbox(name.clone(), s));
                apply!(e, *width);
                apply!(e, *spacing);
                apply!(e, *text_size);
                e.into()
            },
            Self::Container { content, padding, width, height, max_width, max_height, align_x, align_y } => {
                let mut e = Container::new(content.build(app));
                apply_padding!(e, padding);
                apply!(e, *width);
                apply!(e, *height);
                apply!(e, *max_width);
                apply!(e, *max_height);
                apply!(e, *align_x);
                apply!(e, *align_y);
                e.into()
            },
            Self::TextInput { name, initial_value, placeholder, subtype, width, padding, override_value } => {
                let text = override_value.as_ref().unwrap_or_else(|| app.get_text(name.clone(), initial_value));
                let mut e = TextInput::new(&placeholder, &text, |t| Message::TextInputChange(name.clone(), t));
                e = e.on_submit(Message::TextInputSubmit(name.clone(), text.clone()));
                match subtype {
                    TextInputKind::Normal => {},
                    TextInputKind::Password => e = e.password(),
                }
                apply!(e, *width);
                apply_padding!(e, padding);
                e.into()
            },
        }
    }
}

impl Label {
    pub(crate) fn build(&self, app: &App) -> Element<'_, Message> {
        match self {
            Self::String(s)	=> Text::new(s).into(),
            Self::Component(c)	=> c.build(app),
        }
    }
}


impl Into<iced::Length> for Length {
    fn into(self) -> iced::Length {
        match self {
            Self::Fill				=> iced::Length::Fill,
            Self::Shrink			=> iced::Length::Shrink,
            Self::FillPortion(p)	=> iced::Length::FillPortion(p),
            Self::Units(u)			=> iced::Length::Units(u),
        }
    }
}

impl Into<iced::Alignment> for Alignment {
    fn into(self) -> iced::Alignment {
        match self {
            Self::Start	=> iced::Alignment::Start,
            Self::End		=> iced::Alignment::End,
            Self::Center	=> iced::Alignment::Center,
            Self::Fill		=> iced::Alignment::Fill,
        }
    }
}

impl Into<Horizontal> for HorizontalAlignment {
    fn into(self) -> Horizontal {
        match self {
            Self::Left		=> Horizontal::Left,
            Self::Center	=> Horizontal::Center,
            Self::Right	=> Horizontal::Right,
        }
    }
}

impl Into<Vertical> for VerticalAlignment {
    fn into(self) -> Vertical {
        match self {
            Self::Top		=> Vertical::Top,
            Self::Center	=> Vertical::Center,
            Self::Bottom	=> Vertical::Bottom,
        }
    }
}

impl Into<iced::Padding> for Padding {
    fn into(self) -> iced::Padding {
        match self {
            Self::Simple(p) => p.into(),
            Self::Full { top, bottom, left, right } => {
                iced::Padding { top, bottom, left, right }
            }
        }
    }
}
