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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use crate::{api::API, plugin::Plugin};
use bitflags::bitflags;
use core::fmt;
use rustsynth_sys as ffi;
use std::{ffi::CStr, marker::PhantomData, ptr::NonNull};

bitflags! {
    pub struct CoreCreationFlags: u32 {
        const NONE = 0b00000000;
        const ENABLE_GRAPH_INSPECTION = 0b00000001;
        const DISABLE_AUTO_LOADING = 0b00000010;
        const DISABLE_LIBRARY_UNLOADING = 0b00000100;
    }
}

/// A reference to a VapourSynth core.
#[derive(Debug, Clone, Copy)]
pub struct CoreRef<'core> {
    handle: NonNull<ffi::VSCore>,
    _owner: PhantomData<&'core ()>,
}

unsafe impl<'core> Send for CoreRef<'core> {}
unsafe impl<'core> Sync for CoreRef<'core> {}

impl<'core> CoreRef<'core> {
    /// Creates and returns a new core.
    ///
    /// Note that there's currently no safe way of freeing the returned core, and the lifetime is
    /// unbounded, because it can live for an arbitrary long time. You may use the (unsafe)
    /// `rustsynth_sys::VSAPI::freeCore()` after ensuring that all frame requests have completed
    /// and all objects belonging to the core have been released.
    ///
    /// # Example
    ///
    /// ```
    /// use rustsynth::core::{CoreFlags, CoreRef};
    /// let core = CoreRef::new(CoreFlags::ENABLE_GRAPH_INSPECTION | CoreFlags::DISABLE_AUTO_LOADING)
    /// ```
    #[inline]
    pub fn new(flags: CoreCreationFlags) -> Self {
        let api = API::get().unwrap();
        unsafe {
            let handle = api.create_core(flags.bits() as i32);
            Self::from_ptr(handle)
        }
    }
    /// Wraps `handle` in a `CoreRef`.
    ///
    /// # Safety
    /// The caller must ensure `handle` is valid and API is cached.
    #[inline]
    pub(crate) unsafe fn from_ptr(handle: *mut ffi::VSCore) -> Self {
        Self {
            handle: NonNull::new_unchecked(handle),
            _owner: PhantomData,
        }
    }

    /// Returns the underlying pointer.
    #[inline]
    pub(crate) fn ptr(&self) -> *mut ffi::VSCore {
        self.handle.as_ptr()
    }

    /// Returns an instance of `CoreInfo`
    ///
    /// # Panics
    ///
    /// Will panic if core configuration is not valid
    pub fn info(&self) -> CoreInfo {
        let core_info = unsafe { API::get_cached().get_core_info(self.ptr()) };
        let version_string = unsafe { CStr::from_ptr(core_info.versionString).to_str().unwrap() };
        debug_assert!(core_info.numThreads >= 0);
        debug_assert!(core_info.maxFramebufferSize >= 0);
        debug_assert!(core_info.usedFramebufferSize >= 0);

        CoreInfo {
            version_string,
            core_version: core_info.core,
            api_version: core_info.api,
            num_threads: core_info.numThreads as usize,
            max_framebuffer_size: core_info.maxFramebufferSize as u64,
            used_framebuffer_size: core_info.usedFramebufferSize as u64,
        }
    }

    /// Returns an instance of `Some(Plugin)` if there exists a plugin loaded associated with the namespace
    ///
    /// None if no plugin is found
    pub fn plugin_by_namespace(&self, namespace: &str) -> Option<Plugin<'core>> {
        unsafe { API::get_cached() }.plugin_by_namespace(namespace, self)
    }

    /// Returns an instance of `Some(Plugin)` if there exists a plugin loaded associated with the id
    ///
    /// None if no plugin is found
    pub fn plugin_by_id(&self, id: &str) -> Option<Plugin<'_>> {
        unsafe { API::get_cached() }.plugin_by_id(id, self)
    }

    /// Returns a iterator over the loaded plugins
    pub fn plugins(&self) -> Plugins<'_> {
        unsafe { API::get_cached() }.plugins(self)
    }

    pub fn set_thread_count(&self, count: usize) -> i32 {
        unsafe { API::get_cached().set_thread_count(self.ptr(), count as i32) }
    }

    /// Consumes and frees the core and core reference
    ///
    /// # Safety
    ///
    /// Must ensure that all frame requests have completed and all objects belonging to the core have been released.
    pub unsafe fn free_core(self) {
        API::get_cached().free_core(self.handle.as_ptr());
    }
}

/// Contains information about a VapourSynth core.
#[derive(Debug, Clone, Copy, Hash)]
pub struct CoreInfo {
    pub version_string: &'static str,
    pub core_version: i32,
    pub api_version: i32,
    pub num_threads: usize,
    pub max_framebuffer_size: u64,
    pub used_framebuffer_size: u64,
}

/// An interator over the loaded plugins
///
/// created by [`CoreRef::plugins()`]
///
/// [`CoreRef::plugins()`]: crate::core::CoreRef::plugins()
#[derive(Debug, Clone, Copy)]
pub struct Plugins<'core> {
    plugin: Option<Plugin<'core>>,
    core: &'core CoreRef<'core>,
}

impl<'core> Plugins<'core> {
    pub(crate) fn new(core: &'core CoreRef<'core>) -> Self {
        Plugins { plugin: None, core }
    }
}

impl<'core> Iterator for Plugins<'core> {
    type Item = Plugin<'core>;

    fn next(&mut self) -> Option<Self::Item> {
        self.plugin = unsafe { API::get_cached().next_plugin(self.plugin, self.core) };
        self.plugin
    }
}

impl fmt::Display for CoreInfo {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.version_string)?;
        writeln!(f, "Worker threads: {}", self.num_threads)?;
        writeln!(
            f,
            "Max framebuffer cache size: {}",
            self.max_framebuffer_size
        )?;
        writeln!(
            f,
            "Current framebuffer cache size: {}",
            self.used_framebuffer_size
        )
    }
}