use bevy::prelude::*;
use serde::{Deserialize, Serialize};

use super::*;

#[derive(Clone, Default, Debug, Hash, Eq, PartialEq, Serialize, Deserialize, Reflect)]
pub struct CreatureDef {
    pub id: String,
    pub name_singular: String,
    pub name_plural: String,
    pub health: i32,
    pub armor_class: i8,
    pub to_hit_modifier: i8,
    pub min_damage: i32,
    pub max_damage: i32,
    pub multiattack: Option<u32>,
    pub is_humanoid: bool,
    pub is_animal: bool,
    pub is_intelligent: bool,
    pub patrols: bool,
    pub prefers_dark: bool,
    pub invulnerable: bool,
}

impl CreatureDef {
    pub const CATEGORY: TopLevelTag = TopLevelTag::Creature;

    pub fn parse(header: Tag, tags: &[Tag]) -> Result<Self, ParseError> {
        let mut result = Self {
            id: header.parse_value().unwrap(),
            ..Default::default()
        };
        for tag in tags {
            match tag.name.as_str() {
                "NAME" => (result.name_singular, result.name_plural) = tag.parse_value()?,
                "HEALTH" => result.health = tag.parse_value()?,
                "AC" => result.armor_class = tag.parse_value()?,
                "ATTACK" => {
                    (result.to_hit_modifier, result.min_damage, result.max_damage) =
                        tag.parse_value()?
                }
                "MULTIATTACK" => result.multiattack = Some(tag.parse_value()?),
                "HUMANOID" => result.is_humanoid = true,
                "ANIMAL" => result.is_animal = true,
                "INTELLIGENT" => result.is_intelligent = true,
                "PATROL" => result.patrols = true,
                "PREFER_DARK" => result.prefers_dark = true,
                "INVULNERABLE" => result.invulnerable = true,
                _ => {
                    return Err(ParseError::UnknownTag {
                        tag: tag.name.clone(),
                        id: Some(result.id),
                        category: Some(Self::CATEGORY),
                    });
                }
            }
        }
        Ok(result)
    }

    pub fn attack_count(&self) -> u32 {
        self.multiattack.unwrap_or(1)
    }

    pub fn damage_roll(&self) -> i32 {
        fastrand::i32(self.min_damage..=self.max_damage)
    }
}
