use image::RgbaImage;
use palette::{Srgba, blend::Compose};

use crate::{Rect, Vec2, color};

pub struct View<'a> {
    area: Rect,
    buffer: &'a mut RgbaImage,
}

impl<'a> View<'a> {
    pub fn new(buffer: &'a mut RgbaImage) -> Self {
        let size = Vec2::from_u32(buffer.width(), buffer.height());
        let area = Rect::from_nw(Vec2::ZERO, size);
        Self { area, buffer }
    }

    pub fn dup(&mut self) -> View<'_> {
        View {
            area: self.area,
            buffer: self.buffer,
        }
    }

    pub fn zoom(mut self, area: Rect) -> Self {
        self.area = area + self.area.corner_nw();
        self
    }

    pub fn size(&self) -> Vec2 {
        self.area.size()
    }

    pub fn area(&self) -> Rect {
        Rect::from_nw(Vec2::ZERO, self.size())
    }

    fn pos_to_buffer_pos(&self, pos: Vec2) -> Vec2 {
        pos + self.area.corner_nw()
    }

    pub fn get(&self, pos: Vec2) -> Option<Srgba> {
        let (x, y) = self.pos_to_buffer_pos(pos).to_u32_checked()?;
        let pixel = self.buffer.get_pixel_checked(x, y)?;
        Some(color::from_image_color(*pixel))
    }

    pub fn set(&mut self, pos: Vec2, color: Srgba) {
        let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else {
            return;
        };

        let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else {
            return;
        };

        let below = color::from_image_color(*pixel);
        *pixel = color::to_image_color(color.atop(below));
    }

    pub fn replace(&mut self, pos: Vec2, color: Srgba) {
        let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else {
            return;
        };

        let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else {
            return;
        };

        *pixel = color::to_image_color(color);
    }

    // More complicated drawing primitives

    pub fn rect(&mut self, area: Rect, color: Srgba) {
        for pos in area.points() {
            self.set(pos, color);
        }
    }

    pub fn image(&mut self, image: &RgbaImage) {
        for y in 0..image.height() {
            for x in 0..image.width() {
                let color = color::from_image_color(*image.get_pixel(x, y));
                self.set(Vec2::from_u32(x, y), color);
            }
        }
    }
}
