#include "StateChecksum.h"

#include <sstream>

void Hasher::appendBytes(const void* data, std::size_t byteCount)
{
    const unsigned char* bytes = static_cast<const unsigned char*>(data);
    for (std::size_t i = 0; i < byteCount; ++i)
    {
        m_state ^= bytes[i];
        m_state *= 1099511628211ull;  // FNV-1a 64-bit prime
    }
}

void Hasher::append(float value)
{
    // Normalize -0.0f to +0.0f so the two equal values share a fingerprint.
    if (value == 0.0f) { value = 0.0f; }
    appendBytes(&value, sizeof(value));
}

void Hasher::append(double value)
{
    if (value == 0.0) { value = 0.0; }
    appendBytes(&value, sizeof(value));
}

void Hasher::append(const QPoint& point)
{
    const int coords[2] = { point.x(), point.y() };
    appendBytes(coords, sizeof(coords));
}

void Hasher::append(const QPointF& point)
{
    append(point.x());
    append(point.y());
}

void Hasher::append(const QVector2D& vector)
{
    append(vector.x());
    append(vector.y());
}

void Hasher::append(const std::string& text)
{
    appendBytes(text.data(), text.size());
    // Length terminator so "ab"+"c" and "a"+"bc" do not collide.
    const std::size_t length = text.size();
    appendBytes(&length, sizeof(length));
}

std::uint64_t fingerprintRng(const std::mt19937& rng)
{
    std::ostringstream stream;
    stream << rng;  // full internal state as space-separated integers
    Hasher hasher;
    hasher.append(stream.str());
    return hasher.getValue();
}
