use {UiAttr, UiCell, UiCol, UiElem, UiFixSize, UiParam, UiPos, UiSize, UiSizeVal};

pub struct Horizontal {
    elements: Vec<UiCell<UiElem>>,
    background_color: ::sdl2::pixels::Color,
    size: UiSize,
    fix_size: UiFixSize,
}

impl Horizontal {
    pub fn new() -> Self {
        Horizontal {
            elements: vec![],
            background_color: ::sdl2::pixels::Color::RGBA(0, 0, 0, 0),
            size: UiSize::new(),
            fix_size: UiFixSize::new(),
        }
    }

    pub fn add_child(&mut self, child: UiCell<UiElem>) {
        self.elements.push(child);
        self.calculate_children_size();
    }

    fn calculate_children_size(&mut self) {
        let mut num_of_elements_rel: u32 = 0;

        let mut sum_of_elements_px: u32 = 0;
        let mut sum_of_elements_rel: u32 = 0;

        let mut elem_queue = vec![];

        for elem in &self.elements {
            let mut borrowed_element = elem.write().unwrap();
            let elem_size = borrowed_element.get_size().x;

            match elem_size {
                UiSizeVal::Max => {
                    elem_queue.push(elem.clone());
                    num_of_elements_rel += 1;
                    sum_of_elements_rel += 1;
                }
                UiSizeVal::Min => (), // TODO: Implement me
                UiSizeVal::Percent(val) => {
                    elem_queue.push(elem.clone());
                    num_of_elements_rel += 1;
                    sum_of_elements_rel += val as u32;
                }
                UiSizeVal::Px(val) => {
                    borrowed_element.set_fix_size(UiFixSize {
                        x: val,
                        y: self.fix_size.y,
                    });
                    sum_of_elements_px += val;
                }
            }
        }

        let size_left_for_relatives = if self.fix_size.x <= sum_of_elements_px {
            0
        } else {
            self.fix_size.x - sum_of_elements_px
        };

        if num_of_elements_rel == 0 {
            self.fix_size.x = sum_of_elements_px;
            return;
        }

        if size_left_for_relatives == 0 {
            return;
        }

        let rel_multiplier: f32 = size_left_for_relatives as f32 / sum_of_elements_rel as f32;

        for elem_iter in elem_queue {
            let mut elem = elem_iter.write().unwrap();
            let elem_size = elem.get_size().x;
            match elem_size {
                UiSizeVal::Max => elem.set_fix_size(UiFixSize {
                    x: rel_multiplier as u32,
                    y: self.fix_size.y,
                }),
                UiSizeVal::Percent(val) => elem.set_fix_size(UiFixSize {
                    x: (val as f32 * rel_multiplier) as u32,
                    y: self.fix_size.y,
                }),
                _ => (),
            }
        }
    }

    pub fn set_background_color(&mut self, color: UiCol) {
        self.background_color = color.sdl2();
    }
}

impl UiElem for Horizontal {
    fn draw(&self, canvas: &mut ::sdl2::render::Canvas<::sdl2::video::Window>, cv_pos: &UiPos) {
        ::util::draw_rect(canvas, cv_pos, &self.fix_size, self.background_color);

        let mut count_elements_size = 0;
        for elem_iter in &self.elements {
            let elem = elem_iter.read().unwrap();
            let elem_size = elem.get_fix_size().x;
            if count_elements_size + elem_size > self.fix_size.x {
                break;
            }

            elem.draw(
                canvas,
                &UiPos {
                    x: cv_pos.x + count_elements_size as i32,
                    y: cv_pos.y,
                },
            );

            count_elements_size += elem_size;
        }
    }

    fn set_attribute(&mut self, attr: UiAttr) {
        match attr {
            UiAttr::BackgroundColor(val) => self.set_background_color(val),
            UiAttr::FixSize(val) => self.set_fix_size(val),
            UiAttr::Size(val) => self.set_size(val),
            _ => (),
        }
    }

    fn set_value(&mut self, param: UiParam) {
        match param {
            UiParam::Attr(val) => self.set_attribute(val),
            UiParam::Child(val) => self.add_child(val),
        }
    }

    fn set_size(&mut self, size: UiSize) {
        self.size = size;
    }

    fn get_size(&self) -> UiSize {
        self.size.clone()
    }

    fn set_fix_size(&mut self, size: UiFixSize) {
        self.fix_size = size;
        if self.elements.len() == 0 {
            self.fix_size.x = 0;
        } else {
            self.calculate_children_size();
        }
    }

    fn get_fix_size(&self) -> UiFixSize {
        self.fix_size.clone()
    }
}
