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
pub use lotus_shared::graphics::*;

pub mod textures {
    use lotus_script_sys::FfiObject;
    use lotus_shared::{content::ContentId, graphics::Color, math::UVec2};

    pub use lotus_shared::graphics::textures::*;

    /// A texture that can be manipulated and displayed on script texture slots.
    #[derive(Debug)]
    pub struct Texture(TextureHandle);

    impl Texture {
        /// Create a new texture.
        #[must_use]
        pub fn create<'a>(options: impl Into<TextureCreationOptions<'a>>) -> Self {
            let options = options.into();
            let options = FfiObject::new(&options);

            unsafe {
                Self(TextureHandle::new(lotus_script_sys::textures::create(
                    options.packed(),
                )))
            }
        }

        /// Add an action to the texture. You may want to call the helper methods
        /// instead of this.
        pub fn add_action(&mut self, action: TextureAction) {
            let action = FfiObject::new(&action);

            unsafe { lotus_script_sys::textures::add_action(self.0.id(), action.packed()) }
        }

        /// Draw a rectangle on the texture.
        pub fn draw_rect(&mut self, start: impl Into<UVec2>, end: impl Into<UVec2>, color: Color) {
            self.add_action(TextureAction::DrawRect {
                start: start.into(),
                end: end.into(),
                color,
            });
        }

        /// Clear the texture with a color.
        pub fn clear(&mut self, color: Color) {
            self.add_action(TextureAction::Clear(color));
        }

        /// Read the color of a pixel on the texture.
        #[inline]
        pub fn read_pixel(&self, x: u32, y: u32) -> Color {
            let packed = unsafe { lotus_script_sys::textures::get_pixel(self.0.id(), x, y) };
            packed.into()
        }

        /// Draw multiple pixels on the texture.
        pub fn draw_pixels<P>(&mut self, pixels: &[P])
        where
            P: Into<DrawPixel> + Copy,
        {
            let pixels = pixels.iter().map(|p| (*p).into()).collect();

            self.add_action(TextureAction::DrawPixels(pixels));
        }

        /// Draws another texture on top of this one.
        pub fn draw_texture(&mut self, other: &Texture, options: DrawTextureOpts) {
            self.add_action(TextureAction::DrawScriptTexture {
                handle: other.handle(),
                options,
            });
        }

        /// Call this once for every game-texture you want to apply this to. You can define the name in the content tool.
        pub fn apply_to(&mut self, name: &str) {
            let name = FfiObject::new(&name);
            unsafe { lotus_script_sys::textures::apply_to(self.0.id(), name.packed()) }
        }

        /// Only call this if you need your actions to be applied immediately.
        /// Cause of streaming assets, this method will return false if the actions are not yet applied.
        /// Just call this method again until it returns true.
        pub fn flush(&mut self) -> bool {
            unsafe { lotus_script_sys::textures::flush_actions(self.0.id()) == 1 }
        }

        /// Draws another texture on top of this one.
        pub fn draw_script_texture(&mut self, other: &Texture, options: DrawTextureOpts) {
            self.add_action(TextureAction::DrawScriptTexture {
                handle: other.handle(),
                options,
            });
        }

        /// Draws a text on the texture.
        pub fn draw_text(
            &mut self,
            font: ContentId,
            text: impl Into<String>,
            top_left: impl Into<UVec2>,
            letter_spacing: u32,
            full_color: impl Into<Option<Color>>,
            alpha_mode: AlphaMode,
        ) {
            self.add_action(TextureAction::DrawText {
                font,
                text: text.into(),
                top_left: top_left.into(),
                letter_spacing,
                full_color: full_color.into(),
                alpha_mode,
            });
        }

        /// Get the handle of the texture.
        pub fn handle(&self) -> TextureHandle {
            self.0
        }

        /// Forget the texture. This means it will not be disposed when the texture is dropped.
        /// Use this only if you want to keep the texture alive without keeping a reference to it.
        pub fn forget(mut self) {
            self.0 = TextureHandle::new(u32::MAX);
        }
    }

    impl Drop for Texture {
        fn drop(&mut self) {
            if self.0.id() != u32::MAX {
                unsafe { lotus_script_sys::textures::dispose(self.0.id()) }
            }
        }
    }

    pub enum DrawableTexture {
        // TODO: Support content textures.
        // Content(ContentId),
        Script(TextureHandle),
    }

    impl From<&Texture> for DrawableTexture {
        fn from(texture: &Texture) -> Self {
            Self::Script(texture.handle())
        }
    }

    impl From<TextureHandle> for DrawableTexture {
        fn from(handle: TextureHandle) -> Self {
            Self::Script(handle)
        }
    }
}