use oden_js::{module::native::NativeModuleBuilder, ContextRef};
use std::cell::RefCell;
use std::sync::Arc;
use std::time::{Duration, Instant};

struct TimeImpl {
    start: Instant,
    last_frame_time: Instant,
    frame_time: Instant,
}

impl TimeImpl {
    fn new() -> Self {
        let now = Instant::now();
        TimeImpl {
            start: now,
            frame_time: now,
            last_frame_time: now - Duration::from_millis(15),
        }
    }

    fn since_last_frame(&self) -> f64 {
        (self.frame_time - self.last_frame_time).as_secs_f64()
    }

    fn since_start(&self) -> f64 {
        (self.frame_time - self.start).as_secs_f64()
    }

    fn set_frame_time(&mut self, now: Instant) {
        self.last_frame_time = self.frame_time;
        self.frame_time = now;
    }
}

pub struct TimeAPI {
    time: Arc<RefCell<TimeImpl>>,
}

impl TimeAPI {
    pub fn define(ctx: &ContextRef) -> oden_js::Result<Self> {
        let time = Arc::new(RefCell::new(TimeImpl::new()));
        let mut builder = NativeModuleBuilder::new(ctx);
        {
            let time = time.clone();
            builder.export(
                "since_last_frame",
                ctx.new_fn(move |_ctx: &ContextRef| time.borrow().since_last_frame())?,
            )?;
        }
        {
            let time = time.clone();
            builder.export(
                "since_start",
                ctx.new_fn(move |_ctx: &ContextRef| time.borrow().since_start())?,
            )?;
        }
        builder.build("time-core")?;
        Ok(TimeAPI { time })
    }

    pub fn set_frame_time(&mut self, now: Instant) {
        self.time.borrow_mut().set_frame_time(now);
    }
}
