// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Software-3.0

#![doc = include_str!("README.md")]
#![no_std]
#![forbid(unsafe_code)]

/// An RGBA color, as held by properties of the `color` type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Color {
    red: u8,
    green: u8,
    blue: u8,
    alpha: u8,
}

impl Color {
    /// Construct a color from its ARGB value, e.g. `0xff123456`.
    pub const fn from_argb_encoded(argb: u32) -> Self {
        let [alpha, red, green, blue] = argb.to_be_bytes();
        Self { red, green, blue, alpha }
    }

    /// Construct a fully opaque color from its red, green, and blue channels.
    pub const fn from_rgb_u8(red: u8, green: u8, blue: u8) -> Self {
        Self { red, green, blue, alpha: 0xff }
    }

    pub const fn red(self) -> u8 {
        self.red
    }

    pub const fn green(self) -> u8 {
        self.green
    }

    pub const fn blue(self) -> u8 {
        self.blue
    }

    pub const fn alpha(self) -> u8 {
        self.alpha
    }
}

#[test]
fn test_color() {
    let c = Color::from_argb_encoded(0x87123456);
    assert_eq!((c.red(), c.green(), c.blue(), c.alpha()), (0x12, 0x34, 0x56, 0x87));
    assert_eq!(Color::from_rgb_u8(0x12, 0x34, 0x56).alpha(), 0xff);
    // The default color is transparent
    assert_eq!(Color::default().alpha(), 0);
}

/// Error returned by the generated render functions.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum RenderError {
    /// The frame buffer size doesn't match the requested width and height.
    InvalidFrameBufferSize,
}

impl core::fmt::Display for RenderError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::InvalidFrameBufferSize => {
                f.write_str("the frame buffer size doesn't match the requested width and height")
            }
        }
    }
}

impl core::error::Error for RenderError {}

#[test]
fn test_render_error_display() {
    use core::fmt::Write;
    // A no_std, no_alloc sink to capture the Display output.
    struct Sink {
        buf: [u8; 80],
        len: usize,
    }
    impl Write for Sink {
        fn write_str(&mut self, s: &str) -> core::fmt::Result {
            let end = self.len + s.len();
            self.buf[self.len..end].copy_from_slice(s.as_bytes());
            self.len = end;
            Ok(())
        }
    }
    let mut sink = Sink { buf: [0; 80], len: 0 };
    write!(sink, "{}", RenderError::InvalidFrameBufferSize).unwrap();
    assert_eq!(
        core::str::from_utf8(&sink.buf[..sink.len]).unwrap(),
        "the frame buffer size doesn't match the requested width and height"
    );
}

/// Module only meant to be used by the code generated by the Slint SC compiler.
#[doc(hidden)]
pub mod private_unstable_api {
    pub mod renderer;
}
