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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
//!

use crate::{c_api::ffi, NcBlitter, NcPlane, NcRgba, NcScale};
use core::ptr::null_mut;

mod builder;
pub use builder::NcVisualOptionsBuilder;

/// Options struct for [`NcVisual`][crate::NcVisual].
///
/// It is recommended to construct it via [`NcVisualOptionsBuilder`]
/// by calling [`NcVisualOptions::builder()`].
///
/// # Usage
///
/// If a plane is not provided, one will be created, having the exact size
/// necessary to display the visual (this might be smaller or larger than
/// the rendering area). if [`ChildPlane`] is provided, this will be
/// interpreted as the parent.
///
/// A subregion of the visual can be rendered using `beg_y`, `beg_x`, `len_y`,
/// and `len_x`.
///
/// # Fields
///
/// - [`n`]: an optional mutable pointer to an [`NcPlane`].
///
/// - [`scaling`]: an [`NcScale`] indicating how the source will be
///   stretched/scaled relative to the `NcPlane`.
///
/// - [`y`]: if an `NcPlane` is provided in `n` then this specifies where the
///   `NcVisual` will be on that plane.
///
///   Otherwise it specifies where the created `NcPlane` will be placed relative
///   to the standard plane's origin.
///
///   If [`VerAligned`] is set, this will be interpreted as an [`NcAlign`] value.
///
/// - [`x`]: if an `NcPlane` is provided in `n` then this specifies where the
///   `NcVisual` will be on that plane.
///
///   Otherwise it specifies where the created `NcPlane` will be placed relative
///   to the standard plane's origin.
///
///   If [`HorAligned`] is set, this will be interpreted as an [`NcAlign`] value.
///
/// - [`begy`]: origin of rendered section in the *y* axis.
/// - [`begx`]: origin of rendered section in the *x* axis.
/// - [`leny`]: length of rendered section in the *y* axis.
/// - [`lenx`]: length of rendered section in the *x* axis.
///
/// - [`blitter`]: [`NcBlitter`] glyph set to use for blitting.
///
/// - [`flags`]: [`NcVisualFlag`].
///
/// - [`transcolor`]: treats this color as transparent when the [`AddAlpha`]
///   flag is active.
///
/// - [`pxoffy`]: pixel offset within the cell in the *y* axis.
///
///   If [`NcBlitter::Pixel`] is used the bitmap will be drawn offset from the
///   upper-left cell’s origin by these amounts, otherwise this will be ignored.
///
///   It is an error if either number exceeds the cell-pixel geometry in any
///   dimension (see [`NcPixelGeometry.cell_y`], [`NcVisualGeometry.cdim_yx`]).
///
/// - [`pxoffx`]: pixel offset within the cell in the *x* axis.
///
///   If [`NcBlitter::Pixel`] is used, the bitmap will be drawn offset from the
///   upper-left cell’s origin by these amounts, otherwise this will be ignored.
///
///   It is an error if either number exceeds the cell-pixel geometry in any
///   dimension (see [`NcPixelGeometry.cell_x`], [`NcVisualGeometry.cdim_yx`]).
///
/// [`NcVisualOptions::builder()`]: NcVisualOptions#method.builder
/// [`NcAlign`]: crate::NcAlign
/// [`NcPixelGeometry.cell_y`]: crate::NcPixelGeometry#structfield.cell_y
/// [`NcPixelGeometry.cell_x`]: crate::NcPixelGeometry#structfield.cell_x
/// [`NcVisualGeometry.cdim_yx`]: crate::NcVisualGeometry#structfield.cdim_yx
/// [`n`]: crate::c_api::ffi::ncvisual_options#structfield.n
/// [`scaling`]: crate::c_api::ffi::ncvisual_options#structfield.scaling
/// [`y`]: crate::c_api::ffi::ncvisual_options#structfield.y
/// [`x`]: crate::c_api::ffi::ncvisual_options#structfield.x
/// [`begy`]: crate::c_api::ffi::ncvisual_options#structfield.begy
/// [`begx`]: crate::c_api::ffi::ncvisual_options#structfield.begx
/// [`leny`]: crate::c_api::ffi::ncvisual_options#structfield.leny
/// [`lenx`]: crate::c_api::ffi::ncvisual_options#structfield.lenx
/// [`blitter`]: crate::c_api::ffi::ncvisual_options#structfield.blitter
/// [`flags`]: crate::c_api::ffi::ncvisual_options#structfield.flags
/// [`transcolor`]: crate::c_api::ffi::ncvisual_options#structfield.transcolor
/// [`pxoffy`]: crate::c_api::ffi::ncvisual_options#structfield.pxoffy
/// [`pxoffx`]: crate::c_api::ffi::ncvisual_options#structfield.pxoffx
/// [`AddAlpha`]: NcVisualFlag#associatedconstant.AddAlpha
/// [`Childplane`]: NcVisualFlag#associatedconstant.Childplane
/// [`VerAligned`]:NcVisualFlag#associatedconstant.VerAligned
/// [`HorAligned`]: NcVisualFlag#associatedconstant.HorAligned
pub type NcVisualOptions = crate::c_api::ffi::ncvisual_options;

/// # Constructors
impl<'ncplane> NcVisualOptions {
    /// Returns a builder object for `NcVisualOptions`.
    pub fn builder() -> NcVisualOptionsBuilder<'ncplane> {
        NcVisualOptionsBuilder::default()
    }

    /// New `NcVisualOptions`.
    ///
    /// # Arguments
    ///
    /// * `plane` - an optional mutable pointer to an [`NcPlane`].
    ///
    /// * `scale` - An [`NcScale`] indicating how the source will be
    ///   stretched/scaled relative to the `NcPlane`.
    ///
    /// * `y` - if an `NcPlane` is provided in `plane` then this specifies where
    ///   the `NcVisual` will be on that plane in the *y* axis.
    ///
    ///   Otherwise it specifies where the created `NcPlane` will be placed
    ///   in the *y* axis, relative to the standard plane's origin.
    ///
    ///   If [`VerAligned`] is set, this will be interpreted as an [`NcAlign`]
    ///   value.
    ///
    /// * `x` - if an `NcPlane` is provided in `plane` then this specifies where
    ///   the `NcVisual` will be on that plane, in the *x* axis.
    ///
    ///   Otherwise it specifies where the created `NcPlane` will be placed,
    ///   in the *y* axis, relative to the standard plane's origin.
    ///
    ///   If [`HorAligned`] is set, this will be interpreted as an [`NcAlign`]
    ///   value.
    ///
    /// * `section_yx_lenyx` - The size of the rendered section.
    ///
    ///   `None` renders the entire visual, otherwise the provided tuple
    ///   (`y`, `x`, `len_y`, `len_x`) sets `[yx]` as the origin of the section
    ///   and `len_[yx]` as the its length on each respective dimension.
    ///
    /// * `cell_offset_yx` - Pixel offsets within the cell.
    ///
    ///   If [`NcBlitter::Pixel`] is used the bitmap will be drawn offset from
    ///   the upper-left cell’s origin by these amounts, otherwise this will be
    ///   ignored.
    ///
    ///   It is an error if either number exceeds the cell-pixel geometry in any
    ///   dimension (see [`NcVisualGeometry.cdim_yx`]).
    ///
    /// * `blitter` - [`NcBlitter`] glyph set to use for blitting.
    ///
    /// * `flags` - [`NcVisualFlag`].
    ///
    /// * `transcolor` - treats this color as transparent when the [`AddAlpha`]
    ///   flag is active
    ///
    /// # Notes
    ///
    /// If the [`Childplane`] flag is used then the `plane` is interpreted as
    /// the parent `NcPlane` of the new plane created for this
    /// [`NcVisual`][crate::NcVisual].
    ///
    /// [`NcAlign`]: crate::NcAlign
    /// [`NcPixelGeometry.cell_y`]: crate::NcPixelGeometry#structfield.cell_y
    /// [`NcPixelGeometry.cell_x`]: crate::NcPixelGeometry#structfield.cell_x
    /// [`NcVisualGeometry.cdim_yx`]: crate::NcVisualGeometry#structfield.cdim_yx
    /// [`AddAlpha`]: NcVisualFlag#associatedconstant.AddAlpha
    /// [`Childplane`]: NcVisualFlag#associatedconstant.Childplane
    /// [`VerAligned`]:NcVisualFlag#associatedconstant.VerALigned
    /// [`HorAligned`]: NcVisualFlag#associatedconstant.HorALigned
    pub fn new(
        plane: Option<&mut NcPlane>,
        scale: impl Into<NcScale>,
        y: i32,
        x: i32,
        section_yx_lenyx: Option<(u32, u32, u32, u32)>,
        cell_offset_yx: Option<(u32, u32)>,
        blitter: impl Into<NcBlitter>,
        flags: impl Into<NcVisualFlag>,
        transcolor: impl Into<NcRgba>,
    ) -> Self {
        let plane_ptr = if let Some(p) = plane { p } else { null_mut() };
        let (begy, begx, leny, lenx) =
            if let Some(s) = section_yx_lenyx { (s.0, s.1, s.2, s.3) } else { (0, 0, 0, 0) };
        let (pxoffy, pxoffx) = if let Some(o) = cell_offset_yx { (o.0, o.1) } else { (0, 0) };

        Self {
            n: plane_ptr,
            scaling: scale.into().into(),

            y,
            x,

            begy,
            begx,
            leny,
            lenx,

            blitter: blitter.into().into(),

            flags: flags.into().into(),

            transcolor: transcolor.into().into(),

            pxoffy,
            pxoffx,
        }
    }
}

/// # Methods
impl<'ncplane> NcVisualOptions {
    /// Returns `true` if it does have an associated [`NcPlane`].
    pub fn does_plane(&self) -> bool {
        !self.n.is_null()
    }

    /// Returns `true` if it has the [`ChildPlane`] flag set.
    ///
    /// [`ChildPlane`]: NcVisualFlag#associatedconstant.ChildPlane
    pub fn does_child_plane(&self) -> bool {
        self.flags & NcVisualFlag::ChildPlane != NcVisualFlag::None
    }

    /// Returns `true` if it has the [`AddAlpha`] flag set.
    ///
    /// [`AddAlpha`]: NcVisualFlag#associatedconstant.AddAlpha
    pub fn does_alpha(&self) -> bool {
        self.flags & NcVisualFlag::AddAlpha != NcVisualFlag::None
    }

    /// Returns `true` if it has the [`Blend`] flag set.
    ///
    /// [`Blend`]: NcVisualFlag#associatedconstant.Blend
    pub fn does_blend(&self) -> bool {
        self.flags & NcVisualFlag::Blend != NcVisualFlag::None
    }

    /// Returns `false` if it has the [`NoDegrade`] flag set.
    ///
    /// [`NoDegrade`]: NcVisualFlag#associatedconstant.NoDegrade
    pub fn does_degrade(&self) -> bool {
        self.flags & NcVisualFlag::NoDegrade == NcVisualFlag::None
    }

    /// Returns `false` if it has the [`NoInterpolate`] flag set.
    ///
    /// [`NoInterpolate`]: NcVisualFlag#associatedconstant.NoInterpolate
    pub fn does_interpolate(&self) -> bool {
        self.flags & NcVisualFlag::NoInterpolate == NcVisualFlag::None
    }

    /// Returns `true` if it has the [`VerAligned`] flag set.
    ///
    /// [`VerAligned`]: NcVisualFlag#associatedconstant.VerAligned
    pub fn is_veraligned(&self) -> bool {
        self.flags & NcVisualFlag::VerAligned != NcVisualFlag::None
    }

    /// Returns `true` if it has the [`HorAligned`] flag set.
    ///
    /// [`HorAligned`]: NcVisualFlag#associatedconstant.HorAligned
    pub fn is_horaligned(&self) -> bool {
        self.flags & NcVisualFlag::HorAligned != NcVisualFlag::None
    }
}

/// A bitmask of flags for [`NcVisualOptions`].
///
/// # Flag
/// - [`None`][NcVisualFlag::None]
/// - [`AddAlpha`][NcVisualFlag::AddAlpha]
/// - [`Blend`][NcVisualFlag::Blend]
/// - [`ChildPlane`][NcVisualFlag::ChildPlane]
/// - [`NoDegrade`][NcVisualFlag::NoDegrade]
/// - [`HorAligned`][NcVisualFlag::HorAligned]
/// - [`VerAligned`][NcVisualFlag::VerAligned]
/// - [`NoInterpolate`][NcVisualFlag::NoInterpolate]
///
/// # Default
/// *[`NcVisualFlag::None`]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NcVisualFlag(pub c_api::NcVisualFlag_u64);

impl NcVisualFlag {
    /// No flags.
    pub const None: Self = Self(0);

    /// Treats as transparent the color specified in the `transcolor` field.
    pub const AddAlpha: Self = Self(c_api::NCVISUAL_OPTION_ADDALPHA);

    /// Uses [`NcAlpha::Blend`] with the `NcVisual`.
    ///
    /// [`NcAlpha::Blend`]: crate::NcAlpha#associatedconstant.Blend
    pub const Blend: Self = Self(c_api::NCVISUAL_OPTION_BLEND);

    /// allows you to indicate that the n field of ncvisual_options refers not to
    /// the plane onto which you'd like to blit, but the parent of a new plane.
    ///
    /// A plane will be created using the other parameters in the ncvisual_options,
    /// as a child of this parent. This means things like, say, vertically centering
    /// a sprixel relative to the standard plane can be done in one step.
    pub const ChildPlane: Self = Self(c_api::NCVISUAL_OPTION_CHILDPLANE);

    /// Fails rather than gracefully degrade. See [`NcBlitter`][crate::NcBlitter].
    pub const NoDegrade: Self = Self(c_api::NCVISUAL_OPTION_NODEGRADE);

    /// Y is an alignment, not absolute.
    pub const VerAligned: Self = Self(c_api::NCVISUAL_OPTION_VERALIGNED);

    /// X is an alignment, not absolute.
    pub const HorAligned: Self = Self(c_api::NCVISUAL_OPTION_HORALIGNED);

    /// Uses non-interpolative scaling.
    pub const NoInterpolate: Self = Self(c_api::NCVISUAL_OPTION_NOINTERPOLATE);
}

mod std_impls {
    use super::{c_api::NcVisualFlag_u64, NcVisualFlag};

    impl Default for NcVisualFlag {
        fn default() -> Self {
            Self::None
        }
    }

    crate::from_primitive![NcVisualFlag, NcVisualFlag_u64];
    crate::unit_impl_from![NcVisualFlag, NcVisualFlag_u64];
    crate::unit_impl_ops![bitwise; NcVisualFlag, NcVisualFlag_u64];
    crate::unit_impl_fmt![bases+display; NcVisualFlag];
}

pub(crate) mod c_api {
    use super::ffi;

    pub type NcVisualFlag_u64 = u64;

    /// [`NcVisualFlag_u64`] flag to treat as transparent the color specified
    /// in the `transcolor` field.
    pub const NCVISUAL_OPTION_ADDALPHA: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_ADDALPHA as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag uses [`NcAlpha::Blend`] with the `NcVisual`.
    ///
    /// [`NcAlpha::Blend`]: crate::NcAlpha#associatedconstant.Blend
    pub const NCVISUAL_OPTION_BLEND: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_BLEND as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag to indicate that the `n` field of
    /// `ncvisual_options` refers not to the plane onto which you'd like to blit,
    /// but the parent of a new plane.
    ///
    /// A plane will be created using the other parameters in the ncvisual_options,
    /// as a child of this parent. This means things like, say, vertically centering
    /// a sprixel relative to the standard plane can be done in one step.
    pub const NCVISUAL_OPTION_CHILDPLANE: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_CHILDPLANE as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag to fail rather than gracefully degrade.
    ///
    /// See [`NcBlitter`][crate::NcBlitter].
    pub const NCVISUAL_OPTION_NODEGRADE: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_NODEGRADE as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag to indicate Y is an alignment, not absolute.
    pub const NCVISUAL_OPTION_VERALIGNED: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_VERALIGNED as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag to indicate X is an alignment, not absolute.
    pub const NCVISUAL_OPTION_HORALIGNED: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_HORALIGNED as NcVisualFlag_u64;

    /// [`NcVisualFlag_u64`] flag to use non-interpolative scaling.
    pub const NCVISUAL_OPTION_NOINTERPOLATE: NcVisualFlag_u64 =
        ffi::NCVISUAL_OPTION_NOINTERPOLATE as NcVisualFlag_u64;
}