use paste::paste;
use std::collections::{BTreeMap, HashMap};

use data::FightPropType;

pub type FightPropValue = f32;

#[derive(Default)]
pub struct FightPropComp {
    pub fight_cur_prop_map: BTreeMap<FightPropType, FightPropValue>,
    pub fight_cal_prop_map: BTreeMap<FightPropType, FightPropValue>,
}

macro_rules! impl_get_cur {
    ($($ty: ident;)*) => {
        paste! {
            $(pub fn [<get_cur_ $ty:lower>](&self) -> FightPropValue {
                let percent = self.get_prop_value(FightPropType::[<$ty Percent>]) + 1.0;
                let percent = FightPropValue::max(percent, 0.0);

                let base = percent * self.get_prop_value(FightPropType::[<Base $ty>]);
                base + self.get_prop_value(FightPropType::$ty)
            })*
        }
    };
}

impl FightPropComp {
    pub fn get_fight_prop_value_map(&self) -> HashMap<u32, f32> {
        let mut all_map: HashMap<u32, f32> = self
            .fight_cur_prop_map
            .iter()
            .map(|(t, v)| (*t as u32, *v))
            .collect();
        all_map.extend(
            self.fight_cal_prop_map
                .iter()
                .map(|(t, v)| (*t as u32, *v))
                .collect::<HashMap<_, _>>(),
        );
        all_map
    }

    pub fn get_prop_value(&self, ty: FightPropType) -> FightPropValue {
        match ty {
            FightPropType::CurAttack => self.get_cur_attack(),
            FightPropType::CurDefense => self.get_cur_defense(),
            FightPropType::CurSpeed => self.get_cur_speed(),
            ty if Self::is_cur_prop_type(ty) => self
                .fight_cur_prop_map
                .get(&ty)
                .cloned()
                .unwrap_or_default(),
            ty => self.find_cal_prop_value(ty),
        }
    }

    impl_get_cur! { Attack; Defense; }

    pub fn get_cur_speed(&self) -> FightPropValue {
        let percent = self.get_prop_value(FightPropType::SpeedPercent) + 1.0;
        let percent = FightPropValue::max(percent, 0.0);

        percent * self.get_prop_value(FightPropType::BaseSpeed)
    }

    pub fn set_cur_hp(&mut self, hp: FightPropValue) {
        self.fight_cur_prop_map.insert(FightPropType::CurHp, hp);
    }

    fn find_cal_prop_value(&self, ty: FightPropType) -> FightPropValue {
        self.fight_cal_prop_map
            .get(&ty)
            .cloned()
            .unwrap_or_default()
    }

    fn is_cur_prop_type(ty: FightPropType) -> bool {
        ty >= FightPropType::CurFireEnergy
            && ty <= FightPropType::CurSpeed
            && ty != FightPropType::MaxHp
    }
}
