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
use crate::{
    bounding_box_attachment::BoundingBoxAttachment,
    c::{
        spAttachment, spAttachmentType, spAttachment_dispose, spBoundingBoxAttachment,
        spClippingAttachment, spMeshAttachment, spPointAttachment, spRegionAttachment,
    },
    c_interface::{NewFromPtr, SyncPtr},
    clipping_attachment::ClippingAttachment,
    mesh_attachment::MeshAttachment,
    point_attachment::PointAttachment,
    region_attachment::RegionAttachment,
};

/// Slot attachments.
///
/// [Spine API Reference](http://esotericsoftware.com/spine-api-reference#Attachment)
///
/// Attachments are reference counted and can be stored and worked with directly, however, some of
/// the underlying data that attachments point to may be cleared. For this reason, attachments
/// should be used with caution, and only used so long as the SkeletonData they came from remains
/// valid, and only attached to the slot they are meant for.
#[derive(Debug)]
pub struct Attachment {
    c_attachment: SyncPtr<spAttachment>,
}

impl NewFromPtr<spAttachment> for Attachment {
    unsafe fn new_from_ptr(c_attachment: *const spAttachment) -> Self {
        (*(c_attachment as *mut spAttachment)).refCount += 1;
        Self {
            c_attachment: SyncPtr(c_attachment as *mut spAttachment),
        }
    }
}

impl Attachment {
    pub fn as_region(&self) -> Option<RegionAttachment> {
        if self.attachment_type() == AttachmentType::Region {
            Some(RegionAttachment::new_from_ptr(
                self.c_attachment.0 as *mut spRegionAttachment,
            ))
        } else {
            None
        }
    }

    pub fn as_bounding_box(&self) -> Option<BoundingBoxAttachment> {
        if self.attachment_type() == AttachmentType::BoundingBox {
            Some(unsafe {
                BoundingBoxAttachment::new_from_ptr(
                    self.c_attachment.0 as *mut spBoundingBoxAttachment,
                )
            })
        } else {
            None
        }
    }

    pub fn as_mesh(&self) -> Option<MeshAttachment> {
        if self.attachment_type() == AttachmentType::Mesh {
            Some(unsafe {
                MeshAttachment::new_from_ptr(self.c_attachment.0 as *mut spMeshAttachment)
            })
        } else {
            None
        }
    }

    pub fn as_point(&self) -> Option<PointAttachment> {
        if self.attachment_type() == AttachmentType::Point {
            Some(unsafe {
                PointAttachment::new_from_ptr(self.c_attachment.0 as *mut spPointAttachment)
            })
        } else {
            None
        }
    }

    pub fn as_clipping(&self) -> Option<ClippingAttachment> {
        if self.attachment_type() == AttachmentType::Clipping {
            Some(unsafe {
                ClippingAttachment::new_from_ptr(self.c_attachment.0 as *mut spClippingAttachment)
            })
        } else {
            None
        }
    }

    c_accessor_string!(name, name);
    c_accessor_enum!(attachment_type, type_0, AttachmentType);
    c_ptr!(c_attachment, spAttachment);
}

impl Clone for Attachment {
    fn clone(&self) -> Self {
        unsafe { Attachment::new_from_ptr(self.c_ptr()) }
    }
}

impl Drop for Attachment {
    fn drop(&mut self) {
        unsafe {
            spAttachment_dispose(self.c_ptr());
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttachmentType {
    Region = 0,
    BoundingBox = 1,
    Mesh = 2,
    LinkedMesh = 3,
    Path = 4,
    Point = 5,
    Clipping = 6,
    Unknown = 99,
}

impl From<spAttachmentType> for AttachmentType {
    fn from(attachment_type: spAttachmentType) -> Self {
        match attachment_type {
            0 => Self::Region,
            1 => Self::BoundingBox,
            2 => Self::Mesh,
            3 => Self::LinkedMesh,
            4 => Self::Path,
            5 => Self::Point,
            6 => Self::Clipping,
            _ => Self::Unknown,
        }
    }
}