use image::RgbaImage;

pub struct Square {
    xoffset: u32,
    yoffset: u32,
    level: u32,
    coordinates: Vec<(u32, u32)>,
    light_color: [u8; 4],
    dark_color: [u8; 4],
}

impl Square {
    pub fn new(
        xoffset: u32,
        yoffset: u32,
        level: u32,
        light_color: [u8; 4],
        dark_color: [u8; 4],
    ) -> Self {
        let coordinates = Self::calculated_coordinates(xoffset, yoffset, level);
        Self {
            xoffset,
            yoffset,
            level,
            coordinates,
            light_color,
            dark_color,
        }
    }

    pub fn colorize(&self, image: RgbaImage) -> RgbaImage {
        let base_image = self.write_base(image);
        let colorized_image = self.write_filling(base_image);
        colorized_image
    }

    fn write_base(&self, mut image: RgbaImage) -> RgbaImage {
        let base_color = image::Rgba(self.base_color());
        for x in 0..5 {
            for y in 0..5 {
                image.put_pixel(self.xoffset + x, self.yoffset + y, base_color);
            }
        }
        image
    }

    fn write_filling(&self, mut image: RgbaImage) -> RgbaImage {
        let filling_color = image::Rgba(self.filling_color());
        for coordinates in self.coordinates.iter() {
            image.put_pixel(coordinates.0, coordinates.1, filling_color);
        }
        image
    }

    fn base_color(&self) -> [u8; 4] {
        if self.level < 6 {
            self.dark_color
        } else {
            self.light_color
        }
    }

    fn filling_color(&self) -> [u8; 4] {
        if self.level < 6 {
            self.light_color
        } else {
            self.dark_color
        }
    }

    fn calculated_coordinates(xoffset: u32, yoffset: u32, level: u32) -> Vec<(u32, u32)> {
        match level {
            1 => Self::level_one_coordinates(xoffset, yoffset),
            2 => Self::level_two_coordinates(xoffset, yoffset),
            3 => Self::level_three_coordinates(xoffset, yoffset),
            4 => Self::level_four_coordinates(xoffset, yoffset),
            5 => Self::level_five_coordinates(xoffset, yoffset),
            6 => Self::level_six_coordinates(xoffset, yoffset),
            7 => Self::level_seven_coordinates(xoffset, yoffset),
            8 => Self::level_height_coordinates(xoffset, yoffset),
            _ => vec![],
        }
    }

    fn level_height_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![(xoffset + 2, yoffset + 2)]
    }

    fn level_seven_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset + 2),
            (xoffset + 2, yoffset),
            (xoffset + 2, yoffset + 4),
            (xoffset + 4, yoffset + 2),
        ]
    }

    fn level_six_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset),
            (xoffset, yoffset + 2),
            (xoffset, yoffset + 4),
            (xoffset + 2, yoffset),
            (xoffset + 2, yoffset + 2),
            (xoffset + 2, yoffset + 4),
            (xoffset + 4, yoffset),
            (xoffset + 4, yoffset + 2),
            (xoffset + 4, yoffset + 4),
        ]
    }

    fn level_five_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset),
            (xoffset, yoffset + 2),
            (xoffset, yoffset + 4),
            (xoffset + 1, yoffset + 1),
            (xoffset + 2, yoffset),
            (xoffset + 2, yoffset + 2),
            (xoffset + 2, yoffset + 4),
            (xoffset + 3, yoffset + 3),
            (xoffset + 4, yoffset),
            (xoffset + 4, yoffset + 2),
            (xoffset + 4, yoffset + 4),
        ]
    }

    fn level_four_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset + 1),
            (xoffset, yoffset + 3),
            (xoffset + 1, yoffset),
            (xoffset + 1, yoffset + 2),
            (xoffset + 1, yoffset + 4),
            (xoffset + 2, yoffset + 1),
            (xoffset + 2, yoffset + 3),
            (xoffset + 3, yoffset),
            (xoffset + 3, yoffset + 2),
            (xoffset + 3, yoffset + 4),
            (xoffset + 4, yoffset + 1),
            (xoffset + 4, yoffset + 3),
        ]
    }

    fn level_three_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset),
            (xoffset, yoffset + 4),
            (xoffset + 1, yoffset + 1),
            (xoffset + 1, yoffset + 3),
            (xoffset + 2, yoffset + 2),
            (xoffset + 3, yoffset + 1),
            (xoffset + 3, yoffset + 3),
            (xoffset + 4, yoffset),
            (xoffset + 4, yoffset + 4),
        ]
    }

    fn level_two_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset + 1),
            (xoffset + 2, yoffset + 1),
            (xoffset + 4, yoffset + 1),
            (xoffset, yoffset + 3),
            (xoffset + 2, yoffset + 3),
            (xoffset + 4, yoffset + 3),
        ]
    }

    fn level_one_coordinates(xoffset: u32, yoffset: u32) -> Vec<(u32, u32)> {
        vec![
            (xoffset, yoffset + 2),
            (xoffset + 2, yoffset),
            (xoffset + 2, yoffset + 4),
            (xoffset + 4, yoffset + 2),
        ]
    }
}
