use std::cmp::Ordering::Equal;
use std::collections::HashMap;

enum GameResult {
    Win,
    Draw,
    Loss
}

struct TeamResult {
    wins: u32,
    draws: u32,
    losses: u32,
}

impl TeamResult {
    fn new() -> TeamResult {
        TeamResult { wins: 0, draws: 0, losses: 0 }
    }
    fn add_game_result(&mut self, result: GameResult) {
        match result {
            GameResult::Win => self.wins += 1,
            GameResult::Draw => self.draws += 1,
            GameResult::Loss => self.losses += 1,
        }
    }
}

pub fn tally(input: &str) -> String {
    let mut results: HashMap<String, TeamResult> = HashMap::new();
    for line in input.to_string().lines() {
        let parts: Vec<&str> = line.trim_right().split(';').collect();
        if parts.len() != 3 { continue; }
        let team1 = parts[0];
        let team2 = parts[1];
        let outcome = parts[2];
        match outcome {
            "win" => {
                add_game_result(&mut results, team1.to_string(), GameResult::Win);
                add_game_result(&mut results, team2.to_string(), GameResult::Loss);
            },
            "draw" => {
                add_game_result(&mut results, team1.to_string(), GameResult::Draw);
                add_game_result(&mut results, team2.to_string(), GameResult::Draw);
            },
            "loss" => {
                add_game_result(&mut results, team1.to_string(), GameResult::Loss);
                add_game_result(&mut results, team2.to_string(), GameResult::Win);
            },
            _ => () // Ignore bad lines
        }
    }
    write_tally(&results)
}

fn write_tally(results: &HashMap<String, TeamResult>) -> String {
    let mut v: Vec<_> = results.iter().map(|(team, r)| {
        let games = r.wins + r.draws + r.losses;
        let points = r.wins * 3 + r.draws;
        (team, games, r, points)
    }).collect();
    // Sort by points descending, then name A-Z.
    v.sort_by(|a, b|
              match a.3.cmp(&(b.3)).reverse() {
                  Equal => a.0.cmp(&(b.0)),
                  other => other
              });
    let mut lines = vec![format!("{:30} | MP |  W |  D |  L |  P", "Team")];
    lines.extend(v.iter().map(|&(ref team, games, r, points)| {
        format!("{:30} | {:2} | {:2} | {:2} | {:2} | {:2}",
                team, games, r.wins, r.draws, r.losses, points)
    }));
    lines.join("\n")
}

fn add_game_result(results: &mut HashMap<String, TeamResult>, team: String, result: GameResult) {
    results.entry(team).or_insert(TeamResult::new()).add_game_result(result);
}
