use std::cell::RefCell;
use std::rc::{Rc, Weak};
use wasm_bindgen::prelude::*;

use crate::effect::{current_subscriber, run_effect, EffectImpl};

struct SignalInner {
    value: RefCell<JsValue>,
    subscribers: RefCell<Vec<Weak<EffectImpl>>>,
}

#[wasm_bindgen]
pub struct Signal {
    inner: Rc<SignalInner>,
}

#[wasm_bindgen]
impl Signal {
    #[wasm_bindgen(constructor)]
    pub fn new(initial: JsValue) -> Signal {
        Signal {
            inner: Rc::new(SignalInner {
                value: RefCell::new(initial),
                subscribers: RefCell::new(Vec::new()),
            }),
        }
    }

    pub fn get(&self) -> JsValue {
        if let Some(w) = current_subscriber() {
            self.inner.subscribers.borrow_mut().push(w);
        }
        self.inner.value.borrow().clone()
    }

    pub fn set(&self, v: JsValue) {
        *self.inner.value.borrow_mut() = v;
        let mut subscribers = self.inner.subscribers.borrow_mut();
        subscribers.retain(|w| w.upgrade().is_some());
        let live: Vec<Rc<EffectImpl>> = subscribers.iter().filter_map(|w| w.upgrade()).collect();
        drop(subscribers);
        for effect_handle in live {
            run_effect(&effect_handle);
        }
    }

    #[wasm_bindgen(js_name = clone_handle)]
    pub fn clone_handle(&self) -> Signal {
        Signal {
            inner: self.inner.clone(),
        }
    }
}
