#include "Core.hpp"
#include "MayaFlux/Core/Engine.hpp"

#include "MayaFlux/Journal/Archivist.hpp"
#include "MayaFlux/Transitive/Memory/Persist.hpp"

namespace MayaFlux {

//-------------------------------------------------------------------------
// Internal Functions and Data
//-------------------------------------------------------------------------

namespace internal {

    std::unique_ptr<Core::Engine> engine_ref;
    bool initialized = false;
    bool terminating = false;
    std::recursive_mutex engine_mutex;

    void cleanup_engine()
    {
        std::lock_guard<std::recursive_mutex> lock(engine_mutex);
        if (engine_ref) {
            if (initialized) {
                engine_ref->End();
                Journal::Archivist::shutdown();
            }
            cleanup_persistent_store();
            engine_ref.reset();
            initialized = false;
        }
    }

    Core::Engine& get_or_create_engine()
    {
        std::lock_guard<std::recursive_mutex> lock(engine_mutex);
        if (!engine_ref) {
            engine_ref = std::make_unique<Core::Engine>();
            std::atexit(cleanup_engine);
        }
        terminating = false;
        initialized = true;
        return *engine_ref;
    }

}

//-------------------------------------------------------------------------
// Engine Management
//-------------------------------------------------------------------------

bool is_initialized()
{
    if (internal::terminating) {
        return false;
    }
    return internal::initialized;
}

bool is_configured()
{
    if (!is_initialized()) {
        return false;
    }
    return get_context().is_configured();
}

Core::Engine& get_context()
{
    return internal::get_or_create_engine();
}

void set_and_transfer_context(Core::Engine instance)
{
    bool is_same_instance = false;

    {
        std::lock_guard<std::recursive_mutex> lock(internal::engine_mutex);
        is_same_instance = (&instance == internal::engine_ref.get());
        if (internal::engine_ref && !is_same_instance && internal::engine_ref->is_running()) {
            internal::engine_ref->Pause();
        }
    }

    if (internal::engine_ref && !is_same_instance) {
        internal::engine_ref->End();
    }

    if (!is_same_instance) {
        std::lock_guard<std::recursive_mutex> lock(internal::engine_mutex);
        internal::engine_ref = std::make_unique<Core::Engine>(std::move(instance));
        internal::initialized = true;
    }
}

void Init()
{
    auto& engine = internal::get_or_create_engine();
    engine.Init();
}

void Init(uint32_t sample_rate, uint32_t buffer_size, uint32_t num_out_channels, uint32_t num_in_channels)
{
    auto& engine = internal::get_or_create_engine();
    auto& stream_info = engine.get_stream_info();

    stream_info.sample_rate = sample_rate;
    stream_info.buffer_size = buffer_size;
    stream_info.output.channels = num_out_channels;
    stream_info.input.channels = num_in_channels;
    engine.Init();
}

void Init(Core::GlobalStreamInfo stream_info, Core::GlobalGraphicsConfig graphics_config, Core::GlobalInputConfig input_config, Core::GlobalNetworkConfig network_config)
{
    auto& engine = internal::get_or_create_engine();
    engine.Init(stream_info, graphics_config, input_config, network_config);
}

void Start()
{
    get_context().Start();
}

void Pause()
{
    if (internal::initialized) {
        get_context().Pause();
    }
}

void Resume()
{
    if (internal::initialized) {
        get_context().Resume();
    }
}

void Await()
{
    if (internal::initialized) {
        get_context().await_shutdown();
    }
}

void End()
{
    internal::terminating = true;
    if (internal::initialized) {
        internal::cleanup_engine();
    }
}

}
