use display_interface::DisplayError;
use embassy_time::{Duration, Instant, Timer};
use embedded_graphics::{pixelcolor::BinaryColor, prelude::DrawTarget};
use figments::render::RenderSource;
use figments_render::output::OutputAsync;
use log::*;

use crate::{backoff::Backoff, graphics::ssd1306::SsdOutput, tasks::oled::{LockedUniforms, OledUiSurfacePool}};


#[embassy_executor::task]
pub async fn oled_render(mut output: SsdOutput, mut surfaces: OledUiSurfacePool, uniforms: LockedUniforms) {
    warn!("Starting OLED rendering task");
    Backoff::from_secs(1).forever().attempt::<_, (), DisplayError>(async || {
        const FPS: u64 = 30;
        const RENDER_BUDGET: Duration = Duration::from_millis(1000 / FPS);

        const ANIMATION_TPS: u64 = 30;
        const ANIMATION_FRAME_TIME: Duration = Duration::from_millis(1000 / ANIMATION_TPS);
        info!("Starting Oled display driver");
        output.init().await?;

        loop {
            let start = Instant::now();
            surfaces.commit();
            {
                let mut locked = uniforms.lock().await;
                output.clear(BinaryColor::Off).unwrap();
                locked.frame = (Instant::now().as_millis() / ANIMATION_FRAME_TIME.as_millis()) as usize;
                locked.current_screen.render_to(&mut output, &locked);
                surfaces.render_to(&mut output, &locked);
            }
            output.commit_async().await?;
            let frame_time = Instant::now() - start;
            if frame_time < RENDER_BUDGET {
                Timer::after(RENDER_BUDGET - frame_time).await;
            } else {
                //warn!("OLED Frame took too long to render! {}ms", frame_time.as_millis());
            }
        }
    }).await.unwrap();
}
