#include <cstring>
#include <random>

#include <PasswordHash/PasswordHash.hpp>
#include <Log/Log.hpp>
#include <Utilities/Extern.hpp>

PasswordHash::PasswordHash()
{
    std::memset(padding, 0, sizeof(padding));
}

PasswordHash::~PasswordHash()
{
    std::memset(padding, 0, sizeof(padding));
}

PasswordHash& PasswordHash::operator=(std::string password)
{
    std::memset(padding, 0, sizeof(padding));
    scrypt.type = PasswordHashType::Scrypt;

    std::independent_bits_engine<std::default_random_engine, 8, unsigned short> rbe;
    std::generate(scrypt.salt, scrypt.salt + sizeof(scrypt.salt), rbe);

    CryptoPP::Scrypt scryptHasher;
    scryptHasher.DeriveKey(
        scrypt.digest, sizeof(scrypt.digest),
        (CryptoPP::byte*) password.c_str(), password.size(),
        scrypt.salt, sizeof(scrypt.salt)
    );

    return *this;
}

bool PasswordHash::operator==(std::string password)
{
    if (type == PasswordHashType::Scrypt)
    {
        uint8_t candidateDigest[64];

        CryptoPP::Scrypt scryptHasher;
        scryptHasher.DeriveKey(
            candidateDigest, sizeof(candidateDigest),
            (CryptoPP::byte*) password.c_str(), password.size(),
            scrypt.salt, sizeof(scrypt.salt)
        );

        return std::memcmp(candidateDigest, scrypt.digest, sizeof(candidateDigest)) == 0;
    }
    else
    {
        crash(formatString("FATAL: Unrecognized password hash type $+", static_cast<int>(type)));
    }
}