#include <iostream>
#include <sstream>
#include <map>
#include <vector>
#include <algorithm>

using namespace std;

const int MIN_SCORE = 0;
const string usage = "\
usage: grade [-n, --by-name] [-p, --by-points] [-v, --verbose]";

struct student {
    string name;

    int lab_score;
    int exam_score;

    int final_score;
    string letter_grade;
};
typedef struct student student;

ostream & operator<<(ostream & os, const student & s) {
    os << "name: '" << s.name
        << ", final score: " << s.final_score
        << ", grade: " << s.letter_grade;
    return os;
}

bool sort_by_name(const student & a, const student & b) {
    return a.name < b.name;
}

bool sort_by_score(const student & a, const student & b) {
    return a.final_score > b.final_score;
}

struct grade {
    int max_score;
    int min_late_days;
    int max_late_days;
};
typedef struct grade grade;

struct result {
    int score;
    int late_days;
};
typedef struct result result;

result collect_grade(string name, int score_max,
        int late_min, int late_max, bool verbose);
string determine_letter_grade(int score);

int main(int argc, char * argv[]) {
    bool verbose = false;
    bool by_points = false;
    bool by_name = false;
    for(int i = 0; i < argc; i++) {
        string cur_arg = argv[i];
        if(cur_arg == "--verbose" or cur_arg == "-v") {
            verbose = true;
        }
        if(cur_arg == "--by-points" or cur_arg == "-p") {
            by_points = true;
        }
        if(cur_arg == "--by-name" or cur_arg == "-n") {
            by_name = true;
        }
        if(cur_arg == "--help" or cur_arg == "-h") {
            cout << usage << endl;
            return 0;
        }
    }
    if (by_points and by_name) {
        cerr << "only specify one sort order" << endl;
        return -1;
    }

    map<string, grade> labs = {
        {"Lab01", {20, -2, 2}},
        {"Lab02", {20, -2, 2}},
        {"Lab03", {30, -3, 3}},
        {"Lab04", {30, -3, 3}},
        {"Lab05", {30, -3, 3}},
        {"Lab06", {30, -3, 3}},
        {"Lab07", {30, -3, 3}},
        {"Lab08", {30, -3, 3}},
        {"Lab09", {20, -2, 2}},
        {"Lab10", {20, -2, 2}},
        {"Lab11", {40, -4, 4}},
        {"exam1", {100, 0, 3}},
        {"exam2", {100, 0, 3}},
        {"final", {100, 0, 3}},
    };

    vector<student> students;

    while (!cin.eof()) {
        if(verbose)
            cerr << "Please enter name of student: ";
        string name;
        getline(cin, name);
        if (!name.empty()) {
            int total_lab_points = 0;
            int total_exam_points = 0;

            for(pair<string, grade> l: labs) {
                string n = l.first;
                grade g = l.second;
                try {
                    result cur_result = collect_grade(n, g.max_score,
                            g.min_late_days, g.max_late_days, verbose);

                    total_lab_points += cur_result.score;

                    if (n.substr(0, 3) == "lab")
                        total_lab_points -= 5 * cur_result.late_days;
                    else if (n.substr(0, 4) == "exam" or n.substr(0, 4) == "final") 
                        total_exam_points -= 20 * cur_result.late_days;
                } catch (string e) {
                    cerr << e << endl;
                    return 1;
                }
            }


            int final_score = total_lab_points  +  total_exam_points;
            student s = {name, total_lab_points, total_exam_points,
                final_score, determine_letter_grade(final_score)}; 
            students.push_back(s);
        }
    }

    if(by_name) {
        sort(students.begin(), students.end(), sort_by_name);
    }
    else if (by_points) {
        sort(students.begin(), students.end(), sort_by_score);
    }

    for(student s: students) {
        cout << s << endl;
    }

    return 0;
}

result collect_grade(string name, int score_max,
        int late_min, int late_max, bool verbose=false) {
    int score = 0;
    if(verbose) {
        cerr << "Please enter score for " << name
            << "[" << MIN_SCORE << ", " << score_max << "]: ";
    }
    cin >> score;
    if (!(MIN_SCORE <= score and score <= score_max)) {
        ostringstream s;
        s << score << " is not in accepted range: ["
            << MIN_SCORE << ", "
            << score_max << "]" << endl;
        throw s.str();
    }

    int late_days;
    if (verbose) {
        cerr << "Please enter late days ["
            << late_min << ", " << late_max << "]: ";
    }
    cin >> late_days;
    if (!(late_min <= late_days and late_days <= late_max)) {
        ostringstream s;
        s << late_days << " is not in accepted range: ["
            << late_min << ", "
            << late_max << "]" << endl;
        throw s.str();
    }

    result r = {score, late_days};
    return r;
}

string determine_letter_grade(int score) {
    string letter_grade;
    if(score >= 570)
        letter_grade = "A";
    if(score < 570 && score >=540)
        letter_grade = "A-";
    if(score < 540 && score >= 522)
        letter_grade = "B+";
    if(score < 522 && score >= 498)
        letter_grade = "B";
    if(score < 498 && score >= 480)
        letter_grade = "B-";
    if(score < 480 && score >= 462)
        letter_grade = "C+";
    if(score < 462 && score >= 438)
        letter_grade = "C";
    if(score < 438 && score >= 420)
        letter_grade = "C-";
    if(score < 420 && score >= 402)
        letter_grade = "D+";
    if(score < 402 && score >= 378)
        letter_grade = "D";
    if(score < 378 && score >= 360)
        letter_grade = "D-";
    if(score < 360)
        letter_grade = "E";
    return letter_grade;
}
