mod metronome;

use std::{cell::RefCell, time::Duration};

use metronome::Metronome;

use neon::prelude::*;

type BoxedTracker = JsBox<RefCell<Metronome>>;
impl Finalize for Metronome {}

pub fn get_beat_tracker(mut cx: FunctionContext) -> JsResult<JsObject> {
    let obj = cx.empty_object();
    let value_obj = cx.empty_object();

    let boxed_tracker = cx.boxed(RefCell::new(Metronome::new(Duration::from_secs(2))));
    value_obj.set(&mut cx, "_rust_ptr", boxed_tracker)?;

    let tap_function = JsFunction::new(&mut cx, tap)?;
    value_obj.set(&mut cx, "tap", tap_function)?;

    let get_progress_function = JsFunction::new(&mut cx, get_progress)?;
    value_obj.set(&mut cx, "getProgress", get_progress_function)?;

    obj.set(&mut cx, "value", value_obj)?;

    let success_string = cx.string("success".to_string());
    obj.set(&mut cx, "type", success_string)?;

    Ok(obj)
}

pub fn tap(mut cx: FunctionContext) -> JsResult<JsValue> {
    let this = cx.this();

    let boxed_tracker = this
        .get(&mut cx, "_rust_ptr")?
        .downcast_or_throw::<BoxedTracker, _>(&mut cx)?;

    boxed_tracker.borrow_mut().tap();

    Ok(JsUndefined::new(&mut cx).upcast())
}

pub fn get_progress(mut cx: FunctionContext) -> JsResult<JsObject> {
    let this = cx.this();

    let boxed_tracker = this
        .get(&mut cx, "_rust_ptr")?
        .downcast_or_throw::<BoxedTracker, _>(&mut cx)?;

    let obj = cx.empty_object();

    let type_string;
    match boxed_tracker.borrow().current_beat_progress() {
        Some(progress) => {
            type_string = cx.string("some".to_string());

            let progress = cx.number(progress);
            obj.set(&mut cx, "value", progress)?;
        }
        None => {
            type_string = cx.string("none".to_string());
        }
    }

    obj.set(&mut cx, "type", type_string)?;

    Ok(obj)
}
