use super::dice::{AdvancementRoll, DiceRoll, DiceRollModifier};
use crate::parser::dice::DiceParsingError;

//TOOD convert these to use parse_amounts from the common dice code.

fn parse_modifier(input: &str) -> Result<DiceRollModifier, DiceParsingError> {
    if input.ends_with("bb") {
        Ok(DiceRollModifier::TwoBonus)
    } else if input.ends_with("b") {
        Ok(DiceRollModifier::OneBonus)
    } else if input.ends_with("pp") {
        Ok(DiceRollModifier::TwoPenalty)
    } else if input.ends_with("p") {
        Ok(DiceRollModifier::OnePenalty)
    } else {
        Ok(DiceRollModifier::Normal)
    }
}

//Make diceroll take a vec of Amounts
//Split based on :, send first part to parse_modifier.
//Send second part to parse_amounts
pub fn parse_regular_roll(input: &str) -> Result<DiceRoll, DiceParsingError> {
    let input: Vec<&str> = input.trim().split(":").collect();

    let (modifiers_str, amounts_str) = match input[..] {
        [amounts] => Ok(("", amounts)),
        [modifiers, amounts] => Ok((modifiers, amounts)),
        _ => Err(DiceParsingError::UnconsumedInput),
    }?;

    let modifier = parse_modifier(modifiers_str)?;
    let amount = crate::parser::dice::parse_single_amount(amounts_str)?;
    Ok(DiceRoll { modifier, amount })
}

pub fn parse_advancement_roll(input: &str) -> Result<AdvancementRoll, DiceParsingError> {
    let input = input.trim();
    let amounts = crate::parser::dice::parse_single_amount(input)?;

    Ok(AdvancementRoll {
        existing_skill: amounts,
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::dice::{Amount, Element, Operator};

    #[test]
    fn regular_roll_accepts_single_number() {
        let result = parse_regular_roll("60");
        assert!(result.is_ok());
        assert_eq!(
            DiceRoll {
                amount: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                },
                modifier: DiceRollModifier::Normal
            },
            result.unwrap()
        );
    }

    #[test]
    fn regular_roll_rejects_complex_expressions() {
        let result = parse_regular_roll("3 + abc + bob - 4");
        assert!(result.is_err());
    }

    #[test]
    fn regular_roll_accepts_two_bonus() {
        let result = parse_regular_roll("bb:60");
        assert!(result.is_ok());
        assert_eq!(
            DiceRoll {
                amount: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                },
                modifier: DiceRollModifier::TwoBonus
            },
            result.unwrap()
        );
    }

    #[test]
    fn regular_roll_accepts_one_bonus() {
        let result = parse_regular_roll("b:60");
        assert!(result.is_ok());
        assert_eq!(
            DiceRoll {
                amount: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                },
                modifier: DiceRollModifier::OneBonus
            },
            result.unwrap()
        );
    }

    #[test]
    fn regular_roll_accepts_two_penalty() {
        let result = parse_regular_roll("pp:60");
        assert!(result.is_ok());
        assert_eq!(
            DiceRoll {
                amount: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                },
                modifier: DiceRollModifier::TwoPenalty
            },
            result.unwrap()
        );
    }

    #[test]
    fn regular_roll_accepts_one_penalty() {
        let result = parse_regular_roll("p:60");
        assert!(result.is_ok());
        assert_eq!(
            DiceRoll {
                amount: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                },
                modifier: DiceRollModifier::OnePenalty
            },
            result.unwrap()
        );
    }

    #[test]
    fn regular_roll_accepts_whitespace() {
        assert!(parse_regular_roll("60     ").is_ok());
        assert!(parse_regular_roll("   60").is_ok());
        assert!(parse_regular_roll("   60    ").is_ok());

        assert!(parse_regular_roll("bb:60     ").is_ok());
        assert!(parse_regular_roll("   bb:60").is_ok());
        assert!(parse_regular_roll("   bb:60    ").is_ok());

        assert!(parse_regular_roll("b:60     ").is_ok());
        assert!(parse_regular_roll("   b:60").is_ok());
        assert!(parse_regular_roll("   b:60    ").is_ok());

        assert!(parse_regular_roll("pp:60     ").is_ok());
        assert!(parse_regular_roll("   pp:60").is_ok());
        assert!(parse_regular_roll("   pp:60    ").is_ok());

        assert!(parse_regular_roll("p:60     ").is_ok());
        assert!(parse_regular_roll("   p:60").is_ok());
        assert!(parse_regular_roll("   p:60    ").is_ok());
    }

    #[test]
    fn advancement_roll_accepts_whitespacen() {
        assert!(parse_advancement_roll("60     ").is_ok());
        assert!(parse_advancement_roll("   60").is_ok());
        assert!(parse_advancement_roll("   60    ").is_ok());
    }

    #[test]
    fn advancement_roll_accepts_single_number() {
        let result = parse_advancement_roll("60");
        assert!(result.is_ok());
        assert_eq!(
            AdvancementRoll {
                existing_skill: Amount {
                    operator: Operator::Plus,
                    element: Element::Number(60)
                }
            },
            result.unwrap()
        );
    }

    #[test]
    fn advancement_roll_allows_big_numbers() {
        assert!(parse_advancement_roll("3000").is_ok());
    }

    #[test]
    fn advancement_roll_allows_variables() {
        let result = parse_advancement_roll("abc");
        assert!(result.is_ok());
        assert_eq!(
            AdvancementRoll {
                existing_skill: Amount {
                    operator: Operator::Plus,
                    element: Element::Variable(String::from("abc"))
                }
            },
            result.unwrap()
        );
    }

    #[test]
    fn advancement_roll_rejects_complex_expressions() {
        let result = parse_advancement_roll("3 + abc + bob - 4");
        assert!(result.is_err());
    }
}
