#include "./includes/3thparty/CLI11.hpp"
#include "./includes/aggregate_tests.h"
#include <iostream>

// 3. folly F14ValueMap // lots of dependencies, even tho it really seems cool
// 4. folly F14NodeMap
// 5. Tessil/ordered-map [n] something is wrong with this one, verrrrry slow
// 6. Tessil/array-hash[n] (not with a small modification of the insert function)

string choicetext = "Select implementation to test: '-i 1 2 3 4' or '-i 1,2,3'. Default is all  \n"
"1. std::unordered_hashmap(1) \n"
"2. google:: hashmaps(2) \n"
"3. abseil's absl:: hashmaps(2) \n"
"4. tessil's tsl:: hashmaps(3) \n"
"5. boost::unordered_map(1) \n"
"6. Skarupke ska:: hashmaps(3) \n"
"7. greg7ndp's  phmap:: hashmaps(2) \n"
"8. emilk's emilib:: hashmap(1) \n"
"9. martin's robin_hood:: hashmaps(2) \n";

// default arguments
vector<int> hashmaps = {1, 2, 3, 4, 5, 6, 7, 8, 9};
bool use_abseil_hash = false;
int runs = 1;
int maxsize = 50000000;



/*
int_test_aggregate and string_test_aggregate are called for different hashmaps
based on the choices
see implementation of these in includes/aggregate_tests
*/
int main(int argc, char **argv) {
  // This is just stuff to add options like selecting which hashmaps need to get benchmarked
    CLI::App app{"Hashmap benchmarks"};
    app.add_option("-i,--implementation", hashmaps, choicetext )->delimiter(',');
    app.add_option("-a,--abseil", use_abseil_hash, "use absl::Hash, default is false");
    app.add_option("-r,--runs", runs, "total runs for each map, default is 1");
    app.add_option("-m, --maxsize", maxsize, "The max size of the hashmaps to test for. Default is 50 million.");
    CLI11_PARSE(app, argc, argv);
    time_point<steady_clock> start_test = steady_clock::now();
    // calls int_test_aggregate and it's string version for different hashmaps based on the choices selected
    if (use_abseil_hash) {
        for (auto i : hashmaps) {
            switch (i) {
                case 1: {
                    int_test_aggregate(std::unordered_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(std::unordered_map<string, string>{}, runs, maxsize);
                    break;
                }

                case 2: {
                    int_test_aggregate(google::sparse_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(google::sparse_hash_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(google::dense_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(google::dense_hash_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 3: {
                    int_test_aggregate(absl::node_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(absl::node_hash_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(absl::flat_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(absl::flat_hash_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 4: {
                    int_test_aggregate(tsl::sparse_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(tsl::sparse_map<string, string>{}, runs, maxsize);
                    // int_test_aggregate(tsl::array_map<int, int>{}, runs, maxsize);
                    // string_test_aggregate(tsl::array_map<string, string>{}, runs, maxsize);
                    // int_test_aggregate(tsl::ordered_map<int, int>{}, runs, maxsize);
                    // string_test_aggregate(tsl::ordered_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(tsl::robin_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(tsl::robin_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(tsl::hopscotch_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(tsl::hopscotch_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 5: {
                    int_test_aggregate(boost::unordered_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(boost::unordered_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 6: {
                    int_test_aggregate(ska::unordered_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(ska::unordered_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(ska::bytell_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(ska::bytell_hash_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(ska::flat_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(ska::flat_hash_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 7: {
                    int_test_aggregate(phmap::parallel_flat_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(phmap::parallel_flat_hash_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(phmap::parallel_node_hash_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(phmap::parallel_node_hash_map<string, string>{}, runs, maxsize);
                    break;
                }
                case 8: {
                    int_test_aggregate(emilib::HashMap<int, int>{}, runs, maxsize);
                    string_test_aggregate(emilib::HashMap<string, string>{}, runs, maxsize);
                    break;
                }
                case 9: {
                    int_test_aggregate(robin_hood::unordered_flat_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(robin_hood::unordered_flat_map<string, string>{}, runs, maxsize);
                    int_test_aggregate(robin_hood::unordered_node_map<int, int>{}, runs, maxsize);
                    string_test_aggregate(robin_hood::unordered_node_map<string, string>{}, runs, maxsize);
                    break;
                }
            }

        }
    } else {
        for (auto i : hashmaps) {
            switch (i) {
                case 1: {
                    int_test_aggregate(std::unordered_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(std::unordered_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }

                case 2: {
                    int_test_aggregate(google::sparse_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(google::sparse_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(google::dense_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(google::dense_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 3: {
                    int_test_aggregate(absl::node_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(absl::node_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(absl::flat_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(absl::flat_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 4: {
                    int_test_aggregate(tsl::sparse_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(tsl::sparse_map<string, string, Hash<string>>{}, runs, maxsize);
                    // int_test_aggregate(tsl::array_map<int, int, Hash<int>>{}, runs, maxsize);
                    // string_test_aggregate(tsl::array_map<string, string, Hash<string>>{}, runs, maxsize);
                    // int_test_aggregate(tsl::ordered_map<int, int, Hash<int>>{}, runs, maxsize);
                    // string_test_aggregate(tsl::ordered_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(tsl::robin_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(tsl::robin_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(tsl::hopscotch_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(tsl::hopscotch_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 5: {
                    int_test_aggregate(boost::unordered_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(boost::unordered_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 6: {
                    int_test_aggregate(ska::unordered_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(ska::unordered_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(ska::bytell_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(ska::bytell_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(ska::flat_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(ska::flat_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 7: {
                    int_test_aggregate(phmap::parallel_flat_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(phmap::parallel_flat_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(phmap::parallel_node_hash_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(phmap::parallel_node_hash_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 8: {
                    int_test_aggregate(emilib::HashMap<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(emilib::HashMap<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }
                case 9: {
                    int_test_aggregate(robin_hood::unordered_flat_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(robin_hood::unordered_flat_map<string, string, Hash<string>>{}, runs, maxsize);
                    int_test_aggregate(robin_hood::unordered_node_map<int, int, Hash<int>>{}, runs, maxsize);
                    string_test_aggregate(robin_hood::unordered_node_map<string, string, Hash<string>>{}, runs, maxsize);
                    break;
                }

            }

        }

    }
    time_point<steady_clock> end_test = steady_clock::now();
    std::cout << "\n\n" << runs << " runs for all tests for " << hashmaps.size() << "maps: " 
    << duration_cast<seconds>(end_test - start_test).count() << " seconds\n\n";

}



