use embassy_sync::pubsub::DynSubscriber;
use embassy_time::Duration;
use figments::{liber8tion::interpolate::Fract8, prelude::*};
use figments_render::output::Brightness;
use rgb::Rgba;
use core::fmt::Debug;
use log::*;

use crate::{animation::{AnimDisplay, AnimatedSurface, Animation}, events::{Personality, Prediction}, graphics::{display::{DisplayControls, SegmentSpace, Uniforms}, shaders::*}, tasks::ui::UiSurfacePool};

#[derive(Debug)]
pub struct SafetyUi<S: Surface> {
    // Headlight and brakelight layers can only be overpainted by the bootsplash overlay layer
    headlight: AnimatedSurface<S>,
    brakelight: AnimatedSurface<S>,

    // The overlay covers everything and is used to implement a power-on and power-off animation.
    overlay: AnimatedSurface<S>,
    display: DisplayControls
}

impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pixel = Rgba<u8>>> SafetyUi<S> {
    pub fn new<SS: Surfaces<Surface = S>>(surfaces: &mut SS, display: DisplayControls) -> Self where SS::Error: Debug {
        let ret = Self {
            overlay: SurfaceBuilder::build(surfaces)
                .rect(Rectangle::everything())
                .shader(Thinking::default())
                .visible(false)
                .finish().unwrap().into(),
            headlight: SurfaceBuilder::build(surfaces)
                .rect(Rectangle::new_from_coordinates(0, 0, 255, 0))
                .shader(Headlight::default())
                .visible(false)
                .opacity(Fract8::MIN)
                .finish().unwrap().into(),
            brakelight: SurfaceBuilder::build(surfaces)
                .rect(Brakelight::rectangle())
                .shader(Brakelight::default())
                .visible(false)
                .opacity(Fract8::MIN)
                .finish().unwrap().into(),
            display
        };
        trace!("shader={:?}", ret.overlay);
        ret
    }

    pub async fn sleep(&mut self) {
        info!("Running sleep sequence");
        let mut disp_anim = AnimDisplay(&mut self.display);
        TURN_OFF.apply([&mut disp_anim]).await;

        warn!("Resetting safety lights");
        self.brakelight.set_visible(false);
        self.headlight.set_visible(false);

        warn!("Turning off display");
        self.display.set_on(false);
        // Wait for the display hardware to actually turn off, before we return to process the next event, which could cause funky behaviors.
        // FIXME: also deadlocks :(
        //self.display.render_is_running.wait().await;
    }

    pub async fn wake(&mut self) {
        info!("Running startup fade sequence");

        info!("Turning on display");
        // Turn on the display hardware
        self.display.set_brightness(Fract8::MIN);
        self.display.set_on(true);
        // Wait for the renderer to start running again
        // FIXME: This deadlocks :(
        //self.display.render_is_running.wait().await;

        let fade_in = Animation::default().duration(Duration::from_secs(3)).from(Fract8::MIN).to(Fract8::MAX);
        trace!("Fading in brightness with overlay={:?}", self.overlay);
        self.overlay.set_opacity(Fract8::MAX);
        self.overlay.set_visible(true);
        fade_in.apply([&mut AnimDisplay(&mut self.display)]).await;

        warn!("Turning on safety lights");
        self.headlight.set_opacity(Fract8::MIN);
        self.headlight.set_visible(true);
        self.brakelight.set_opacity(Fract8::MIN);
        self.brakelight.set_visible(true);
        fade_in.apply([&mut self.headlight, &mut self.brakelight]).await;

        info!("Fade out overlay");
        TURN_OFF.apply([&mut self.overlay]).await;
        self.overlay.set_visible(false);
        info!("Wakeup complete!");
    }

    pub async fn on_event(&mut self, event: Prediction) {
        if let Prediction::SetPersonality(personality) = event { match personality {
            Personality::Active => {
                // FIXME: These should be a Off/Low/High enum, so the stopping brake looks different from the dayrunning brake.
                warn!("Active personality: Turning on safety lights");
                TURN_ON.apply([
                    &mut self.brakelight,
                    &mut self.headlight
                ]).await;
            },
            Personality::Parked => {
                warn!("Idle personality: Turning off safety lights");
                TURN_OFF.apply([
                    &mut self.brakelight,
                    &mut self.headlight
                ]).await;
            },
            Personality::Sleeping => {
                warn!("Sleeping personality: Safety UI is going to sleep");
                self.sleep().await;
            },
            Personality::Waking => {
                warn!("Waking personality: Waking up safety UI");
                self.wake().await;
            },
        } }
    }
}

const TURN_ON: Animation<Fract8> = Animation::new().duration(Duration::from_secs(1)).from(Fract8::MIN).to(Fract8::MAX);
const TURN_OFF: Animation<Fract8> = Animation::new().duration(Duration::from_secs(1)).from(Fract8::MAX).to(Fract8::MIN);

#[embassy_executor::task]
pub async fn safety_ui_main(mut events: DynSubscriber<'static, Prediction>, mut ui: SafetyUi<<UiSurfacePool as Surfaces>::Surface>) {
    // Wait for the renderer to start running
    //ui.display.render_is_running.wait().await;
    trace!("spooling until render starts ui={ui:?}");
    ui.display.wait_until_render_is_running().await;

    trace!("spooling wait task ui={ui:?}");
    // Run the wake sequence, and turn on the lights
    ui.wake().await;

    // Enter the event loop
    loop {
        ui.on_event(events.next_message_pure().await).await;
    }
}