use super::*;
use sge_api::shapes_2d::Orientation;

#[derive(Debug)]
pub struct Flow {
    orientation: Orientation,
    children: Vec<Child>,
    gap: f32,
}

impl Flow {
    pub fn new(orientation: Orientation, children: Vec<Child>) -> UiRef {
        Self {
            orientation,
            children,
            gap: 0.0,
        }
        .to_ref()
    }

    pub fn with_gap(orientation: Orientation, gap: f32, children: Vec<Child>) -> UiRef {
        Self {
            orientation,
            children,
            gap,
        }
        .to_ref()
    }

    pub fn vertical(children: impl Into<Vec<Child>>) -> UiRef {
        Self::new(Orientation::Vertical, children.into())
    }

    pub fn vertical_with_gap(gap: f32, children: impl Into<Vec<Child>>) -> UiRef {
        Self::with_gap(Orientation::Vertical, gap, children.into())
    }

    pub fn horizontal(children: impl Into<Vec<Child>>) -> UiRef {
        Self::new(Orientation::Horizontal, children.into())
    }

    pub fn horizontal_with_gap(gap: f32, children: impl Into<Vec<Child>>) -> UiRef {
        Self::with_gap(Orientation::Horizontal, gap, children.into())
    }

    fn layout(&self, area: Area) -> Vec<Area> {
        let mut max_cross = 0.0;
        let mut main_offset = 0.0;
        let mut cross_offset = 0.0;
        let mut areas = Vec::new();

        for child in &self.children {
            let child_area = Area {
                top_left: area.top_left + self.orientation.create_vec2(main_offset, cross_offset),
                size: area.size - self.orientation.create_vec2(main_offset, cross_offset),
            };
            let size = child.node.size(child_area);
            let main_axis_size = self.orientation.main(size);
            let main_axis_area = self.orientation.main(area.size);
            let remaining_space = main_axis_area - main_offset;

            if remaining_space < main_axis_size {
                cross_offset += max_cross + self.gap;
                main_offset = 0.0;
            }

            let child_area = Area {
                top_left: area.top_left + self.orientation.create_vec2(main_offset, cross_offset),
                size: self
                    .orientation
                    .create_vec2(main_axis_size, self.orientation.cross(size)),
            };

            main_offset += main_axis_size + self.gap;
            max_cross = max_cross.max(self.orientation.cross(size));
            areas.push(child_area);
        }

        areas
    }
}

impl UiNode for Flow {
    fn draw(&self, area: Area, ui: &UiState) -> Vec2 {
        let areas = self.layout(area);
        for (child, area) in self.children.iter().zip(&areas) {
            child.node.draw(*area, ui);
        }
        areas
            .into_iter()
            .reduce(|a, b| a.merge(b))
            .unwrap_or(Area::ZERO)
            .size
    }

    fn preferred_dimensions(&self) -> Vec2 {
        let mut main_axis_size = 0.0;
        let mut max_cross = 0.0f32;

        for child in &self.children {
            let preferred_dimensions = child.node.preferred_dimensions();
            main_axis_size += self.orientation.main(preferred_dimensions) + self.gap;
            max_cross = max_cross.max(self.orientation.cross(preferred_dimensions));
        }

        if !self.children.is_empty() {
            main_axis_size -= self.gap;
        }

        self.orientation.create_vec2(main_axis_size, max_cross)
    }

    fn size(&self, area: Area) -> Vec2 {
        self.layout(area)
            .into_iter()
            .reduce(|a, b| a.merge(b))
            .unwrap_or(Area::ZERO)
            .size
    }
}
