use serde::{Deserialize, Serialize};

use super::chat_message::ChatMessage;

pub trait GameEntry : Serialize + Sized {
    /// Load a single entry from string
    fn load(entry: &str) -> Option<Self>;
    /// Loads multiple items, blanket impl using Self::load
    fn load_mul(entries: &[&str]) -> Vec<Option<Self>> {
        let mut r = Vec::with_capacity(entries.len());
        for  &e in entries {
            r.push(Self::load(e));
        }
        r 
    }
    /// Display name (e.g. `weapon/dagger` -> `Dagger`)
    fn display_name(&self) -> String;
    /// Get all entries, with an optional filter (could be `weapon` for example to show only weapons)
    fn all(filter: Option<&str>) -> Vec<String>;
    /// Get all categories (such as weapons/consumables/spells)
    fn categories() -> Vec<String>;
    /// returns a chat message to show the entry (with description and all)
    fn to_chat(&self) -> ChatMessage;
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct ActionDefinition {
    pub name: String,
    pub display_name: Option<String>,
    pub source: Option<String>,
    pub targets: i32,
    pub rolls: Option<Vec<DiceRoll>>,
}
impl ActionDefinition {
    pub fn new(name: String) -> Self {
        Self {
            name, ..Default::default()
        }
    }
    pub fn display_name(mut self, name: Option<String>) -> Self {
        self.display_name = name;
        self
    }
    pub fn source(mut self, source: Option<String>) -> Self {
        self.source = source;
        self
    }
    pub fn targets(mut self, targets: i32) -> Self {
        self.targets = targets;
        self
    }
    pub fn rolls(mut self, rolls: Option<Vec<DiceRoll>>) -> Self {
        self.rolls = rolls;
        self
    }
    pub fn with_roll(mut self, roll: DiceRoll) -> Self {
        if let Some(rolls) = &mut self.rolls {
            rolls.push(roll);
        }
        else {
            self.rolls = Some(vec![roll]);
        }
        self
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ActionResult {
    pub name: String,
    pub source: String,
    pub targets: Vec<String>,
    pub results: Vec<DiceRollResult>,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct DiceRoll {
    /// Field name
    pub name: String,
    /// dice size (aka d6, d12, d20)
    pub dice_type: u16,
    /// amount of dice (aka 1d6, 2d12, 10d20)
    pub dice_amount: u16,
    /// Constant amout to add (+7, +3, -1)
    pub constant: i16,
    /// should be enabled by default
    pub enabled: bool,
    /// Extra data, like damage type
    pub extra: Option<String>,
}
impl DiceRoll {
    pub fn new(name: String, dice_type: u16, dice_amount: u16) -> Self {
        Self {
            name, dice_type, dice_amount, enabled: true, ..Default::default()
        }
    }
    pub fn constant(mut self, constant: i16) -> Self {
        self.constant = constant;
        self
    }
    pub fn enabled(mut self, enabled: bool) -> Self {
        self.enabled = enabled;
        self
    }
    pub fn extra(mut self, extra: Option<String>) -> Self {
        self.extra = extra;
        self
    }
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DiceRollResult {
    pub name: String,
    pub result: i32,
    pub result_text: String,
    pub extra: Option<String>,
}