use std::path::PathBuf;

use crate::{
    NodeId, PixelCoordinate, PixelDimension,
    color::Color,
    node::{GenericNode, GenericNodeInfo},
};

/// A group node.
#[derive(
    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct GroupNode {
    #[serde(flatten)]
    generic: GenericNode,
    #[serde(skip_serializing_if = "Option::is_none")]
    label: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(flatten)]
    background: Option<Background>,
}

impl GroupNode {
    /// Creates a new [`GroupNode`].
    #[allow(clippy::too_many_arguments)]
    #[must_use]
    pub const fn new(
        id: NodeId,
        x: PixelCoordinate,
        y: PixelCoordinate,
        width: PixelDimension,
        height: PixelDimension,
        color: Option<Color>,
        label: Option<String>,
        background: Option<Background>,
    ) -> Self {
        Self {
            generic: GenericNode::new(id, x, y, width, height, color),
            label,
            background,
        }
    }

    /// Returns the label of this [`GroupNode`].
    #[must_use]
    pub const fn label(&self) -> Option<&String> {
        self.label.as_ref()
    }

    /// Returns the background of this [`GroupNode`].
    #[must_use]
    pub const fn background(&self) -> Option<&Background> {
        self.background.as_ref()
    }
}

impl GenericNodeInfo for GroupNode {
    fn id(&self) -> &NodeId {
        self.generic.id()
    }

    fn x(&self) -> PixelCoordinate {
        self.generic.x()
    }

    fn y(&self) -> PixelCoordinate {
        self.generic.y()
    }

    fn width(&self) -> PixelDimension {
        self.generic.width()
    }

    fn height(&self) -> PixelDimension {
        self.generic.height()
    }

    fn color(&self) -> &Option<Color> {
        self.generic.color()
    }
}

/// The background attributes of a [`GroupNode`].
#[derive(
    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub struct Background {
    image: PathBuf,
    #[serde(skip_serializing_if = "Option::is_none")]
    background_style: Option<BackgroundStyle>,
}

impl Background {
    /// Creates a new [`Background`].
    #[must_use]
    pub const fn new(image: PathBuf, background_style: Option<BackgroundStyle>) -> Self {
        Self {
            image,
            background_style,
        }
    }

    /// Returns a reference to the image of this [`Background`].
    #[must_use]
    pub const fn image(&self) -> &PathBuf {
        &self.image
    }

    /// Returns the style of this [`Background`].
    #[must_use]
    pub const fn style(&self) -> Option<BackgroundStyle> {
        self.background_style
    }
}

/// The rendering style of the background image.
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
#[serde(rename_all = "camelCase")]
pub enum BackgroundStyle {
    /// Fills the entire width and height of the node.
    Cover,
    /// Maintains the aspect ratio of the background image.
    Ratio,
    /// Repeats the image as a pattern in both x/y directions.
    Repeat,
}
