deft 0.15.0

Cross platform ui framework
use crate as deft;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

use crate::base::{Event, EventRegistration};
use crate::ext::audio_player::{AudioNotify, AudioServer, AudioSources};
use crate::js::js_event_loop::{js_create_event_loop_proxy, JsEventLoopProxy};
use crate::{js_deserialize, js_value};
use anyhow::Error;
use deft_macros::{js_methods, mrc_object};
use quick_js::JsValue;
use serde::{Deserialize, Serialize};

thread_local! {
    pub static NEXT_ID: Cell<u32> = Cell::new(1);
    pub static PLAYING_MAP: RefCell<HashMap<u32, Audio >> = RefCell::new(HashMap::new());
    pub static PLAYER: AudioServer = AudioServer::new({
        let elp = js_create_event_loop_proxy();
        move |id, msg| {
            let elp = elp.clone();
            handle_play_notify(elp, id, msg)
        }
    });
}

#[mrc_object]
pub struct Audio {
    id: u32,
    event_registration: EventRegistration<Audio>,
    sources: Arc<Mutex<AudioSources>>,
}

impl AudioData {
    pub fn new(id: u32, sources: Arc<Mutex<AudioSources>>) -> Self {
        Self {
            id,
            event_registration: EventRegistration::new(),
            sources,
        }
    }
}

#[derive(Serialize, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AudioOptions {
    sources: Vec<String>,
    index: Option<usize>,
    cache_dir: Option<String>,
    auto_loop: Option<bool>,
}

fn handle_play_notify(elp: JsEventLoopProxy, id: u32, msg: AudioNotify) {
    let _ = elp.schedule_macro_task(move || {
        let mut audio = PLAYING_MAP.with_borrow_mut(|m| m.get(&id).cloned());
        if let Some(a) = &mut audio {
            let target = a.clone();
            match msg {
                AudioNotify::Load(meta) => {
                    let mut event = Event::new("load", meta, target);
                    a.event_registration.emit_event(&mut event);
                }
                AudioNotify::TimeUpdate(time) => {
                    let mut event = Event::new("timeupdate", time, target);
                    a.event_registration.emit_event(&mut event);
                }
                AudioNotify::End => {
                    let mut event = Event::new("end", (), target);
                    a.event_registration.emit_event(&mut event);
                }
                AudioNotify::Finish => {
                    let mut event = Event::new("finish", (), target);
                    a.event_registration.emit_event(&mut event);
                    unregistry_playing(a);
                }
                AudioNotify::Pause => {
                    let mut event = Event::new("pause", (), target);
                    a.event_registration.emit_event(&mut event);
                }
                AudioNotify::Stop => {
                    let mut event = Event::new("stop", (), target);
                    a.event_registration.emit_event(&mut event);
                }
                AudioNotify::CurrentChange(info) => {
                    let mut event = Event::new("currentchange", info, target);
                    a.event_registration.emit_event(&mut event);
                }
            }
        }
    });
}

fn registry_playing(audio: &Audio) {
    let audio = audio.clone();
    PLAYING_MAP.with_borrow_mut(move |m| {
        m.insert(audio.id, audio);
    })
}

fn unregistry_playing(audio: &Audio) {
    let id = audio.id;
    PLAYING_MAP.with_borrow_mut(move |m| {
        m.remove(&id);
    })
}

js_value!(Audio);
js_deserialize!(AudioOptions);

#[js_methods]
impl Audio {
    #[js_func]
    pub fn create(options: AudioOptions) -> Result<Audio, Error> {
        let id = NEXT_ID.get();
        NEXT_ID.set(id + 1);

        let sources = AudioSources {
            urls: options.sources,
            next_index: options.index.unwrap_or(0),
            cache_dir: options.cache_dir,
            auto_loop: options.auto_loop.unwrap_or(false),
            download_handle: None,
        };
        let audio = AudioData::new(id, Arc::new(Mutex::new(sources)));
        Ok(audio.to_ref())
    }

    #[js_func]
    pub fn play(audio: Audio) -> Result<(), Error> {
        registry_playing(&audio);
        PLAYER.with(move |p| p.play(audio.id, audio.sources.clone()));
        Ok(())
    }

    #[js_func]
    pub fn pause(audio: Audio) -> Result<(), Error> {
        PLAYER.with(|p| p.pause(audio.id));
        Ok(())
    }

    #[js_func]
    pub fn stop(&self) -> Result<(), Error> {
        unregistry_playing(&self);
        PLAYER.with(|p| p.stop(self.id));
        Ok(())
    }

    #[js_func]
    pub fn add_event_listener(
        &mut self,
        event_type: String,
        callback: JsValue,
    ) -> Result<i32, Error> {
        let er = &mut self.event_registration;
        Ok(er.add_js_event_listener(&event_type, callback))
    }

    #[js_func]
    pub fn remove_event_listener(&mut self, event_type: String, id: u32) -> Result<(), Error> {
        self.event_registration
            .remove_event_listener(&event_type, id);
        Ok(())
    }
}