use crate::{
    composite_renderer::{Command, Effect, Image, Renderable, Text},
    math::{Mat2d, Rect, Vec2},
    mesh_animation_asset_protocol::{MeshAnimation, MeshAnimationSequence},
    mesh_asset_protocol::MeshBone,
    resource::{CompositeUiInteractibles, CompositeUiThemes, UiThemed, UiValue, UiValueVec2},
};
use core::{
    ecs::{Component, DenseVecStorage, FlaggedStorage, HashMapStorage, VecStorage},
    prefab::{Prefab, PrefabComponent},
    Ignite, Scalar,
};
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "scalar64"))]
use std::f32::consts::PI;
#[cfg(feature = "scalar64")]
use std::f64::consts::PI;
use std::{borrow::Cow, collections::HashMap};
use utils::grid_2d::Grid2d;
#[derive(Ignite, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct CompositeVisibility(pub bool);
impl Component for CompositeVisibility {
    type Storage = VecStorage<Self>;
}
impl Default for CompositeVisibility {
    fn default() -> Self {
        Self(true)
    }
}
impl Prefab for CompositeVisibility {}
impl PrefabComponent for CompositeVisibility {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeSurfaceCache {
    name: Cow<'static, str>,
    width: usize,
    height: usize,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeSurfaceCache {
    pub fn new(name: Cow<'static, str>, width: usize, height: usize) -> Self {
        Self {
            name,
            width,
            height,
            dirty: true,
        }
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn width(&self) -> usize {
        self.width
    }
    pub fn set_width(&mut self, width: usize) {
        self.width = width;
        self.dirty = true;
    }
    pub fn height(&self) -> usize {
        self.height
    }
    pub fn set_height(&mut self, height: usize) {
        self.height = height;
        self.dirty = true;
    }
    pub fn rebuild(&mut self) {
        self.dirty = true;
    }
    pub fn is_cached(&self) -> bool {
        !self.dirty
    }
}
impl Component for CompositeSurfaceCache {
    type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
impl Prefab for CompositeSurfaceCache {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeSurfaceCache {}
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub struct CompositeRenderable(pub Renderable<'static>);
impl Component for CompositeRenderable {
    type Storage = DenseVecStorage<Self>;
}
impl Default for CompositeRenderable {
    fn default() -> Self {
        Self(().into())
    }
}
impl From<Renderable<'static>> for CompositeRenderable {
    fn from(value: Renderable<'static>) -> Self {
        Self(value)
    }
}
impl Prefab for CompositeRenderable {}
impl PrefabComponent for CompositeRenderable {}
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub struct CompositeRenderableStroke(pub Scalar);
impl Default for CompositeRenderableStroke {
    fn default() -> Self {
        Self(1.0)
    }
}
impl Component for CompositeRenderableStroke {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeRenderableStroke {}
impl PrefabComponent for CompositeRenderableStroke {}
#[derive(Ignite, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CompositeTransform {
    #[serde(default)]
    translation: Vec2,
    #[serde(default)]
    rotation: Scalar,
    #[serde(default = "CompositeTransform::default_scale")]
    scale: Vec2,
    #[serde(skip)]
    #[ignite(ignore)]
    cached: Mat2d,
}
impl Component for CompositeTransform {
    type Storage = DenseVecStorage<Self>;
}
impl Default for CompositeTransform {
    fn default() -> Self {
        Self {
            translation: Vec2::zero(),
            rotation: 0.0,
            scale: Vec2::one(),
            cached: Default::default(),
        }
    }
}
impl CompositeTransform {
    fn default_scale() -> Vec2 {
        1.0.into()
    }
    pub fn new(translation: Vec2, rotation: Scalar, scale: Vec2) -> Self {
        let mut result = Self {
            translation,
            rotation,
            scale,
            cached: Default::default(),
        };
        result.rebuild();
        result
    }
    pub fn translation(v: Vec2) -> Self {
        Self::default().with_translation(v)
    }
    pub fn rotation(v: Scalar) -> Self {
        Self::default().with_rotation(v)
    }
    pub fn scale(v: Vec2) -> Self {
        Self::default().with_scale(v)
    }
    pub fn with_translation(mut self, v: Vec2) -> Self {
        self.translation = v;
        self.rebuild();
        self
    }
    pub fn with_rotation(mut self, v: Scalar) -> Self {
        self.rotation = v;
        self.rebuild();
        self
    }
    pub fn with_scale(mut self, v: Vec2) -> Self {
        self.scale = v;
        self.rebuild();
        self
    }
    pub fn get_translation(&self) -> Vec2 {
        self.translation
    }
    pub fn get_rotation(&self) -> Scalar {
        self.rotation
    }
    pub fn get_direction(&self) -> Vec2 {
        let (y, x) = self.rotation.sin_cos();
        Vec2::new(x, y)
    }
    pub fn get_scale(&self) -> Vec2 {
        self.scale
    }
    pub fn set_translation(&mut self, v: Vec2) {
        self.translation = v;
        self.rebuild();
    }
    pub fn set_rotation(&mut self, v: Scalar) {
        self.rotation = v;
        self.rebuild();
    }
    pub fn set_scale(&mut self, v: Vec2) {
        self.scale = v;
        self.rebuild();
    }
    pub fn apply(&mut self, translation: Vec2, rotation: Scalar, scale: Vec2) {
        self.translation = translation;
        self.rotation = rotation;
        self.scale = scale;
        self.rebuild();
    }
    pub fn matrix(&self) -> Mat2d {
        self.cached
    }
    fn rebuild(&mut self) {
        let t = Mat2d::translation(self.translation);
        let r = Mat2d::rotation(self.rotation);
        let s = Mat2d::scale(self.scale);
        self.cached = t * r * s;
    }
}
impl Prefab for CompositeTransform {
    fn post_from_prefab(&mut self) {
        self.rebuild();
    }
}
impl PrefabComponent for CompositeTransform {}
#[derive(Ignite, Debug, Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CompositeRenderLayer(pub usize);
impl Component for CompositeRenderLayer {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeRenderLayer {}
impl PrefabComponent for CompositeRenderLayer {}
#[derive(Ignite, Debug, Default, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CompositeRenderDepth(pub Scalar);
impl Component for CompositeRenderDepth {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeRenderDepth {}
impl PrefabComponent for CompositeRenderDepth {}
#[derive(Ignite, Debug, Copy, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct CompositeRenderAlpha(pub Scalar);
impl Component for CompositeRenderAlpha {
    type Storage = VecStorage<Self>;
}
impl Default for CompositeRenderAlpha {
    fn default() -> Self {
        Self(1.0)
    }
}
impl Prefab for CompositeRenderAlpha {}
impl PrefabComponent for CompositeRenderAlpha {}
#[derive(Ignite, Debug, Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct CompositeCameraAlignment(pub Vec2);
impl Component for CompositeCameraAlignment {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeCameraAlignment {}
impl PrefabComponent for CompositeCameraAlignment {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeEffect(pub Effect);
impl Component for CompositeEffect {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeEffect {}
impl PrefabComponent for CompositeEffect {}
#[derive(Ignite, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum CompositeScalingMode {
    None,
    Center,
    Aspect,
    CenterAspect,
}
impl Default for CompositeScalingMode {
    fn default() -> Self {
        CompositeScalingMode::None
    }
}
#[derive(Ignite, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum CompositeScalingTarget {
    Width,
    Height,
    Both,
    BothMinimum,
    Cover(Scalar, Scalar),
}
impl Default for CompositeScalingTarget {
    fn default() -> Self {
        CompositeScalingTarget::Both
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeCamera {
    #[serde(default)]
    pub scaling: CompositeScalingMode,
    #[serde(default)]
    pub scaling_target: CompositeScalingTarget,
    #[serde(default)]
    pub tags: Vec<Cow<'static, str>>,
}
impl Component for CompositeCamera {
    type Storage = HashMapStorage<Self>;
}
impl CompositeCamera {
    pub fn new(scaling: CompositeScalingMode) -> Self {
        Self {
            scaling,
            scaling_target: CompositeScalingTarget::default(),
            tags: vec![],
        }
    }
    pub fn with_scaling_target(
        scaling: CompositeScalingMode,
        target: CompositeScalingTarget,
    ) -> Self {
        Self {
            scaling,
            scaling_target: target,
            tags: vec![],
        }
    }
    pub fn tag(mut self, tag: Cow<'static, str>) -> Self {
        self.tags.push(tag);
        self
    }
    pub fn tags(mut self, tags: Vec<Cow<'static, str>>) -> Self {
        self.tags = tags;
        self
    }
    pub fn view_matrix(&self, transform: &CompositeTransform, screen_size: Vec2) -> Mat2d {
        let wh = screen_size.x * 0.5;
        let hh = screen_size.y * 0.5;
        let scale = match self.scaling_target {
            CompositeScalingTarget::Width => screen_size.x,
            CompositeScalingTarget::Height => screen_size.y,
            CompositeScalingTarget::Both => screen_size.x.min(screen_size.y),
            CompositeScalingTarget::BothMinimum => screen_size.x.max(screen_size.y),
            CompositeScalingTarget::Cover(width, height) => {
                (screen_size.x / width).max(screen_size.y / height)
            }
        };
        let s = Mat2d::scale(Vec2::one() / transform.get_scale());
        let ss = Mat2d::scale(Vec2::new(scale, scale) / transform.get_scale());
        let r = Mat2d::rotation(-transform.get_rotation());
        let t = Mat2d::translation(-transform.get_translation());
        let tt = Mat2d::translation([wh, hh].into());
        match self.scaling {
            CompositeScalingMode::None => s * r * t,
            CompositeScalingMode::Center => tt * s * r * t,
            CompositeScalingMode::Aspect => ss * r * t,
            CompositeScalingMode::CenterAspect => tt * ss * r * t,
        }
    }
    pub fn view_box(&self, transform: &CompositeTransform, screen_size: Vec2) -> Option<Rect> {
        if let Some(inv_mat) = !self.view_matrix(transform, screen_size) {
            let points = &[
                Vec2::zero() * inv_mat,
                Vec2::new(screen_size.x, 0.0) * inv_mat,
                screen_size * inv_mat,
                Vec2::new(0.0, screen_size.y) * inv_mat,
            ];
            Rect::bounding(points)
        } else {
            None
        }
    }
}
impl Prefab for CompositeCamera {}
impl PrefabComponent for CompositeCamera {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeSprite {
    #[serde(default)]
    pub alignment: Vec2,
    sheet_frame: Option<(Cow<'static, str>, Cow<'static, str>)>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeSprite {
    pub fn new(sheet: Cow<'static, str>, frame: Cow<'static, str>) -> Self {
        Self {
            alignment: 0.0.into(),
            sheet_frame: Some((sheet, frame)),
            dirty: true,
        }
    }
    pub fn align(mut self, value: Vec2) -> Self {
        self.alignment = value;
        self
    }
    pub fn sheet_frame(&self) -> Option<(&str, &str)> {
        if let Some((sheet, frame)) = &self.sheet_frame {
            Some((sheet, frame))
        } else {
            None
        }
    }
    pub fn set_sheet_frame(&mut self, sheet_frame: Option<(Cow<'static, str>, Cow<'static, str>)>) {
        self.sheet_frame = sheet_frame;
        self.dirty = true;
    }
    pub fn sheet(&self) -> Option<&str> {
        if let Some((sheet, _)) = &self.sheet_frame {
            Some(sheet)
        } else {
            None
        }
    }
    pub fn set_sheet(&mut self, sheet: Option<Cow<'static, str>>) {
        if let Some(sheet) = sheet {
            if let Some(sheet_frame) = &mut self.sheet_frame {
                sheet_frame.0 = sheet;
            } else {
                self.sheet_frame = Some((sheet, "".into()));
            }
        } else {
            self.sheet_frame = None;
        }
    }
    pub fn frame(&self) -> Option<&str> {
        if let Some((_, frame)) = &self.sheet_frame {
            Some(frame)
        } else {
            None
        }
    }
    pub fn set_frame(&mut self, frame: Option<Cow<'static, str>>) {
        if let Some(frame) = frame {
            if let Some(sheet_frame) = &mut self.sheet_frame {
                sheet_frame.1 = frame;
            } else {
                self.sheet_frame = Some(("".into(), frame));
            }
        } else {
            self.sheet_frame = None;
        }
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
}
impl Component for CompositeSprite {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeSprite {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeSprite {}
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub struct SpriteAnimation {
    pub sheet: Cow<'static, str>,
    pub frames: Vec<Cow<'static, str>>,
}
impl SpriteAnimation {
    pub fn new(sheet: Cow<'static, str>, frames: Vec<Cow<'static, str>>) -> Self {
        Self { sheet, frames }
    }
}
impl From<(Cow<'static, str>, Vec<Cow<'static, str>>)> for SpriteAnimation {
    fn from((sheet, frames): (Cow<'static, str>, Vec<Cow<'static, str>>)) -> Self {
        Self::new(sheet, frames)
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeSpriteAnimation {
    pub animations: HashMap<Cow<'static, str>, SpriteAnimation>,
    
    #[serde(default)]
    pub(crate) current: Option<(Cow<'static, str>, Scalar, Scalar, bool)>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeSpriteAnimation {
    pub fn new(animations: HashMap<Cow<'static, str>, SpriteAnimation>) -> Self {
        Self {
            animations,
            current: None,
            dirty: false,
        }
    }
    #[allow(clippy::should_implement_trait)]
    pub fn from_iter<I>(animations: I) -> Self
    where
        I: IntoIterator<Item = (Cow<'static, str>, SpriteAnimation)>,
    {
        Self {
            animations: animations.into_iter().collect::<HashMap<_, _>>(),
            current: None,
            dirty: false,
        }
    }
    pub fn autoplay(mut self, name: &str, speed: Scalar, looped: bool) -> Self {
        self.play(name, speed, looped);
        self
    }
    pub fn play(&mut self, name: &str, speed: Scalar, looped: bool) -> bool {
        if self.animations.contains_key(name) {
            self.current = Some((name.to_owned().into(), 0.0, speed, looped));
            self.dirty = true;
            true
        } else {
            self.current = None;
            false
        }
    }
    pub fn stop(&mut self) {
        self.current = None;
    }
    pub fn is_playing(&self) -> bool {
        self.current.is_some()
    }
    pub fn current(&self) -> Option<&str> {
        if let Some((name, _, _, _)) = &self.current {
            Some(name)
        } else {
            None
        }
    }
    pub fn phase(&self) -> Option<Scalar> {
        if let Some((_, phase, _, _)) = &self.current {
            Some(*phase)
        } else {
            None
        }
    }
    pub fn set_phase(&mut self, value: Scalar) -> bool {
        if let Some((_, phase, _, _)) = &mut self.current {
            *phase = value.max(0.0);
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn speed(&self) -> Option<Scalar> {
        if let Some((_, _, speed, _)) = &self.current {
            Some(*speed)
        } else {
            None
        }
    }
    pub fn set_speed(&mut self, value: Scalar) -> bool {
        if let Some((_, _, speed, _)) = &mut self.current {
            *speed = value;
            true
        } else {
            false
        }
    }
    pub fn looped(&self) -> Option<bool> {
        if let Some((_, _, _, looped)) = &self.current {
            Some(*looped)
        } else {
            None
        }
    }
    pub fn set_looped(&mut self, value: bool) -> bool {
        if let Some((_, _, _, looped)) = &mut self.current {
            *looped = value;
            true
        } else {
            false
        }
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
    pub(crate) fn process(&mut self, delta_time: Scalar) {
        if let Some((name, phase, speed, looped)) = &mut self.current {
            if let Some(animation) = self.animations.get(name) {
                let prev = phase.max(0.0) as usize;
                *phase += *speed * delta_time;
                let next = phase.max(0.0) as usize;
                if next >= animation.frames.len() {
                    if *looped {
                        *phase = 0.0;
                        self.dirty = true;
                    } else {
                        self.current = None;
                    }
                } else if prev != next {
                    self.dirty = true;
                }
            }
        }
    }
}
impl Component for CompositeSpriteAnimation {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeSpriteAnimation {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeSpriteAnimation {}
#[derive(Ignite, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TileRotate {
    Degrees0,
    Degrees90,
    Degrees180,
    Degrees270,
}
impl Default for TileRotate {
    fn default() -> Self {
        TileRotate::Degrees0
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct TileCell {
    pub col: usize,
    pub row: usize,
    #[serde(default)]
    pub flip_x: bool,
    #[serde(default)]
    pub flip_y: bool,
    #[serde(default)]
    pub rotate: TileRotate,
    #[serde(default)]
    pub visible: bool,
}
impl TileCell {
    pub fn new(col: usize, row: usize) -> Self {
        Self {
            col,
            row,
            flip_x: false,
            flip_y: false,
            rotate: Default::default(),
            visible: true,
        }
    }
    pub fn flip(mut self, x: bool, y: bool) -> Self {
        self.flip_x = x;
        self.flip_y = y;
        self
    }
    pub fn rotate(mut self, value: TileRotate) -> Self {
        self.rotate = value;
        self
    }
    pub fn visible(mut self, value: bool) -> Self {
        self.visible = value;
        self
    }
    #[allow(clippy::many_single_char_names)]
    pub fn matrix(&self, col: usize, row: usize, width: Scalar, height: Scalar) -> Mat2d {
        let hw = width * 0.5;
        let hh = height * 0.5;
        let a = Mat2d::translation([-hw, -hh].into());
        let sx = if self.flip_x { -1.0 } else { 1.0 };
        let sy = if self.flip_y { -1.0 } else { 1.0 };
        let b = Mat2d::scale([sx, sy].into());
        let c = match self.rotate {
            TileRotate::Degrees0 => Mat2d::default(),
            TileRotate::Degrees90 => Mat2d::rotation(PI * 0.5),
            TileRotate::Degrees180 => Mat2d::rotation(PI),
            TileRotate::Degrees270 => Mat2d::rotation(PI * 1.5),
        };
        let d = Mat2d::translation([hw, hh].into());
        let e = Mat2d::translation([col as Scalar * width, row as Scalar * height].into());
        e * d * c * b * a
    }
}
impl From<(usize, usize)> for TileCell {
    fn from((col, row): (usize, usize)) -> Self {
        Self::new(col, row)
    }
}
impl From<(usize, usize, bool)> for TileCell {
    fn from((col, row, visible): (usize, usize, bool)) -> Self {
        Self::new(col, row).visible(visible)
    }
}
impl From<(usize, usize, bool, bool)> for TileCell {
    fn from((col, row, flip_x, flip_y): (usize, usize, bool, bool)) -> Self {
        Self::new(col, row).flip(flip_x, flip_y)
    }
}
impl From<(usize, usize, bool, bool, bool)> for TileCell {
    fn from((col, row, flip_x, flip_y, visible): (usize, usize, bool, bool, bool)) -> Self {
        Self::new(col, row).flip(flip_x, flip_y).visible(visible)
    }
}
impl From<(usize, usize, TileRotate)> for TileCell {
    fn from((col, row, rotate): (usize, usize, TileRotate)) -> Self {
        Self::new(col, row).rotate(rotate)
    }
}
impl From<(usize, usize, TileRotate, bool)> for TileCell {
    fn from((col, row, rotate, visible): (usize, usize, TileRotate, bool)) -> Self {
        Self::new(col, row).rotate(rotate).visible(visible)
    }
}
impl From<(usize, usize, bool, bool, TileRotate)> for TileCell {
    fn from((col, row, flip_x, flip_y, rotate): (usize, usize, bool, bool, TileRotate)) -> Self {
        Self::new(col, row).flip(flip_x, flip_y).rotate(rotate)
    }
}
impl From<(usize, usize, bool, bool, TileRotate, bool)> for TileCell {
    fn from(
        (col, row, flip_x, flip_y, rotate, visible): (usize, usize, bool, bool, TileRotate, bool),
    ) -> Self {
        Self::new(col, row)
            .flip(flip_x, flip_y)
            .rotate(rotate)
            .visible(visible)
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeTilemap {
    tileset: Option<Cow<'static, str>>,
    grid: Grid2d<TileCell>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeTilemap {
    pub fn new(tileset: Cow<'static, str>, grid: Grid2d<TileCell>) -> Self {
        Self {
            tileset: Some(tileset),
            grid,
            dirty: true,
        }
    }
    pub fn tileset(&self) -> Option<&str> {
        if let Some(tileset) = &self.tileset {
            Some(tileset)
        } else {
            None
        }
    }
    pub fn set_tileset(&mut self, tileset: Option<Cow<'static, str>>) {
        self.tileset = tileset;
        self.dirty = true;
    }
    pub fn grid(&self) -> &Grid2d<TileCell> {
        &self.grid
    }
    pub fn grid_mut(&mut self) -> &mut Grid2d<TileCell> {
        self.dirty = true;
        &mut self.grid
    }
    pub fn set_grid(&mut self, grid: Grid2d<TileCell>) {
        self.grid = grid;
        self.dirty = true;
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
}
impl Component for CompositeTilemap {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeTilemap {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeTilemap {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct TilemapAnimation {
    pub tileset: Cow<'static, str>,
    pub frames: Vec<Grid2d<TileCell>>,
}
impl TilemapAnimation {
    pub fn new(tileset: Cow<'static, str>, frames: Vec<Grid2d<TileCell>>) -> Self {
        Self { tileset, frames }
    }
}
impl From<(Cow<'static, str>, Vec<Grid2d<TileCell>>)> for TilemapAnimation {
    fn from((tileset, frames): (Cow<'static, str>, Vec<Grid2d<TileCell>>)) -> Self {
        Self::new(tileset, frames)
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeTilemapAnimation {
    pub animations: HashMap<Cow<'static, str>, TilemapAnimation>,
    
    #[serde(default)]
    pub(crate) current: Option<(Cow<'static, str>, Scalar, Scalar, bool)>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeTilemapAnimation {
    pub fn new(animations: HashMap<Cow<'static, str>, TilemapAnimation>) -> Self {
        Self {
            animations,
            current: None,
            dirty: false,
        }
    }
    #[allow(clippy::should_implement_trait)]
    pub fn from_iter<I>(animations: I) -> Self
    where
        I: IntoIterator<Item = (Cow<'static, str>, TilemapAnimation)>,
    {
        Self {
            animations: animations.into_iter().collect::<HashMap<_, _>>(),
            current: None,
            dirty: false,
        }
    }
    pub fn play(&mut self, name: &str, speed: Scalar, looped: bool) -> bool {
        if self.animations.contains_key(name) {
            self.current = Some((name.to_owned().into(), 0.0, speed, looped));
            self.dirty = true;
            true
        } else {
            self.current = None;
            false
        }
    }
    pub fn stop(&mut self) {
        self.current = None;
    }
    pub fn is_playing(&self) -> bool {
        self.current.is_some()
    }
    pub fn current(&self) -> Option<&str> {
        if let Some((name, _, _, _)) = &self.current {
            Some(name)
        } else {
            None
        }
    }
    pub fn phase(&self) -> Option<Scalar> {
        if let Some((_, phase, _, _)) = &self.current {
            Some(*phase)
        } else {
            None
        }
    }
    pub fn set_phase(&mut self, value: Scalar) -> bool {
        if let Some((_, phase, _, _)) = &mut self.current {
            *phase = value.max(0.0);
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn speed(&self) -> Option<Scalar> {
        if let Some((_, _, speed, _)) = &self.current {
            Some(*speed)
        } else {
            None
        }
    }
    pub fn set_speed(&mut self, value: Scalar) -> bool {
        if let Some((_, _, speed, _)) = &mut self.current {
            *speed = value;
            true
        } else {
            false
        }
    }
    pub fn looped(&self) -> Option<bool> {
        if let Some((_, _, _, looped)) = &self.current {
            Some(*looped)
        } else {
            None
        }
    }
    pub fn set_looped(&mut self, value: bool) -> bool {
        if let Some((_, _, _, looped)) = &mut self.current {
            *looped = value;
            true
        } else {
            false
        }
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
    pub(crate) fn process(&mut self, delta_time: Scalar) {
        if let Some((name, phase, speed, looped)) = &mut self.current {
            if let Some(animation) = self.animations.get(name) {
                let prev = phase.max(0.0) as usize;
                *phase += *speed * delta_time;
                let next = phase.max(0.0) as usize;
                if next >= animation.frames.len() {
                    if *looped {
                        *phase = 0.0;
                        self.dirty = true;
                    } else {
                        self.current = None;
                    }
                } else if prev != next {
                    self.dirty = true;
                }
            }
        }
    }
}
impl Component for CompositeTilemapAnimation {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeTilemapAnimation {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeTilemapAnimation {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeMapChunk {
    map_name: Cow<'static, str>,
    layer_name: Cow<'static, str>,
    #[serde(default)]
    offset: (usize, usize),
    #[serde(default)]
    size: Option<(usize, usize)>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeMapChunk {
    pub fn new(map_name: Cow<'static, str>, layer_name: Cow<'static, str>) -> Self {
        Self {
            map_name,
            layer_name,
            offset: (0, 0),
            size: None,
            dirty: true,
        }
    }
    pub fn map_name(&self) -> &str {
        &self.map_name
    }
    pub fn set_map_name(&mut self, map_name: Cow<'static, str>) {
        self.map_name = map_name;
        self.dirty = true;
    }
    pub fn layer_name(&self) -> &str {
        &self.layer_name
    }
    pub fn set_layer_name(&mut self, layer_name: Cow<'static, str>) {
        self.layer_name = layer_name;
        self.dirty = true;
    }
    pub fn offset(&self) -> (usize, usize) {
        self.offset
    }
    pub fn set_offset(&mut self, offset: (usize, usize)) {
        self.offset = offset;
        self.dirty = true;
    }
    pub fn size(&self) -> Option<(usize, usize)> {
        self.size
    }
    pub fn set_size(&mut self, size: Option<(usize, usize)>) {
        self.size = size;
        self.dirty = true;
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
    pub fn is_cached(&self) -> bool {
        !self.dirty
    }
}
impl Component for CompositeMapChunk {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeMapChunk {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeMapChunk {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct MeshMaterial {
    pub image: Cow<'static, str>,
    #[serde(default = "MeshMaterial::default_alpha")]
    pub alpha: Scalar,
    #[serde(default)]
    pub order: Scalar,
}
impl MeshMaterial {
    fn default_alpha() -> Scalar {
        1.0
    }
}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeMesh {
    mesh: Cow<'static, str>,
    materials: Vec<MeshMaterial>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) bones_local_transform: HashMap<String, CompositeTransform>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) bones_model_space: HashMap<String, Mat2d>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty_mesh: bool,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty_visuals: bool,
}
impl CompositeMesh {
    pub fn new(mesh: Cow<'static, str>, materials: Vec<MeshMaterial>) -> Self {
        Self {
            mesh,
            materials,
            bones_local_transform: Default::default(),
            bones_model_space: Default::default(),
            dirty_mesh: true,
            dirty_visuals: true,
        }
    }
    pub fn mesh(&self) -> &str {
        &self.mesh
    }
    pub fn set_mesh(&mut self, mesh: Cow<'static, str>) {
        self.mesh = mesh;
        self.dirty_mesh = true;
        self.dirty_visuals = true;
    }
    pub fn materials(&self) -> &[MeshMaterial] {
        &self.materials
    }
    pub fn materials_mut(&mut self) -> &mut [MeshMaterial] {
        &mut self.materials
    }
    pub fn set_materials(&mut self, materials: Vec<MeshMaterial>) {
        self.materials = materials;
        self.dirty_visuals = true;
    }
    pub fn with_materials<F>(&mut self, mut f: F)
    where
        F: FnMut(&mut Vec<MeshMaterial>),
    {
        f(&mut self.materials);
        self.dirty_visuals = true;
    }
    pub fn bone_local_transform(&self, name: &str) -> Option<&CompositeTransform> {
        self.bones_local_transform.get(name)
    }
    pub fn bone_local_transform_mut(&mut self, name: &str) -> Option<&mut CompositeTransform> {
        self.bones_local_transform.get_mut(name)
    }
    pub fn set_bone_local_transform(&mut self, name: &str, transform: CompositeTransform) {
        if let Some(t) = self.bones_local_transform.get_mut(name) {
            *t = transform;
            self.dirty_visuals = true;
        }
    }
    pub fn with_bone_local_transform<F>(&mut self, name: &str, mut f: F)
    where
        F: FnMut(&mut CompositeTransform),
    {
        if let Some(t) = self.bones_local_transform.get_mut(name) {
            f(t);
            self.dirty_visuals = true;
        }
    }
    pub fn apply(&mut self) {
        self.dirty_visuals = true;
    }
    pub(crate) fn rebuild_model_space(&mut self, root: &MeshBone) {
        self.rebuild_bone_model_space(root, "", Mat2d::default())
    }
    fn rebuild_bone_model_space(&mut self, bone: &MeshBone, name: &str, parent: Mat2d) {
        if let Some(t) = self.bones_local_transform.get(name) {
            let m = parent * t.matrix();
            self.bones_model_space.insert(name.to_owned(), m);
            for (name, bone) in &bone.children {
                self.rebuild_bone_model_space(bone, name, m);
            }
        }
    }
    pub(crate) fn setup_bones_from_rig(&mut self, root: &MeshBone) {
        let count = root.bones_count();
        let mut bones_local_transform = HashMap::with_capacity(count);
        let mut bones_model_space = HashMap::with_capacity(count);
        Self::register_bone(
            root,
            "",
            Mat2d::default(),
            &mut bones_local_transform,
            &mut bones_model_space,
        );
        self.bones_local_transform = bones_local_transform;
        self.bones_model_space = bones_model_space;
    }
    fn register_bone(
        bone: &MeshBone,
        name: &str,
        parent: Mat2d,
        bones_local_transform: &mut HashMap<String, CompositeTransform>,
        bones_model_space: &mut HashMap<String, Mat2d>,
    ) {
        bones_local_transform.insert(name.to_owned(), bone.transform.clone());
        let m = parent * bone.transform.matrix();
        bones_model_space.insert(name.to_owned(), m);
        for (name, bone) in &bone.children {
            Self::register_bone(bone, name, m, bones_local_transform, bones_model_space);
        }
    }
}
impl Component for CompositeMesh {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeMesh {
    fn post_from_prefab(&mut self) {
        self.dirty_mesh = true;
        self.dirty_visuals = true;
    }
}
impl PrefabComponent for CompositeMesh {}
#[derive(Ignite, Debug, Default, Clone, Serialize, Deserialize)]
pub struct CompositeMeshAnimation {
    animation: Cow<'static, str>,
    
    #[serde(default)]
    pub(crate) current: HashMap<String, (Scalar, Scalar, bool, Scalar)>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl CompositeMeshAnimation {
    pub fn animation(&self) -> &str {
        &self.animation
    }
    pub fn autoplay(
        mut self,
        name: &str,
        speed: Scalar,
        looped: bool,
        blend_weight: Scalar,
    ) -> Self {
        self.play(name, speed, looped, blend_weight);
        self
    }
    pub fn play(&mut self, name: &str, speed: Scalar, looped: bool, blend_weight: Scalar) {
        self.current
            .insert(name.to_owned(), (0.0, speed, looped, blend_weight));
        self.dirty = true;
    }
    pub fn stop(&mut self, name: &str) {
        self.current.remove(name);
    }
    pub fn is_playing_any(&self) -> bool {
        !self.current.is_empty()
    }
    pub fn is_playing(&self, name: &str) -> bool {
        self.current.contains_key(name)
    }
    pub fn current(&self) -> impl Iterator<Item = &String> {
        self.current.keys()
    }
    pub fn phase(&self, name: &str) -> Option<Scalar> {
        self.current.get(name).map(|(phase, _, _, _)| *phase)
    }
    pub fn set_phase(&mut self, name: &str, value: Scalar) -> bool {
        if let Some((phase, _, _, _)) = self.current.get_mut(name) {
            *phase = value.max(0.0);
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn speed(&self, name: &str) -> Option<Scalar> {
        self.current.get(name).map(|(_, speed, _, _)| *speed)
    }
    pub fn set_speed(&mut self, name: &str, value: Scalar) -> bool {
        if let Some((_, speed, _, _)) = self.current.get_mut(name) {
            *speed = value;
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn looped(&self, name: &str) -> Option<bool> {
        self.current.get(name).map(|(_, _, looped, _)| *looped)
    }
    pub fn set_looped(&mut self, name: &str, value: bool) -> bool {
        if let Some((_, _, looped, _)) = self.current.get_mut(name) {
            *looped = value;
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn blend_weight(&self, name: &str) -> Option<Scalar> {
        self.current
            .get(name)
            .map(|(_, _, _, blend_weight)| *blend_weight)
    }
    pub fn set_blend_weight(&mut self, name: &str, value: Scalar) -> bool {
        if let Some((_, _, _, blend_weight)) = self.current.get_mut(name) {
            *blend_weight = value.max(0.0);
            self.dirty = true;
            true
        } else {
            false
        }
    }
    pub fn apply(&mut self) {
        self.dirty = true;
    }
    pub(crate) fn process(
        &mut self,
        delta_time: Scalar,
        animation: &MeshAnimation,
        mesh: &mut CompositeMesh,
    ) {
        if !self.dirty || self.current.is_empty() {
            return;
        }
        self.dirty = false;
        for (name, (phase, speed, looped, _)) in &mut self.current {
            if let Some(sequence) = animation.sequences.get(name) {
                let old = *phase;
                *phase = (*phase + *speed * delta_time)
                    .max(0.0)
                    .min(sequence.length());
                if (*phase - old).abs() > 0.0 {
                    self.dirty = true;
                } else if *looped {
                    if *phase > 0.0 {
                        *phase = 0.0;
                    } else {
                        *phase = sequence.length();
                    }
                    self.dirty = true;
                }
            }
        }
        if !self.dirty {
            return;
        }
        let meta = self
            .current
            .iter()
            .filter_map(|(n, (p, _, _, bw))| animation.sequences.get(n).map(|s| (s, *p, *bw)))
            .collect::<Vec<_>>();
        if meta.is_empty() {
            return;
        }
        let total_blend_weight = meta.iter().fold(0.0, |a, v| a + v.2);
        for (index, material) in mesh.materials.iter_mut().enumerate() {
            Self::apply_mesh_material(material, index, &meta, total_blend_weight);
        }
        for (name, transform) in mesh.bones_local_transform.iter_mut() {
            Self::apply_mesh_bone_transform(transform, name, &meta, total_blend_weight);
        }
        mesh.apply();
    }
    fn apply_mesh_material(
        material: &mut MeshMaterial,
        index: usize,
        
        meta: &[(&MeshAnimationSequence, Scalar, Scalar)],
        total_blend_weight: Scalar,
    ) {
        material.alpha = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_submesh_alpha(v.1, index, material.alpha) * v.2
        }) / total_blend_weight;
        material.order = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_submesh_order(v.1, index, material.order) * v.2
        }) / total_blend_weight;
    }
    fn apply_mesh_bone_transform(
        transform: &mut CompositeTransform,
        name: &str,
        
        meta: &[(&MeshAnimationSequence, Scalar, Scalar)],
        total_blend_weight: Scalar,
    ) {
        let position = transform.get_translation();
        let rotation = transform.get_rotation();
        let scale = transform.get_scale();
        let px = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_bone_position_x(v.1, name, position.x) * v.2
        }) / total_blend_weight;
        let py = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_bone_position_y(v.1, name, position.y) * v.2
        }) / total_blend_weight;
        let rt = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_bone_rotation(v.1, name, rotation) * v.2
        }) / total_blend_weight;
        let sx = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_bone_scale_x(v.1, name, scale.x) * v.2
        }) / total_blend_weight;
        let sy = meta.iter().fold(0.0, |a, v| {
            a + v.0.sample_bone_scale_y(v.1, name, scale.x) * v.2
        }) / total_blend_weight;
        *transform = CompositeTransform::new((px, py).into(), rt, (sx, sy).into());
    }
}
impl Component for CompositeMeshAnimation {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeMeshAnimation {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeMeshAnimation {}
#[derive(Ignite, Debug, Copy, Clone, Default, Serialize, Deserialize)]
pub struct UiMargin {
    #[serde(default)]
    pub left: Scalar,
    #[serde(default)]
    pub right: Scalar,
    #[serde(default)]
    pub top: Scalar,
    #[serde(default)]
    pub bottom: Scalar,
}
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub enum UiImagePath {
    Single(Cow<'static, str>),
    Set(Box<[Option<Cow<'static, str>>; 9]>),
}
impl UiImagePath {
    pub fn get(&self, index: usize) -> Option<&str> {
        match self {
            Self::Single(p) => Some(p),
            Self::Set(p) => {
                if let Some(p) = &p[index] {
                    Some(p)
                } else {
                    None
                }
            }
        }
    }
}
impl Default for UiImagePath {
    fn default() -> Self {
        Self::Single(Default::default())
    }
}
#[derive(Ignite, Debug, Clone, Default, Serialize, Deserialize)]
pub struct UiImage {
    #[serde(default)]
    pub image_path: UiImagePath,
    #[serde(default)]
    pub source_rect: Rect,
}
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub enum UiElementType {
    None,
    Image(Box<UiImage>),
    Text(Text<'static>),
}
impl Default for UiElementType {
    fn default() -> Self {
        Self::None
    }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UiRectsTree(
    pub Option<Cow<'static, str>>,
    pub Rect,
    pub Vec<UiRectsTree>,
);
#[derive(Ignite, Debug, Clone, Serialize, Deserialize)]
pub struct CompositeUiElement {
    #[serde(default)]
    pub id: Option<Cow<'static, str>>,
    #[serde(default)]
    pub theme: Option<Cow<'static, str>>,
    #[serde(default)]
    pub camera_name: Cow<'static, str>,
    #[serde(default)]
    pub interactive: Option<Cow<'static, str>>,
    #[serde(default)]
    pub element_type: UiElementType,
    #[serde(default)]
    pub margin: UiMargin,
    #[serde(default)]
    pub padding: UiMargin,
    #[serde(default)]
    pub left_anchor: UiValue,
    #[serde(default)]
    pub right_anchor: UiValue,
    #[serde(default)]
    pub top_anchor: UiValue,
    #[serde(default)]
    pub bottom_anchor: UiValue,
    #[serde(default)]
    pub alignment: UiValueVec2,
    #[serde(default)]
    pub offset: UiValueVec2,
    #[serde(default)]
    pub fixed_width: Option<UiValue>,
    #[serde(default)]
    pub fixed_height: Option<UiValue>,
    #[serde(default = "CompositeUiElement::default_scale")]
    pub scale: Vec2,
    #[serde(default = "CompositeUiElement::default_alpha")]
    pub alpha: UiValue,
    #[serde(default)]
    pub hidden: bool,
    #[serde(default)]
    pub children: Vec<CompositeUiElement>,
    #[serde(default)]
    pub state: HashMap<Cow<'static, str>, Scalar>,
    #[serde(skip)]
    #[ignite(ignore)]
    pub(crate) dirty: bool,
}
impl Default for CompositeUiElement {
    fn default() -> Self {
        Self {
            id: None,
            theme: None,
            camera_name: "".into(),
            interactive: None,
            element_type: Default::default(),
            margin: Default::default(),
            padding: Default::default(),
            left_anchor: 0.0.into(),
            right_anchor: 0.0.into(),
            top_anchor: 0.0.into(),
            bottom_anchor: 0.0.into(),
            alignment: Default::default(),
            offset: Default::default(),
            fixed_width: None,
            fixed_height: None,
            scale: Self::default_scale(),
            alpha: Self::default_alpha(),
            hidden: false,
            children: Default::default(),
            state: Default::default(),
            dirty: true,
        }
    }
}
impl CompositeUiElement {
    fn default_scale() -> Vec2 {
        Vec2::one()
    }
    fn default_alpha() -> UiValue {
        UiValue::Value(1.0)
    }
    pub fn rebuild(&mut self) {
        self.dirty = true;
    }
    pub fn find(&self, id: &str) -> Option<&CompositeUiElement> {
        if let Some(index) = id.find('/') {
            let part = &id[0..index];
            let test = self.children.iter().enumerate().find(|(i, c)| {
                if let Some(name) = &c.id {
                    name == part
                } else {
                    i.to_string() == part
                }
            });
            if let Some((_, child)) = test {
                if index < id.len() {
                    return child.find(&id[(index + 1)..]);
                } else {
                    return Some(child);
                }
            }
        } else {
            let part = id;
            let test = self.children.iter().enumerate().find(|(i, c)| {
                if let Some(name) = &c.id {
                    name == part
                } else {
                    i.to_string() == part
                }
            });
            if let Some((_, child)) = test {
                return Some(child);
            }
        }
        None
    }
    pub fn find_mut(&mut self, id: &str) -> Option<&mut CompositeUiElement> {
        if let Some(index) = id.find('/') {
            let part = &id[0..index];
            let test = self.children.iter_mut().enumerate().find(|(i, c)| {
                if let Some(name) = &c.id {
                    name == part
                } else {
                    i.to_string() == part
                }
            });
            if let Some((_, child)) = test {
                if index < id.len() {
                    return child.find_mut(&id[(index + 1)..]);
                } else {
                    return Some(child);
                }
            }
        } else {
            let part = id;
            let test = self.children.iter_mut().enumerate().find(|(i, c)| {
                if let Some(name) = &c.id {
                    name == part
                } else {
                    i.to_string() == part
                }
            });
            if let Some((_, child)) = test {
                return Some(child);
            }
        }
        None
    }
    pub fn calculate_rect(
        &self,
        parent_rect: Rect,
        themes: &CompositeUiThemes,
        states: &[&HashMap<Cow<'static, str>, Scalar>],
    ) -> Rect {
        let margin = if let Some(name) = &self.theme {
            if let Some(UiThemed::Image { image_margin, .. }) = themes.themes.get(name) {
                *image_margin
            } else {
                self.margin
            }
        } else {
            self.margin
        };
        let min_width = margin.left + margin.right;
        let min_height = margin.top + margin.bottom;
        let left_anchor = self.calculate_value(&self.left_anchor, states);
        let right_anchor = self.calculate_value(&self.right_anchor, states);
        let top_anchor = self.calculate_value(&self.top_anchor, states);
        let bottom_anchor = self.calculate_value(&self.bottom_anchor, states);
        let offset = self.calculate_value_vec2(&self.offset, states);
        let alignment = self.calculate_value_vec2(&self.alignment, states);
        let (x, width) = {
            let (x, w) = if let Some(fixed_width) = &self.fixed_width {
                let f = parent_rect.w * left_anchor + self.padding.left;
                let t = parent_rect.w * right_anchor - self.padding.right;
                let fixed_width = self.calculate_value(fixed_width, states);
                let o = (f + t) * 0.5;
                (
                    o,
                    (fixed_width - self.padding.left - self.padding.right).max(min_width),
                )
            } else {
                let f = parent_rect.w * left_anchor + self.padding.left;
                let t = parent_rect.w * right_anchor - self.padding.right;
                (f, (t - f))
            };
            (x, w.max(min_width) * self.scale.x)
        };
        let (y, height) = {
            let (y, h) = if let Some(fixed_height) = &self.fixed_height {
                let f = parent_rect.h * top_anchor + self.padding.top;
                let t = parent_rect.h * bottom_anchor - self.padding.bottom;
                let o = (f + t) * 0.5;
                let fixed_height = self.calculate_value(fixed_height, states);
                (
                    o,
                    (fixed_height - self.padding.top - self.padding.bottom).max(min_height),
                )
            } else {
                let f = parent_rect.h * top_anchor + self.padding.top;
                let t = parent_rect.h * bottom_anchor - self.padding.bottom;
                (f, (t - f))
            };
            (y, h.max(min_height) * self.scale.y)
        };
        Rect {
            x: offset.x + x + parent_rect.x - alignment.x * width,
            y: offset.y + y + parent_rect.y - alignment.y * height,
            w: width,
            h: height,
        }
    }
    pub fn calculate_value(
        &self,
        state: &UiValue,
        states: &[&HashMap<Cow<'static, str>, Scalar>],
    ) -> Scalar {
        match state {
            UiValue::Value(value) => *value,
            UiValue::State(state) => {
                if let Some(index) = state.find('/') {
                    let levels = state[0..index].chars().filter(|c| *c == '.').count();
                    let state = &state[(index + 1)..];
                    states
                        .get(states.len() - levels)
                        .map(|states| states.get(state).copied().unwrap_or(0.0))
                        .unwrap_or(0.0)
                } else {
                    self.state.get(state).copied().unwrap_or(0.0)
                }
            }
            UiValue::MapState(state, sl, su, tl, tu) => {
                if let Some(index) = state.find('/') {
                    let levels = state[0..index].chars().filter(|c| *c == '.').count();
                    let state = &state[(index + 1)..];
                    states
                        .get(states.len() - levels)
                        .map(|states| {
                            states
                                .get(state)
                                .map(|value| {
                                    let f = (*value - sl) / (su - sl);
                                    f * (tu - tl) + tl
                                })
                                .unwrap_or(0.0)
                        })
                        .unwrap_or(0.0)
                } else {
                    self.state.get(state).copied().unwrap_or(0.0)
                }
            }
        }
    }
    pub fn calculate_value_vec2(
        &self,
        state: &UiValueVec2,
        states: &[&HashMap<Cow<'static, str>, Scalar>],
    ) -> Vec2 {
        Vec2::new(
            self.calculate_value(&state.x, states),
            self.calculate_value(&state.y, states),
        )
    }
    pub fn build_rects_tree<'a>(
        &'a self,
        parent_rect: Rect,
        themes: &CompositeUiThemes,
        states: &mut Vec<&'a HashMap<Cow<'static, str>, Scalar>>,
    ) -> Option<UiRectsTree> {
        states.push(&self.state);
        let rect = self.calculate_rect(parent_rect, themes, states);
        if self.hidden {
            states.pop();
            return None;
        }
        let children = self
            .children
            .iter()
            .filter_map(|item| item.build_rects_tree(rect, themes, states))
            .collect::<Vec<_>>();
        states.pop();
        Some(UiRectsTree(self.id.clone(), rect, children))
    }
    pub fn build_commands<'a>(
        &'a self,
        parent_rect: Rect,
        interactibles: &mut CompositeUiInteractibles,
        themes: &CompositeUiThemes,
        states: &mut Vec<&'a HashMap<Cow<'static, str>, Scalar>>,
    ) -> (Vec<Command<'static>>, Rect) {
        states.push(&self.state);
        let rect = self.calculate_rect(parent_rect, themes, states);
        if self.hidden {
            states.pop();
            return (vec![], rect);
        }
        if let Some(name) = &self.interactive {
            interactibles.bounding_boxes.insert(name.clone(), rect);
        }
        let mut commands = match &self.element_type {
            UiElementType::Text(text) => {
                let mut text = text.clone();
                text.position = Vec2::new(rect.x, rect.y);
                text.max_width = Some(rect.w);
                let alpha = if let Some(name) = &self.theme {
                    if let Some(UiThemed::Text {
                        font_name,
                        font_size,
                        alpha,
                    }) = themes.themes.get(name)
                    {
                        text.font = font_name.clone();
                        text.size = self.calculate_value(font_size, states);
                        self.calculate_value(alpha, states)
                    } else {
                        1.0
                    }
                } else {
                    1.0
                };
                vec![
                    Command::Store,
                    Command::Alpha(alpha),
                    Command::Draw(text.into()),
                    Command::Restore,
                ]
            }
            UiElementType::Image(image) => {
                let (margin, source_rect, image_path, alpha) = if let Some(name) = &self.theme {
                    if let Some(UiThemed::Image {
                        image_margin,
                        image_path,
                        source_rect,
                        alpha,
                    }) = themes.themes.get(name)
                    {
                        (
                            *image_margin,
                            *source_rect,
                            image_path,
                            self.calculate_value(alpha, states),
                        )
                    } else {
                        (self.margin, image.source_rect, &image.image_path, 1.0)
                    }
                } else {
                    (self.margin, image.source_rect, &image.image_path, 1.0)
                };
                let sx1 = source_rect.x;
                let sx2 = source_rect.x + margin.left;
                let sx3 = source_rect.x + source_rect.w - margin.right;
                let sy1 = source_rect.y;
                let sy2 = source_rect.y + margin.top;
                let sy3 = source_rect.y + source_rect.h - margin.bottom;
                let sw1 = margin.left;
                let sw2 = source_rect.w - margin.left - margin.right;
                let sw3 = margin.right;
                let sh1 = margin.top;
                let sh2 = source_rect.h - margin.top - margin.bottom;
                let sh3 = margin.bottom;
                let dx1 = rect.x;
                let dx2 = rect.x + margin.left;
                let dx3 = rect.x + rect.w - margin.right;
                let dy1 = rect.y;
                let dy2 = rect.y + margin.top;
                let dy3 = rect.y + rect.h - margin.bottom;
                let dw1 = margin.left;
                let dw2 = rect.w - margin.left - margin.right;
                let dw3 = margin.right;
                let dh1 = margin.top;
                let dh2 = rect.h - margin.top - margin.bottom;
                let dh3 = margin.bottom;
                vec![
                    Command::Store,
                    Command::Alpha(alpha),
                    if let Some(path) = image_path.get(0) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx1,
                                    y: sy1,
                                    w: sw1,
                                    h: sh1,
                                }),
                                destination: Some(Rect {
                                    x: dx1,
                                    y: dy1,
                                    w: dw1,
                                    h: dh1,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(1) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx2,
                                    y: sy1,
                                    w: sw2,
                                    h: sh1,
                                }),
                                destination: Some(Rect {
                                    x: dx2,
                                    y: dy1,
                                    w: dw2,
                                    h: dh1,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(2) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx3,
                                    y: sy1,
                                    w: sw3,
                                    h: sh1,
                                }),
                                destination: Some(Rect {
                                    x: dx3,
                                    y: dy1,
                                    w: dw3,
                                    h: dh1,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(3) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx1,
                                    y: sy2,
                                    w: sw1,
                                    h: sh2,
                                }),
                                destination: Some(Rect {
                                    x: dx1,
                                    y: dy2,
                                    w: dw1,
                                    h: dh2,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(4) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx2,
                                    y: sy2,
                                    w: sw2,
                                    h: sh2,
                                }),
                                destination: Some(Rect {
                                    x: dx2,
                                    y: dy2,
                                    w: dw2,
                                    h: dh2,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(5) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx3,
                                    y: sy2,
                                    w: sw3,
                                    h: sh2,
                                }),
                                destination: Some(Rect {
                                    x: dx3,
                                    y: dy2,
                                    w: dw3,
                                    h: dh2,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(6) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx1,
                                    y: sy3,
                                    w: sw1,
                                    h: sh3,
                                }),
                                destination: Some(Rect {
                                    x: dx1,
                                    y: dy3,
                                    w: dw1,
                                    h: dh3,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(7) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx2,
                                    y: sy3,
                                    w: sw2,
                                    h: sh3,
                                }),
                                destination: Some(Rect {
                                    x: dx2,
                                    y: dy3,
                                    w: dw2,
                                    h: dh3,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    if let Some(path) = image_path.get(8) {
                        Command::Draw(
                            Image {
                                image: path.to_owned().into(),
                                source: Some(Rect {
                                    x: sx3,
                                    y: sy3,
                                    w: sw3,
                                    h: sh3,
                                }),
                                destination: Some(Rect {
                                    x: dx3,
                                    y: dy3,
                                    w: dw3,
                                    h: dh3,
                                }),
                                alignment: Vec2::new(0.0, 0.0),
                            }
                            .into(),
                        )
                    } else {
                        Command::None
                    },
                    Command::Restore,
                ]
            }
            _ => vec![],
        };
        for child in &self.children {
            commands.extend(child.build_commands(rect, interactibles, themes, states).0);
        }
        states.pop();
        (commands, rect)
    }
}
impl Component for CompositeUiElement {
    type Storage = VecStorage<Self>;
}
impl Prefab for CompositeUiElement {
    fn post_from_prefab(&mut self) {
        self.dirty = true;
    }
}
impl PrefabComponent for CompositeUiElement {}