//! Implementation of a haku2 canvas based on tiny-skia.

use std::mem;

use tiny_skia::{
    BlendMode, Color, FillRule, LineCap, Paint, PathBuilder, Shader, Stroke, Transform,
};

use super::Chunk;

pub struct ChunkCanvas<'c> {
    chunk: &'c mut Chunk,
    transform: Transform,
    pb: PathBuilder,
}

impl<'c> ChunkCanvas<'c> {
    pub fn new(chunk: &'c mut Chunk) -> Self {
        Self {
            chunk,
            transform: Transform::identity(),
            pb: PathBuilder::new(),
        }
    }

    pub fn translated(mut self, x: f32, y: f32) -> Self {
        self.transform = self.transform.post_translate(x, y);
        self
    }
}

impl haku2::Canvas for ChunkCanvas<'_> {
    fn begin(&mut self) -> Result<(), haku2::RenderError> {
        self.pb.clear();
        Ok(())
    }

    fn line(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> Result<(), haku2::RenderError> {
        self.pb.move_to(x1, y1);
        self.pb.line_to(x2, y2);
        Ok(())
    }

    fn rectangle(
        &mut self,
        x: f32,
        y: f32,
        width: f32,
        height: f32,
    ) -> Result<(), haku2::RenderError> {
        if let Some(rect) = tiny_skia::Rect::from_xywh(x, y, width, height) {
            self.pb.push_rect(rect);
        }
        Ok(())
    }

    fn circle(&mut self, x: f32, y: f32, r: f32) -> Result<(), haku2::RenderError> {
        self.pb.push_circle(x, y, r);
        Ok(())
    }

    fn fill(&mut self, r: u8, g: u8, b: u8, a: u8) -> Result<(), haku2::RenderError> {
        let pb = mem::take(&mut self.pb);
        if let Some(path) = pb.finish() {
            let paint = Paint {
                shader: Shader::SolidColor(Color::from_rgba8(r, g, b, a)),
                ..default_paint()
            };
            self.chunk
                .pixmap
                .fill_path(&path, &paint, FillRule::EvenOdd, self.transform, None);
        }
        Ok(())
    }

    fn stroke(
        &mut self,
        r: u8,
        g: u8,
        b: u8,
        a: u8,
        thickness: f32,
    ) -> Result<(), haku2::RenderError> {
        let pb = mem::take(&mut self.pb);
        if let Some(path) = pb.finish() {
            let paint = Paint {
                shader: Shader::SolidColor(Color::from_rgba8(r, g, b, a)),
                ..default_paint()
            };
            self.chunk.pixmap.stroke_path(
                &path,
                &paint,
                &Stroke {
                    width: thickness,
                    line_cap: LineCap::Round,
                    ..Default::default()
                },
                self.transform,
                None,
            );
        }
        Ok(())
    }
}

fn default_paint() -> Paint<'static> {
    Paint {
        shader: Shader::SolidColor(Color::BLACK),
        blend_mode: BlendMode::SourceOver,
        anti_alias: false,
        force_hq_pipeline: false,
    }
}
