1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Graph and Map types for working with libmapper's distributed graph.
//! 
//! The [Map] type is used to create a connection between two [Signal] instances.
//! 
//! The [Graph] type can be shared between devices to improve performance and memory usage.
use std::{ffi::c_int, time::Duration};

use crate::{bindings::*, signal::Signal, object::MapperObject};

/// A graph is a lightweight connection to libmapper's distributed graph.
/// You can use a graph to create maps and query the state of the graph.
pub struct Graph {
    pub(crate) handle: mpr_graph,
    owned: bool
}

unsafe impl Send for Graph {}
unsafe impl Sync for Graph {}

impl Graph {
  pub fn create() -> Graph {
    Graph {
      owned: true,
      handle: unsafe { mpr_graph_new(0) }
    }
  }
}

impl Drop for Graph {
    fn drop(&mut self) {
        if self.owned {
            unsafe {
                mpr_graph_free(self.handle);
            }
        }
    }
}

impl Graph {
  /// Poll the graph without blocking
  pub fn poll(&self) {
    unsafe {
      mpr_graph_poll(self.handle, 0);
    }
  }
  /// Poll the graph and block for the specified amount of time
  /// 
  /// Use this instead of sleeping in a loop
  pub fn poll_and_block(&self, time: Duration) {
    unsafe {
      mpr_graph_poll(self.handle, time.as_millis() as c_int);
    }
  }
}

/// A directional connection between multiple signals. Changes to input signals will affect output signals.
/// 
/// # Examples
/// Create a map between two signals:
/// ```
/// use std::thread;
/// use std::time::Duration;
/// use libmapper_rs::graph::Map;
/// use libmapper_rs::signal::Signal;
/// fn create_map(sig_a: &Signal, sig_b: &Signal) -> Map {
///   let map = Map::create(sig_a, sig_b);
///   loop {
///     if map.is_ready() {
///      break;
///     }
///    thread::sleep(Duration::from_millis(10));
///   }
///   map
/// }
/// ```
pub struct Map {
  pub(crate) handle: mpr_map
}

impl Map {
  /// Create a new map between two signals.
  /// This does not actually create the map in the graph, [push](Map::push) must be called to let the rest of the graph know about the map.
  pub fn create(src: &Signal, dst: &Signal) -> Map {
    Map {
      handle: unsafe { mpr_map_new(1, &src.handle, 1, &dst.handle) }
    }
  }

  /// Publish this map to the distributed graph.
  /// After calling this function and once [is_ready](Map::is_ready) returns `true`, the map is active.
  pub fn push(&self) {
    unsafe {
      mpr_obj_push(self.handle);
    }
  }

  /// Returns `true` once the map has been published and is active.
  /// Otherwise, returns false.
  pub fn is_ready(&self) -> bool {
    unsafe {
      mpr_map_get_is_ready(self.handle) != 0
    }
  }

  /// Destroy the map, severing the connection between the signals.
  pub fn release(self) {
    unsafe {
      mpr_map_release(self.handle)
    }
  }

  /// Set the expression used to map the values from the source(s) to the destination.
  ///
  /// This is a helper function wrapping [`MapperObject::set_property_str`]
  pub fn set_expr(&self, expression: &str) {
    self.set_property_str(mpr_prop::MPR_PROP_EXPR, expression);
  }
}