use failure;
use rrecutils;

/// A `Card` always has a name, an icon, and some miscellaneous other information
#[derive(Debug)]
pub struct Card<'a> {
    pub name: &'a str,
    pub icon: String,
    pub card_type: CardType<'a>,
}

/// Different card types have different miscellaneous other pieces of
/// information, but most of them are kept in a catch-all "Generic"
/// case
#[derive(Debug)]
pub enum CardType<'a> {
    Monster {
        hp: &'a str,
        melee: Option<&'a str>,
        ranged: Option<&'a str>,
        arcane: Option<&'a str>,
        defense: Option<&'a str>,
        action: &'a str,
        effect: &'a str,
    },
    Weapon {
        weapon_type: WeaponType,
        ability: &'a str,
        effect: &'a str,
    },
    Generic {
        kind: Kind,
        effect: &'a str,
    },
}

#[derive(Debug, Clone, Copy)]
pub enum Kind { Ability, Equipment, Item, Plant, Treasure }

#[derive(Debug, Clone, Copy)]
pub enum WeaponType { Melee, Ranged, Arcane }

impl<'a> Card<'a> {
    /// Here we parse out our in-program `Card` type from the recutils
    /// storage format of the card
    pub fn from_record(rec: &'a rrecutils::Record)
                   -> Result<Card<'a>, failure::Error>
    {
        let name = rec.get("Name")?;
        // If this doesn't have a Type set by the Recutils parser,
        // then that's a problem!
        let typ = rec.get_type()?;

        let card_type = match typ {
            "Weapon" => CardType::Weapon {
                weapon_type: match rec.get("Type")? {
                    "melee" => WeaponType::Melee,
                    "ranged" => WeaponType::Ranged,
                    // XXX FIXME IN THE FILE (but for ease of writing
                    // this assumes that we might keep using the old
                    // "magic" terminology instead of "arcane")
                    "magic" | "arcane" => WeaponType::Arcane,
                    t => bail!("Unknown weapon type: {}", t),
                },
                ability: rec.get("Ability")?,
                effect: rec.get("Effect")?,
            },

            "Monster" => CardType::Monster {
                hp: rec.get("HP")?,
                melee: rec.get("Melee").ok(),
                ranged: rec.get("Ranged").ok(),
                arcane: rec.get("Arcane").ok(),
                defense: rec.get("Defense").ok(),
                action: rec.get("Action")?,
                effect: rec.get("Effect")?,
            },

            t => CardType::Generic {
                kind: match t {
                    "Ability" => Kind::Ability,
                    "Item" => Kind::Item,
                    "Equipment" => Kind::Equipment,
                    "Plant" => Kind::Plant,
                    "Treasure" => Kind::Treasure,
                    _ => bail!("Unknown card type: {}", t),
                },
                effect: rec.get("Effect")?,
            }
        };


        let lower_type = typ.to_lowercase();
        let icon = format!(
            "../icons/{}-icon.svg",
            rec.get("Icon").unwrap_or(
                match typ {
                    "Weapon" => rec.get("Type")?,
                    _ => &lower_type,
                }
            ),
        );

        Ok(Card { name, icon, card_type })
    }

    pub fn type_name(&self) -> &'static str {
        match self.card_type {
            CardType::Weapon { .. } => "weapon",
            CardType::Monster { .. } => "monster",
            CardType::Generic { kind, .. } => match kind {
                Kind::Ability => "ability",
                Kind::Item => "item",
                Kind::Equipment => "equipment",
                Kind::Plant => "plant",
                Kind::Treasure => "treasure",
            },
        }
    }

    pub fn card_name(&self) -> String {
        self.name.chars().filter_map(|c| {
            if c == ' '{
                Some('_')
            } else if c.is_alphabetic() {
                Some(c.to_ascii_lowercase())
            } else {
                None
            }
        }).collect()
    }
}
