use std::ffi::{CStr, CString, c_char, c_void};
use crate::types::{LV2_ATOM__SEQUENCE, LV2_MIDI__MIDI_EVENT, LV2_URID__MAP, LV2Feature};
pub type Urid = u32;
#[repr(C)]
struct Lv2UridMapFeature {
handle: *mut c_void,
map: Option<unsafe extern "C" fn(handle: *mut c_void, uri: *const c_char) -> Urid>,
}
#[derive(Default)]
pub struct UridMap {
pub midi_event: Urid,
pub atom_sequence: Urid,
pub atom_chunk: Urid,
pub atom_blank: Urid,
pub atom_object: Urid,
pub atom_bool: Urid,
pub atom_int: Urid,
pub atom_long: Urid,
pub atom_float: Urid,
pub atom_double: Urid,
pub time_position: Urid,
pub time_bar: Urid,
pub time_bar_beat: Urid,
pub time_beat: Urid,
pub time_beat_unit: Urid,
pub time_beats_per_bar: Urid,
pub time_beats_per_minute: Urid,
pub time_frame: Urid,
pub time_speed: Urid,
pub patch_set: Urid,
pub patch_property: Urid,
pub patch_value: Urid,
pub patch_subject: Urid,
handle: *mut c_void,
map_fn: Option<unsafe extern "C" fn(*mut c_void, *const c_char) -> Urid>,
}
impl UridMap {
pub unsafe fn from_features(features: *const *const LV2Feature) -> Self {
unsafe {
if features.is_null() {
return UridMap::default();
}
let map_uri = CString::new(LV2_URID__MAP).unwrap();
let mut handle: *mut c_void = std::ptr::null_mut();
let mut map_fn = None;
let mut i = 0;
while !(*features.add(i)).is_null() {
let feat = &**features.add(i);
if !feat.uri.is_null() && CStr::from_ptr(feat.uri) == map_uri.as_c_str() {
let map_feat = feat.data as *const Lv2UridMapFeature;
if !map_feat.is_null() {
handle = (*map_feat).handle;
map_fn = (*map_feat).map;
}
break;
}
i += 1;
}
UridMap::from_host(handle, map_fn)
}
}
pub unsafe fn from_host(
handle: *mut c_void,
map_fn: Option<unsafe extern "C" fn(*mut c_void, *const c_char) -> Urid>,
) -> Self {
let mut out = UridMap {
handle,
map_fn,
..UridMap::default()
};
out.midi_event = out.intern(LV2_MIDI__MIDI_EVENT);
out.atom_sequence = out.intern(LV2_ATOM__SEQUENCE);
out.atom_chunk = out.intern("http://lv2plug.in/ns/ext/atom#Chunk");
out.atom_blank = out.intern("http://lv2plug.in/ns/ext/atom#Blank");
out.atom_object = out.intern("http://lv2plug.in/ns/ext/atom#Object");
out.atom_bool = out.intern("http://lv2plug.in/ns/ext/atom#Bool");
out.atom_int = out.intern("http://lv2plug.in/ns/ext/atom#Int");
out.atom_long = out.intern("http://lv2plug.in/ns/ext/atom#Long");
out.atom_float = out.intern("http://lv2plug.in/ns/ext/atom#Float");
out.atom_double = out.intern("http://lv2plug.in/ns/ext/atom#Double");
out.time_position = out.intern("http://lv2plug.in/ns/ext/time#Position");
out.time_bar = out.intern("http://lv2plug.in/ns/ext/time#bar");
out.time_bar_beat = out.intern("http://lv2plug.in/ns/ext/time#barBeat");
out.time_beat = out.intern("http://lv2plug.in/ns/ext/time#beat");
out.time_beat_unit = out.intern("http://lv2plug.in/ns/ext/time#beatUnit");
out.time_beats_per_bar = out.intern("http://lv2plug.in/ns/ext/time#beatsPerBar");
out.time_beats_per_minute = out.intern("http://lv2plug.in/ns/ext/time#beatsPerMinute");
out.time_frame = out.intern("http://lv2plug.in/ns/ext/time#frame");
out.time_speed = out.intern("http://lv2plug.in/ns/ext/time#speed");
out.patch_set = out.intern("http://lv2plug.in/ns/ext/patch#Set");
out.patch_property = out.intern("http://lv2plug.in/ns/ext/patch#property");
out.patch_value = out.intern("http://lv2plug.in/ns/ext/patch#value");
out.patch_subject = out.intern("http://lv2plug.in/ns/ext/patch#subject");
out
}
#[must_use]
pub fn has_map(&self) -> bool {
self.map_fn.is_some()
}
pub fn intern(&self, uri: &str) -> Urid {
let Some(map_fn) = self.map_fn else {
return 0;
};
let Ok(c) = CString::new(uri) else {
return 0;
};
unsafe { map_fn(self.handle, c.as_ptr()) }
}
}
unsafe impl Send for UridMap {}