use core_foundation::base::OSStatus;
use std::collections::HashMap;
use std::ffi::c_void;
use std::ops::Deref;
use std::ptr;
use coremidi_sys::{
MIDIObjectRef, MIDIPortConnectSource, MIDIPortDisconnectSource, MIDIPortDispose, MIDIPortRef,
MIDISend, MIDISendEventList,
};
use crate::endpoints::destinations::Destination;
use crate::endpoints::sources::Source;
use crate::object::Object;
use crate::packets::PacketList;
use crate::{EventBuffer, EventList, PacketBuffer};
pub enum Packets<'a> {
BorrowedPacketList(&'a PacketList),
BorrowedEventList(&'a EventList),
OwnedEventBuffer(EventBuffer),
}
impl<'a> From<&'a PacketList> for Packets<'a> {
fn from(packet_list: &'a PacketList) -> Self {
Self::BorrowedPacketList(packet_list)
}
}
impl<'a> From<&'a PacketBuffer> for Packets<'a> {
fn from(packet_buffer: &'a PacketBuffer) -> Self {
Self::BorrowedPacketList(&*packet_buffer)
}
}
impl<'a> From<&'a EventList> for Packets<'a> {
fn from(event_list: &'a EventList) -> Self {
Self::BorrowedEventList(event_list)
}
}
impl<'a> From<&'a EventBuffer> for Packets<'a> {
fn from(event_buffer: &'a EventBuffer) -> Self {
Self::BorrowedEventList(&*event_buffer)
}
}
impl<'a> From<EventBuffer> for Packets<'a> {
fn from(event_buffer: EventBuffer) -> Self {
Self::OwnedEventBuffer(event_buffer)
}
}
#[derive(Debug)]
pub struct Port {
pub(crate) object: Object,
}
impl Port {
pub(crate) fn new(port_ref: MIDIPortRef) -> Self {
Self {
object: Object(port_ref),
}
}
}
impl Deref for Port {
type Target = Object;
fn deref(&self) -> &Object {
&self.object
}
}
impl Drop for Port {
fn drop(&mut self) {
unsafe { MIDIPortDispose(self.object.0) };
}
}
#[derive(Debug)]
pub struct OutputPort {
pub(crate) port: Port,
}
impl OutputPort {
pub(crate) fn new(port_ref: MIDIPortRef) -> Self {
Self {
port: Port::new(port_ref),
}
}
pub fn send<'a, P>(&self, destination: &Destination, packets: P) -> Result<(), OSStatus>
where
P: Into<Packets<'a>>,
{
let status = match packets.into() {
Packets::BorrowedPacketList(packet_list) => unsafe {
MIDISend(
self.port.object.0,
destination.endpoint.object.0,
packet_list.as_ptr(),
)
},
Packets::BorrowedEventList(event_list) => unsafe {
MIDISendEventList(
self.port.object.0,
destination.endpoint.object.0,
event_list.as_ptr(),
)
},
Packets::OwnedEventBuffer(event_buffer) => unsafe {
MIDISendEventList(
self.port.object.0,
destination.endpoint.object.0,
event_buffer.as_ptr(),
)
},
};
if status == 0 {
Ok(())
} else {
Err(status)
}
}
}
impl Deref for OutputPort {
type Target = Port;
fn deref(&self) -> &Port {
&self.port
}
}
#[derive(Debug)]
pub struct InputPort {
pub(crate) port: Port,
}
impl InputPort {
pub(crate) fn new(port_ref: MIDIPortRef) -> Self {
Self {
port: Port::new(port_ref),
}
}
pub fn connect_source(&self, source: &Source) -> Result<(), OSStatus> {
let status =
unsafe { MIDIPortConnectSource(self.object.0, source.object.0, ptr::null_mut()) };
if status == 0 {
Ok(())
} else {
Err(status)
}
}
pub fn disconnect_source(&self, source: &Source) -> Result<(), OSStatus> {
let status = unsafe { MIDIPortDisconnectSource(self.object.0, source.object.0) };
if status == 0 {
Ok(())
} else {
Err(status)
}
}
}
impl Deref for InputPort {
type Target = Port;
fn deref(&self) -> &Port {
&self.port
}
}
#[derive(Debug)]
pub struct InputPortWithContext<T> {
pub(crate) port: Port,
pub(crate) contexts: HashMap<MIDIObjectRef, Box<T>>,
}
impl<T> InputPortWithContext<T> {
pub(crate) fn new(port_ref: MIDIPortRef) -> Self {
Self {
port: Port::new(port_ref),
contexts: HashMap::new(),
}
}
pub fn connect_source(&mut self, source: &Source, context: T) -> Result<(), OSStatus> {
let mut context = Box::new(context);
let context_ptr = context.as_mut() as *mut T;
let status = unsafe {
MIDIPortConnectSource(self.object.0, source.object.0, context_ptr as *mut c_void)
};
if status == 0 {
self.contexts.insert(source.object.0, context);
Ok(())
} else {
Err(status)
}
}
pub fn disconnect_source(&mut self, source: &Source) -> Result<(), OSStatus> {
let status = unsafe { MIDIPortDisconnectSource(self.object.0, source.object.0) };
if status == 0 {
self.contexts.remove(&source.object.0);
Ok(())
} else {
Err(status)
}
}
}
impl<T> Deref for InputPortWithContext<T> {
type Target = Port;
fn deref(&self) -> &Port {
&self.port
}
}