yuno 0.1.1

A declarative UI layout and rendering framework powered by Skia.
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;

#[allow(deprecated)]
use skia_safe::{Canvas, Color, Paint, PaintStyle, PathBuilder, Point, TileMode, gradient_shader};

use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
#[allow(deprecated)]
use skia_safe::gradient_shader::GradientShaderColors;

pub struct LineChart {
    pub history: RefCell<VecDeque<f32>>,
    colors: [Color; 2],
    planner: RefCell<Option<Rc<dyn Layout>>>,
}

impl HasPlanner for LineChart {
    fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
        &self.planner
    }
    fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
        *self.planner.borrow_mut() = p;
    }
}

impl Drawable for LineChart {
    fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
        let area = self.get_planned_drawing_area(canvas);
        let history = self.history.borrow();
        if history.is_empty() {
            return Ok(());
        }

        let mut line_builder = PathBuilder::new();
        let mut fill_builder = PathBuilder::new();

        let w = area.width();
        let h = area.height();
        let step_x = w / (history.len().saturating_sub(1).max(1) as f32);

        fill_builder.move_to(Point::new(area.left, area.bottom));

        for (i, &val) in history.iter().enumerate() {
            let px = area.left + (i as f32) * step_x;
            // Map percentage (0-100) to height, anchoring at bottom
            let py = area.bottom - ((val / 100.0) * h).clamp(0.0, h);
            let pt = Point::new(px, py);

            if i == 0 {
                line_builder.move_to(pt);
            } else {
                line_builder.line_to(pt);
            }
            fill_builder.line_to(pt);
        }
        fill_builder.line_to(Point::new(area.right, area.bottom));
        fill_builder.close();

        #[allow(deprecated)]
        let shader = gradient_shader::linear(
            (
                Point::new(area.left, area.top),
                Point::new(area.right, area.top),
            ),
            GradientShaderColors::Colors(&self.colors),
            None,
            TileMode::Clamp,
            None,
            None,
        );

        let mut fill_paint = Paint::default();
        fill_paint.set_anti_alias(true).set_alpha_f(0.25);
        if let Some(s) = &shader {
            fill_paint.set_shader(s.clone());
        }

        let mut stroke_paint = Paint::default();
        stroke_paint
            .set_anti_alias(true)
            .set_style(PaintStyle::Stroke)
            .set_stroke_width(2.0);
        if let Some(s) = &shader {
            stroke_paint.set_shader(s.clone());
        }

        canvas.draw_path(&fill_builder.detach(), &fill_paint);
        canvas.draw_path(&line_builder.detach(), &stroke_paint);
        Ok(())
    }

    fn get_horizontal_plan(&self) -> Plan {
        Plan::Fill
    }
    fn get_vertical_plan(&self) -> Plan {
        Plan::Fill
    }
}

impl LineChart {
    pub fn new(history: VecDeque<f32>, colors: [Color; 2]) -> Rc<Self> {
        Rc::new(Self {
            history: RefCell::new(history),
            colors,
            planner: RefCell::new(None),
        })
    }
}