/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include <iostream>
#include <cstring>
#include <memory>

#include "eckit/utils/Hash.h"

#include "eckit/testing/Test.h"

using namespace std;
using namespace eckit;
using namespace eckit::testing;

namespace eckit {
namespace test {

//----------------------------------------------------------------------------------------------------------------------


std::vector<string> tests = {"", "a", "abc", "message digest", "abcdefghijklmnopqrstuvwxyz",
                             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
                             "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
                             "The quick brown fox jumps over the lazy cog"};

std::map<string, std::vector<string>> results = {
    {"nOnE", {
        "",     //""
        "",     //"a"
        "",     //"abc"
        "",     //"message digest"
        "",     //"abcdefghijklmnopqrstuvwxyz"
        "",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        "",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
        "",     //"The quick brown fox jumps over the lazy cog"
        "",     //"The quick brown fox jumps over the lazy cog" x 2

    }},
    {"md4", {
        "31d6cfe0d16ae931b73c59d7e0c089c0",     //""
        "bde52cb31de33e46245e05fbdbd6fb24",     //"a"
        "a448017aaf21d8525fc10ae87aa6729d",     //"abc"
        "d9130a8164549fe818874806e1c7014b",     //"message digest"
        "d79e1c308aa5bbcdeea8ed63df412da9",     //"abcdefghijklmnopqrstuvwxyz"
        "043f8582f241db351ce627e153e7f0e4",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        "e33b4ddc9c38f2199c3e7b164fcc0536",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
        "b86e130ce7028da59e672d56ad0113df",     //"The quick brown fox jumps over the lazy cog"
        "16b52abf32d2796d53c6202a877a69a0",     //"The quick brown fox jumps over the lazy cog" x 2

    }},
    {"MD4", {
        "31d6cfe0d16ae931b73c59d7e0c089c0",     //""
        "bde52cb31de33e46245e05fbdbd6fb24",     //"a"
        "a448017aaf21d8525fc10ae87aa6729d",     //"abc"
        "d9130a8164549fe818874806e1c7014b",     //"message digest"
        "d79e1c308aa5bbcdeea8ed63df412da9",     //"abcdefghijklmnopqrstuvwxyz"
        "043f8582f241db351ce627e153e7f0e4",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        "e33b4ddc9c38f2199c3e7b164fcc0536",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
        "b86e130ce7028da59e672d56ad0113df",     //"The quick brown fox jumps over the lazy cog"
        "16b52abf32d2796d53c6202a877a69a0",     //"The quick brown fox jumps over the lazy cog" x 2

    }},
    {"MD5", {
        "d41d8cd98f00b204e9800998ecf8427e",     //""
        "0cc175b9c0f1b6a831c399e269772661",     //"a"
        "900150983cd24fb0d6963f7d28e17f72",     //"abc"
        "f96b697d7cb7938d525a2f31aaf161d0",     //"message digest"
        "c3fcd3d76192e4007dfb496cca67e13b",     //"abcdefghijklmnopqrstuvwxyz"
        "d174ab98d277d9f5a5611c2c9f419d9f",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        "57edf4a22be3c955ac49da2e2107b67a",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
        "1055d3e698d289f2af8663725127bd4b",     //"The quick brown fox jumps over the lazy cog"
        "f6c40218ba016d723a32a43e3c96a929",     //"The quick brown fox jumps over the lazy cog" x 2
    }},
    {"SHA1", {
         "da39a3ee5e6b4b0d3255bfef95601890afd80709",     //""
         "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",     //"a"
         "a9993e364706816aba3e25717850c26c9cd0d89d",     //"abc"
         "c12252ceda8be8994d5fa0290a47231c1d16aae3",     //"message digest"
         "32d10c7b8cf96570ca04ce37f2a19d84240d3a89",     //"abcdefghijklmnopqrstuvwxyz"
         "761c457bf73b14d27e9e9265c46f4b4dda11f940",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
         "50abf5706a150990a08b2c5ea40fa0e585554732",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
         "de9f2c7fd25e1b3afad3e85a0bd17d9b100db4b3",     //"The quick brown fox jumps over the lazy cog"
         "6f020371ced46a2989a2161a113f34ccd35e0729",     //"The quick brown fox jumps over the lazy cog" x 2
    }},
    {"xxHash", {
         "ef46db3751d8e999",     //""
         "d24ec4f1a98c6e5b",     //"a"
         "44bc2cf5ad770999",     //"abc"
         "066ed728fceeb3be",     //"message digest"
         "cfe1f278fa89835c",     //"abcdefghijklmnopqrstuvwxyz"
         "aaa46907d3047814",     //"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
         "e04a477f19ee145d",     //"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
         "2dcf47703493b6ca",     //"The quick brown fox jumps over the lazy cog"
         "e32a7da747f1bd6e",     //"The quick brown fox jumps over the lazy cog" x 2
    }},
 };

void testHash(Hash &hash, std::string hashName) {
    auto hashResults = results.find(hashName);
    if (hashResults == results.end())
        return;

    for(int i=0; i<tests.size(); i++) {
        std::string testResults = hashResults->second[i];
//        if (testResults.empty())
//            break;

        hash.reset();
        hash.add(tests[i].c_str(), tests[i].size());
        EXPECT(testResults == hash.digest());

        // constructor-based initialization
        Hash *hashInitialized = HashFactory::instance().build(hashName, tests[i]);
        EXPECT(testResults == hashInitialized->digest());

        // initialize with dummy data and test reset
        hashInitialized = HashFactory::instance().build(hashName, "dummy data");
        hashInitialized->reset();
        hashInitialized->add(tests[i].c_str(), tests[i].size());
        EXPECT(testResults == hashInitialized->digest());
    }

    // test add
    std::string testResults = hashResults->second[tests.size()];
//    if (!testResults.empty()) {
        hash.reset();
        hash.add(tests[tests.size()-1].c_str(), tests[tests.size()-1].size());
        hash.add(tests[tests.size()-1].c_str(), tests[tests.size()-1].size());
        EXPECT(testResults == hash.digest());
//    }
}


CASE("Hashing") {
    std::unique_ptr<Hash> hash;

    SECTION("Default Hashing") {
        EXPECT_NO_THROW(hash.reset(HashFactory::instance().build()));
        testHash(*hash, "MD5");
    }

    SECTION("Not Existing Hashing") {
        EXPECT_THROWS(HashFactory::instance().build("dummy name"));
    }

    for (auto const& element: results) {
        if (HashFactory::instance().has(element.first)) {
            SECTION(element.first+" Hashing") {
                EXPECT_NO_THROW(hash.reset(HashFactory::instance().build(element.first)));
                testHash(*hash, element.first);
            }
        }
    }
}

//----------------------------------------------------------------------------------------------------------------------

}  // end namespace test
}  // end namespace eckit

int main(int argc, char* argv[]) {
    return run_tests(argc, argv);
}
