#pragma once

#include <juce_core/juce_core.h>
#include <array>
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>

namespace deeppluck::benchmark
{
inline void require (bool condition, const std::string& message)
{
    if (! condition) throw std::runtime_error (message);
}
inline juce::var object() { return juce::var (new juce::DynamicObject); }
inline void put (juce::var& value, const char* key, const juce::var& item)
{
    value.getDynamicObject()->setProperty (key, item);
}
inline void put (juce::var& value, const char* key, const std::string& item)
{
    put(value,key,juce::var(juce::String(item)));
}
inline void put (juce::var& value, const char* key, const char* item)
{
    put(value,key,juce::var(item));
}
inline void put (juce::var& value, const char* key, const juce::String& item)
{
    put(value,key,juce::var(item));
}
template <typename Range> juce::var array (const Range& values)
{
    juce::Array<juce::var> result;
    for (const auto& value : values) result.add (juce::var (value));
    return result;
}
inline juce::String json (const juce::var& value)
{
    return juce::JSON::toString (value, true, 17);
}
inline void saveJson (const juce::File& file, const juce::var& value)
{
    require (! file.exists(), "Refusing to overwrite evidence: " + file.getFullPathName().toStdString());
    require (file.replaceWithText (json (value)), "Cannot write JSON");
}
inline juce::var readJson (const juce::File& file)
{
    require (file.existsAsFile(), "Missing JSON: " + file.getFullPathName().toStdString());
    juce::var value;
    require (juce::JSON::parse (file.loadFileAsString(), value).wasOk() && value.isObject(), "Invalid JSON");
    return value;
}
juce::String sha256 (const void* data, size_t bytes);
juce::String fileSha256 (const juce::File& file);
inline juce::String jsonSha256 (const juce::var& value)
{
    const auto text = json (value);
    return sha256 (text.toRawUTF8(), text.getNumBytesAsUTF8());
}
}
