use crate::tui::ecs::components::*;
use crate::tui::ecs::world::*;
use freecs::Entity;

pub struct EntityBuilder {
    mask: u64,
    position: Option<Position>,
    velocity: Option<Velocity>,
    sprite: Option<Sprite>,
    collider: Option<Collider>,
    name: Option<Name>,
    visibility: Option<Visibility>,
    z_index: Option<ZIndex>,
    parent: Option<Parent>,
    label: Option<Label>,
    tilemap: Option<Tilemap>,
    sprite_animation: Option<SpriteAnimation>,
    local_offset: Option<LocalOffset>,
}

impl EntityBuilder {
    pub fn new() -> Self {
        Self {
            mask: 0,
            position: None,
            velocity: None,
            sprite: None,
            collider: None,
            name: None,
            visibility: None,
            z_index: None,
            parent: None,
            label: None,
            tilemap: None,
            sprite_animation: None,
            local_offset: None,
        }
    }

    pub fn position(mut self, position: Position) -> Self {
        self.mask |= POSITION;
        self.position = Some(position);
        self
    }

    pub fn velocity(mut self, velocity: Velocity) -> Self {
        self.mask |= VELOCITY;
        self.velocity = Some(velocity);
        self
    }

    pub fn sprite(mut self, sprite: Sprite) -> Self {
        self.mask |= SPRITE;
        self.sprite = Some(sprite);
        self
    }

    pub fn collider(mut self, collider: Collider) -> Self {
        self.mask |= COLLIDER;
        self.collider = Some(collider);
        self
    }

    pub fn name(mut self, name: Name) -> Self {
        self.mask |= NAME;
        self.name = Some(name);
        self
    }

    pub fn visibility(mut self, visibility: Visibility) -> Self {
        self.mask |= VISIBILITY;
        self.visibility = Some(visibility);
        self
    }

    pub fn z_index(mut self, z_index: ZIndex) -> Self {
        self.mask |= Z_INDEX;
        self.z_index = Some(z_index);
        self
    }

    pub fn parent(mut self, parent: Parent) -> Self {
        self.mask |= PARENT;
        self.parent = Some(parent);
        self
    }

    pub fn label(mut self, label: Label) -> Self {
        self.mask |= LABEL;
        self.label = Some(label);
        self
    }

    pub fn tilemap(mut self, tilemap: Tilemap) -> Self {
        self.mask |= TILEMAP;
        self.tilemap = Some(tilemap);
        self
    }

    pub fn sprite_animation(mut self, sprite_animation: SpriteAnimation) -> Self {
        self.mask |= SPRITE_ANIMATION;
        self.sprite_animation = Some(sprite_animation);
        self
    }

    pub fn local_offset(mut self, local_offset: LocalOffset) -> Self {
        self.mask |= LOCAL_OFFSET;
        self.local_offset = Some(local_offset);
        self
    }

    pub fn spawn(self, world: &mut World) -> Entity {
        let entity = world.spawn_entities(self.mask, 1)[0];

        if let Some(position) = self.position {
            world.set_position(entity, position);
        }
        if let Some(velocity) = self.velocity {
            world.set_velocity(entity, velocity);
        }
        if let Some(sprite) = self.sprite {
            world.set_sprite(entity, sprite);
        }
        if let Some(collider) = self.collider {
            world.set_collider(entity, collider);
        }
        if let Some(name) = self.name {
            world.set_name(entity, name);
        }
        if let Some(visibility) = self.visibility {
            world.set_visibility(entity, visibility);
        }
        if let Some(z_index) = self.z_index {
            world.set_z_index(entity, z_index);
        }
        if let Some(parent) = self.parent {
            world.set_parent(entity, parent);
        }
        if let Some(label) = self.label {
            world.set_label(entity, label);
        }
        if let Some(tilemap) = self.tilemap {
            world.set_tilemap(entity, tilemap);
        }
        if let Some(sprite_animation) = self.sprite_animation {
            world.set_sprite_animation(entity, sprite_animation);
        }
        if let Some(local_offset) = self.local_offset {
            world.set_local_offset(entity, local_offset);
        }

        entity
    }
}

impl Default for EntityBuilder {
    fn default() -> Self {
        Self::new()
    }
}
