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
use std::sync::Arc;

use crate::{
    animation::Animation,
    c::{
        c_void, spAnimationStateData, spAnimationStateData_create, spAnimationStateData_dispose,
        spAnimationStateData_getMix, spAnimationStateData_setMix,
        spAnimationStateData_setMixByName, spSkeletonData,
    },
    c_interface::{to_c_str, NewFromPtr, SyncPtr},
    skeleton_data::SkeletonData,
};

#[allow(unused_imports)]
use crate::AnimationState;

/// Animation settings used to instantiate [`AnimationState`].
///
/// [Spine API Reference](http://esotericsoftware.com/spine-api-reference#AnimationStateData)
///
/// Mix durations can be applied to automatically blend between animations.  For example, to
/// smoothly mix between a `walk` and `run` animation for `0.2` seconds:
///
/// ```
/// # #[path="./test.rs"]
/// # mod test;
/// # let mut animation_state_data = test::TestAsset::spineboy().animation_state_data(true);
/// animation_state_data.set_mix_by_name("walk", "run", 0.2);
/// ```
///
/// This operation is one way, so to blend back and forth between the two animations, two mix
/// durations must be specified:
///
/// ```
/// # #[path="./test.rs"]
/// # mod test;
/// # let mut animation_state_data = test::TestAsset::spineboy().animation_state_data(true);
/// animation_state_data.set_mix_by_name("walk", "run", 0.2);
/// animation_state_data.set_mix_by_name("run", "walk", 0.2);
/// ```
#[derive(Debug)]
pub struct AnimationStateData {
    c_animation_state_data: SyncPtr<spAnimationStateData>,
    owns_memory: bool,
    _skeleton_data: Option<Arc<SkeletonData>>,
}

impl NewFromPtr<spAnimationStateData> for AnimationStateData {
    unsafe fn new_from_ptr(c_animation_state_data: *mut spAnimationStateData) -> Self {
        Self {
            c_animation_state_data: SyncPtr(c_animation_state_data),
            owns_memory: false,
            _skeleton_data: None,
        }
    }
}

impl AnimationStateData {
    #[must_use]
    pub fn new(skeleton_data: Arc<SkeletonData>) -> Self {
        let c_animation_state_data = unsafe { spAnimationStateData_create(skeleton_data.c_ptr()) };
        Self {
            c_animation_state_data: SyncPtr(c_animation_state_data),
            owns_memory: true,
            _skeleton_data: Some(skeleton_data),
        }
    }

    pub fn set_mix_by_name(&mut self, from_name: &str, to_name: &str, duration: f32) {
        let c_from_name = to_c_str(from_name);
        let c_to_name = to_c_str(to_name);
        unsafe {
            spAnimationStateData_setMixByName(
                self.c_ptr(),
                c_from_name.as_ptr(),
                c_to_name.as_ptr(),
                duration,
            );
        }
    }

    pub fn set_mix(&mut self, from: &Animation, to: &Animation, duration: f32) {
        unsafe {
            spAnimationStateData_setMix(self.c_ptr(), from.c_ptr(), to.c_ptr(), duration);
        }
    }

    pub fn get_mix(&mut self, from: &Animation, to: &Animation) -> f32 {
        unsafe { spAnimationStateData_getMix(self.c_ptr(), from.c_ptr(), to.c_ptr()) }
    }

    c_accessor_tmp_ptr_mut!(
        skeleton_data,
        skeleton_data_mut,
        skeletonData,
        SkeletonData,
        spSkeletonData
    );
    c_accessor_mut!(default_mix, set_default_mix, defaultMix, f32);
    c_accessor_passthrough!(entries, entries, *const c_void);
    c_ptr!(c_animation_state_data, spAnimationStateData);
}

impl Drop for AnimationStateData {
    fn drop(&mut self) {
        if self.owns_memory {
            unsafe {
                spAnimationStateData_dispose(self.c_animation_state_data.0);
            }
        }
    }
}