#include <fstream>
#include <iostream>
#include <unordered_map>
#include <vector>

struct Textonym_Checker {
private:
    int total;
    int elements;
    int textonyms;
    int max_found;
    std::vector<std::string> max_strings;
    std::unordered_map<std::string, std::vector<std::string>> values;

    int get_mapping(std::string &result, const std::string &input)
    {
        static std::unordered_map<char, char> mapping = {
            {'A', '2'}, {'B', '2'}, {'C', '2'},
            {'D', '3'}, {'E', '3'}, {'F', '3'},
            {'G', '4'}, {'H', '4'}, {'I', '4'},
            {'J', '5'}, {'K', '5'}, {'L', '5'},
            {'M', '6'}, {'N', '6'}, {'O', '6'},
            {'P', '7'}, {'Q', '7'}, {'R', '7'}, {'S', '7'},
            {'T', '8'}, {'U', '8'}, {'V', '8'},
            {'W', '9'}, {'X', '9'}, {'Y', '9'}, {'Z', '9'}
        };

        result = input;
        for (char &c : result) {
            if (!isalnum(c)) return 0;
            if (isalpha(c)) c = mapping[toupper(c)];
        }

        return 1;
    }

public:
    Textonym_Checker(void) : total(0), elements(0), textonyms(0), max_found(0) { }

    ~Textonym_Checker(void) { }

    void add(const std::string &str) {
        std::string mapping;
        total += 1;

        if (!get_mapping(mapping, str)) return;

        const int num_strings = values[mapping].size();

        textonyms += num_strings == 1 ? 1 : 0;
        elements  += 1;

        if (num_strings > max_found) {
            max_strings.clear();
            max_strings.push_back(mapping);
            max_found = num_strings;
        }
        else if (num_strings == max_found) {
            max_strings.push_back(mapping);
        }

        values[mapping].push_back(str);
    }

    void results(const std::string &filename) {
        std::cout << "Read " << total << " words from " << filename << "\n\n";

        std::cout << "There are " << elements << " words in " << filename;
        std::cout << " which can be represented by the digit key mapping.\n";
        std::cout << "They require " << values.size() <<
                     " digit combinations to represent them.\n";
        std::cout << textonyms << " digit combinations represent Textonyms.\n\n";
        std::cout << "The numbers mapping to the most words map to ";
        std::cout << max_found + 1 << " words each:\n";

        for (auto it1 = max_strings.begin(); it1 != max_strings.end(); ++it1) {
            std::cout << '\t' << *it1 << " maps to: ";
            for (auto it2 = values[*it1].begin(); it2 != values[*it1].end(); ++it2) {
                std::cout << *it2 << " ";
            }
            std::cout << "\n";
        }
        std::cout << '\n';
    }

    void match(const std::string &str) {
        auto match = values.find(str);

        if (match == values.end()) {
            std::cout << "Key '" << str << "' not found\n";
        }
        else {
            std::cout << "Key '" << str << "' matches: ";
            for (auto it = values[str].begin(); it != values[str].end(); ++it)
                std::cout << *it << " ";
            std::cout << '\n';
        }
    }
};

int main(void)
{
    std::string filename = "unixdict.txt";
    std::ifstream input(filename);
    Textonym_Checker tc;

    if (input.is_open()) {
        std::string line;
        while (getline(input, line))
            tc.add(line);
    }

    input.close();

    tc.results(filename);
    tc.match("001");
    tc.match("228");
    tc.match("27484247");
    tc.match("7244967473642");
}
