// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include "arcana/errors.h"
#include "arcana/expected.h"
#include "arcana/functional/inplace_function.h"
#include "arcana/iterators.h"
#include "arcana/type_traits.h"

#include "cancellation.h"

#include <gsl/gsl>
#include <memory>
#include <stdexcept>

#include <atomic>

namespace mira
{
    template<typename ResultT>
    class task_completion_source;

    //
    // A scheduler that will invoke the continuation inline
    // right after the previous task.
    //
    namespace
    {
        struct inline_scheduler_t
        {
            template<typename CallableT>
            constexpr void queue(CallableT&& callable) const
            {
                callable();
            }
        };

        constexpr inline_scheduler_t inline_scheduler{};
    }
}

#include "internal/internal_task.h"

namespace mira
{
    //
    //  Generic task system to run work with continuations on a generic scheduler.
    //
    //  The scheduler on which tasks are queued must satisfy this contract:
    //
    //      struct scheduler
    //      {
    //          template<CallableT>
    //          void queue(CallableT&& callable)
    //          {
    //              callable must be usable like this: callable();
    //          }
    //      };
    //
    template<typename ResultT>
    class task
    {
        using payload_t = internal::base_task_payload_with_return<ResultT>;
        using payload_ptr = std::shared_ptr<payload_t>;

        static_assert(std::is_same<typename as_expected<ResultT>::result, ResultT>::value,
            "task can't be of expected<T>");

    public:
        using result_t = ResultT;

        task() = default;

        task(const task& other) = default;
        task(task&& other) = default;

        task(const task_completion_source<ResultT>& source)
            : m_payload{ source.m_payload }
        {}

        task(task_completion_source<ResultT>&& source)
            : m_payload{ std::move(source.m_payload) }
        {}

        task& operator=(task&& other) = default;
        task& operator=(const task& other) = default;

        bool operator==(const task& other)
        {
            return m_payload == other.m_payload;
        }

        //
        // Executes a callable on this scheduler once this task is finished and
        // returns a task that represents the callable.
        //
        // Calling .then() on the returned task will queue a task to run after the
        // callable is run.
        //
        template<typename SchedulerT, typename CallableT>
        auto then(SchedulerT& scheduler, cancellation& token, CallableT&& callable)
        {
            return internal::continuation_factory<ResultT>::create_continuation_task(
                internal::input_output_wrapper<ResultT>::wrap_callable(std::forward<CallableT>(callable), token),
                scheduler,
                m_payload);
        }

    private:
        explicit task(payload_ptr payload)
            : m_payload{ std::move(payload) }
        {}

        template<typename CallableT, typename InputT>
        explicit task(CallableT&& callable, type_of<InputT> input)
        {
            auto payload = std::make_shared<internal::task_payload<ResultT, sizeof(CallableT)>>(
                std::forward<CallableT>(callable), input);

            m_payload = std::move(payload);
        }

        template<typename OtherResultT, size_t WorkSize>
        friend struct internal::task_payload;

        template<typename OtherResultT>
        friend class task;

        friend class task_completion_source<ResultT>;

        template<typename OtherResultT, typename InputT>
        friend struct internal::task_factory;

        template<typename OtherResultT>
        friend struct internal::continuation_factory;

        template<typename SchedulerT, typename CallableT>
        friend auto make_task(SchedulerT& scheduler, cancellation& token, CallableT&& callable) -> typename internal::task_factory<typename as_expected<decltype(callable())>::result, void>::task_t;

        template<typename OtherResultT>
        friend task<typename as_expected<OtherResultT>::result> task_from_result(OtherResultT&& value);

        friend task<void> task_from_result();

        template<typename OtherResultT, typename ErrorT>
        friend task<OtherResultT> task_from_error(const ErrorT& error);

        payload_ptr m_payload;
    };
}

namespace mira
{
    template<typename ResultT>
    class task_completion_source
    {
        using payload_t = internal::noop_task_payload<ResultT>;
        using payload_ptr = std::shared_ptr<payload_t>;
        using uninitialized = std::integral_constant<int, 0>;

    public:
        using result_t = ResultT;

        task_completion_source()
            : m_payload{ std::make_shared<payload_t>() }
        {}

        static task_completion_source<ResultT> make_uninitialized()
        {
            return task_completion_source<ResultT>{ uninitialized{} };
        }

        //
        // Completes the task this source represents.
        //
        void complete()
        {
            static_assert(std::is_same<ResultT, void>::value,
                "complete with no arguments can only be used with a void completion source");
            m_payload->complete(expected<void>::make_valid());
        }

        //
        // Completes the task this source represents.
        //
        template<typename ValueT>
        void complete(ValueT&& value)
        {
            m_payload->complete(std::forward<ValueT>(value));
        }

        //
        // Returns whether or not the current source has already been completed.
        //
        bool completed() const
        {
            return m_payload->completed();
        }

        //
        // Converts this task_completion_source to a task object for consumers to use.
        //
        task<ResultT> as_task() const &
        {
            return task<ResultT>{ m_payload };
        }

        task<ResultT> as_task() &&
        {
            return task<ResultT>{ std::move(m_payload) };
        }

    private:
        explicit task_completion_source(uninitialized)
        {}

        explicit task_completion_source(std::shared_ptr<payload_t> payload)
            : m_payload{ std::move(payload) }
        {}

        friend class task<ResultT>;
        friend class abstract_task_completion_source;

        template<typename R, typename I>
        friend struct internal::task_factory;

        payload_ptr m_payload;
    };

    //
    // a type erased version of task_completion_source.
    //
    class abstract_task_completion_source
    {
        using payload_t = internal::base_task_payload;
        using payload_ptr = std::shared_ptr<payload_t>;

    public:
        abstract_task_completion_source()
            : m_payload{}
        {}

        template<typename T>
        explicit abstract_task_completion_source(const task_completion_source<T>& other)
            : m_payload{ other.m_payload }
        {}

        template<typename T>
        explicit abstract_task_completion_source(task_completion_source<T>&& other)
            : m_payload{ std::move(other.m_payload) }
        {}

        //
        // Returns whether or not the current source has already been completed.
        //
        bool completed() const
        {
            return m_payload->completed();
        }

        template<typename T>
        bool operator==(const task_completion_source<T>& other)
        {
            return m_payload == other.m_payload;
        }

        template<typename T>
        task_completion_source<T> unsafe_cast()
        {
            return task_completion_source<T>{ std::static_pointer_cast<internal::noop_task_payload<T>>(m_payload) };
        }

    private:
        payload_ptr m_payload;
    };

    //
    // creates a task and queues it to run on the given scheduler
    //
    template<typename SchedulerT, typename CallableT>
    inline auto make_task(SchedulerT& scheduler, cancellation& token, CallableT&& callable) -> typename internal::task_factory<typename as_expected<decltype(callable())>::result, void>::task_t
    {
        using callable_return_t = typename as_expected<decltype(callable())>::result;
        using wrapper = internal::input_output_wrapper<void>;

        internal::task_factory<callable_return_t, void> factory(
            wrapper::wrap_callable(std::forward<CallableT>(callable), token));

        scheduler.queue([to_run = std::move(factory.to_run)]{ to_run.m_payload->run(nullptr); });

        return factory.to_return;
    }

    //
    // creates a completed task from the given result
    //
    template<typename ResultT>
    inline task<typename as_expected<ResultT>::result> task_from_result(ResultT&& value)
    {
        task_completion_source<typename as_expected<ResultT>::result> result;
        result.complete(std::forward<ResultT>(value));
        return std::move(result);
    }

    inline task<void> task_from_result()
    {
        task_completion_source<void> result;
        result.complete();
        return std::move(result);
    }

    template<typename ResultT, typename ErrorT>
    inline task<ResultT> task_from_error(const ErrorT& error)
    {
        task_completion_source<ResultT> result;
        result.complete(std::make_error_code(error));
        return std::move(result);
    }

    template<typename ResultT>
    inline task<ResultT> task_from_error(const std::error_code& error)
    {
        task_completion_source<ResultT> result;
        result.complete(error);
        return std::move(result);
    }

    inline task<void> when_all(gsl::span<task<void>> tasks)
    {
        if (tasks.empty())
        {
            return task_from_result();
        }

        struct when_all_data
        {
            std::mutex mutex;
            size_t pendingCount;
            std::error_code error;
        };

        task_completion_source<void> result;
        auto data = std::make_shared<when_all_data>();
        data->pendingCount = tasks.size();

        for (task<void>& task : tasks)
        {
            task.then(inline_scheduler, cancellation::none(), [data, result](const expected<void>& exp) mutable {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pendingCount -= 1;
                    last = data->pendingCount == 0;

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(data->error);
                    }
                    else
                    {
                        result.complete();
                    }
                }
            });
        }

        return std::move(result);
    }

    template<typename T>
    inline task<std::vector<T>> when_all(gsl::span<task<T>> tasks)
    {
        if (tasks.empty())
        {
            return task_from_result<std::vector<T>>(std::vector<T>());
        }

        struct when_all_data
        {
            std::mutex mutex;
            size_t pendingCount;
            std::error_code error;
            std::vector<T> results;
        };

        task_completion_source<std::vector<T>> result;
        auto data = std::make_shared<when_all_data>();
        data->pendingCount = tasks.size();
        data->results.resize(tasks.size());

        //using forloop with index to be able to keep proper order of results
        for (auto idx = 0U; idx < data->results.size(); idx++)
        {
            tasks[idx].then(mira::inline_scheduler, cancellation::none(), [data, result, idx](const expected<T>& exp) mutable {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pendingCount -= 1;
                    last = data->pendingCount == 0;

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                    if (exp.has_value())
                    {
                        data->results[idx] = exp.value();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(data->error);
                    }
                    else
                    {
                        result.complete(data->results);
                    }
                }
            });
        }

        return std::move(result);
    }

    template<typename... ArgTs>
    inline task<std::tuple<typename mira::void_passthrough<ArgTs>::type...>> when_all(task<ArgTs>... tasks)
    {
        using void_passthrough_tuple = std::tuple<typename mira::void_passthrough<ArgTs>::type...>;

        struct when_all_data
        {
            std::mutex mutex;
            int pending;
            std::error_code error;
            void_passthrough_tuple results;
        };

        task_completion_source<void_passthrough_tuple> result;

        auto data = std::make_shared<when_all_data>();
        data->pending = std::tuple_size<void_passthrough_tuple>::value;

        std::tuple<task<ArgTs>&...> taskrefs = std::make_tuple(std::ref(tasks)...);

        iterate_tuple(taskrefs, [&](auto& task, auto idx) {
            using task_t = std::remove_reference_t<decltype(task)>;

            task.then(inline_scheduler,
                cancellation::none(),
                [data, result](const expected<typename task_t::result_t>& exp) mutable {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pending -= 1;
                    last = data->pending == 0;

                    internal::write_expected_to_tuple<decltype(idx)::value>(data->results, exp);

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(data->error);
                    }
                    else
                    {
                        result.complete(std::move(data->results));
                    }
                }
            });
        });
        return std::move(result);
    }
}


#pragma once

#include <Windows.h>

#include <cstdint>
#include <string>
#include <vector>

#include <gsl/span>
#include "hid_handle.h"

namespace hid
{
	struct HidOpenFlags
	{
		enum T : uint32_t
		{
			none,
			exclusive = 1u << 0u,
			async     = 1u << 1u
		};
	};

	using HidOpenFlags_t = uint32_t;

	struct HidCaps
	{
		uint16_t usage;
		uint16_t usagePage;
		uint16_t inputReportSize;
		uint16_t outputReportSize;
		uint16_t featureReportSize;
		uint16_t linkCollectionNodes;
		uint16_t inputButtonCaps;
		uint16_t inputValueCaps;
		uint16_t inputDataIndices;
		uint16_t outputButtonCaps;
		uint16_t outputValueCaps;
		uint16_t outputDataIndices;
		uint16_t featureButtonCaps;
		uint16_t featureValueCaps;
		uint16_t featureDataIndices;
	};

	struct HidAttributes
	{
		uint16_t vendorId;
		uint16_t productId;
		uint16_t versionNumber;
	};

	class HidInstance
	{
		HidOpenFlags_t flags = 0;

		Handle handle = Handle(nullptr, true);

		HidCaps caps_ {};
		HidAttributes attributes_ {};

		OVERLAPPED overlappedIn = {};
		OVERLAPPED overlappedOut = {};

		bool pendingRead_ = false;
		bool pendingWrite_ = false;

		size_t nativeError_ = 0;

	public:
		std::wstring path;
		std::wstring instanceId;
		std::wstring serialString;

		std::vector<uint8_t> inputBuffer;
		std::vector<uint8_t> outputBuffer;

		HidInstance(const HidInstance&) = delete;
		HidInstance& operator=(const HidInstance&) = delete;

		HidInstance() = default;

		HidInstance(std::wstring path, std::wstring instanceId);
		explicit HidInstance(std::wstring path);
		HidInstance(HidInstance&& other) noexcept;

		~HidInstance();

		HidInstance& operator=(HidInstance&& other) noexcept;

		bool isOpen() const;
		bool isExclusive() const;
		bool isAsync() const;
		const HidCaps& caps() const;
		const HidAttributes& attributes() const;

		bool readMetadata();
		bool readCaps();
		bool readSerial();
		bool readAttributes();
		bool getFeature(const gsl::span<uint8_t>& buffer) const;
		bool setFeature(const gsl::span<uint8_t>& buffer) const;

		bool open(HidOpenFlags_t openFlags);
		void close();

		inline auto nativeError() const
		{
			return nativeError_;
		}

		bool read(void* buffer, size_t size) const;
		bool read(const gsl::span<uint8_t>& buffer) const;
		bool read();

		bool readAsync();

		bool write(const void* buffer, size_t size) const;
		bool write(const gsl::span<const uint8_t>& buffer) const;
		bool write() const;

		bool writeAsync();

		bool asyncReadPending() const;
		bool asyncReadInProgress();
		bool asyncWritePending() const;
		bool asyncWriteInProgress();

		void cancelAsyncReadAndWait();
		void cancelAsyncWriteAndWait();

		bool setOutputReport(const gsl::span<uint8_t>& buffer) const;
		bool setOutputReport();

	private:
		void cancelAsyncAndWait(OVERLAPPED* overlapped);
		bool asyncInProgress(OVERLAPPED* overlapped);

		bool readCaps(HANDLE h);
		bool readSerial(HANDLE h);
		bool readAttributes(HANDLE h);
	};
}


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef framesys_300a49a6_c103_4a1f_8166_358e823a432b_h
#define framesys_300a49a6_c103_4a1f_8166_358e823a432b_h

#include <gslib/type.h>
#include <ariel/sysop.h>
#include <ariel/config.h>
#include <ariel/rendersys.h>

__ariel_begin__

enum frame_strategy
{
    fs_busy_loop,
    fs_lazy_passive,
};

struct app_config;
struct app_env;
class framesys;
struct frame_event;
class frame_listener;
class rose;

enum frame_event_id
{
    feid_invalid,
    feid_draw,
    feid_timer,
    feid_paint,
    feid_mouse_down,
    feid_mouse_up,
    feid_mouse_move,
    feid_key_down,
    feid_key_up,
    feid_char,
    feid_show,
    feid_create,
    feid_close,
    feid_resize,
    feid_halt,
    feid_resume,
    feid_custom,
};

struct __gs_novtable frame_event abstract
{
    virtual uint get_id() const = 0;
    virtual uint get_event_size() const = 0;
};

struct frame_draw_event:
    public frame_event
{
public:
    virtual uint get_id() const override { return feid_draw; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_close_event:
    public frame_event
{
public:
    virtual uint get_id() const override { return feid_close; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_halt_event:
    public frame_event
{
public:
    virtual uint get_id() const override { return feid_halt; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_resume_event:
    public frame_event
{
public:
    virtual uint get_id() const override { return feid_resume; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_paint_event:
    public frame_event
{
    rect            boundary;

public:
    virtual uint get_id() const override { return feid_paint; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_timer_event:
    public frame_event
{
    uint            timerid;

public:
    virtual uint get_id() const override { return feid_timer; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_show_event:
    public frame_event
{
    bool            show;

public:
    virtual uint get_id() const override { return feid_show; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_resize_event:
    public frame_event
{
    rect            boundary;

public:
    virtual uint get_id() const override { return feid_resize; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_mouse_down_event:
    public frame_event
{
    uint            modifier;
    unikey          key;
    point           position;

public:
    virtual uint get_id() const override { return feid_mouse_down; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_mouse_up_event:
    public frame_event
{
    uint            modifier;
    unikey          key;
    point           position;

public:
    virtual uint get_id() const override { return feid_mouse_up; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_mouse_move_event:
    public frame_event
{
    uint            modifier;
    point           position;

public:
    virtual uint get_id() const override { return feid_mouse_move; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_key_down_event:
    public frame_event
{
    uint            modifier;
    unikey          key;

public:
    virtual uint get_id() const override { return feid_key_down; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_key_up_event:
    public frame_event
{
    uint            modifier;
    unikey          key;

public:
    virtual uint get_id() const override { return feid_key_up; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_char_event:
    public frame_event
{
    uint            modifier;
    uint            charactor;

public:
    virtual uint get_id() const override { return feid_char; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

struct frame_create_event:
    public frame_event
{
    system_driver*  driver;
    rect            boundary;

public:
    virtual uint get_id() const override { return feid_create; }
    virtual uint get_event_size() const override { return sizeof(*this); }
};

template<frame_event_id _feid>
struct frame_event_table;

#define register_frame_event(feid, fevt) \
    template<> \
    struct frame_event_table<feid> { \
        typedef fevt type; \
    };
register_frame_event(feid_draw, frame_draw_event);
register_frame_event(feid_timer, frame_timer_event);
register_frame_event(feid_paint, frame_paint_event);
register_frame_event(feid_mouse_down, frame_mouse_down_event);
register_frame_event(feid_mouse_up, frame_mouse_up_event);
register_frame_event(feid_mouse_move, frame_mouse_move_event);
register_frame_event(feid_key_down, frame_key_down_event);
register_frame_event(feid_key_up, frame_key_up_event);
register_frame_event(feid_char, frame_char_event);
register_frame_event(feid_show, frame_show_event);
register_frame_event(feid_create, frame_create_event);
register_frame_event(feid_close, frame_close_event);
register_frame_event(feid_resize, frame_resize_event);
register_frame_event(feid_halt, frame_halt_event);
register_frame_event(feid_resume, frame_resume_event);
#undef register_frame_event

class __gs_novtable frame_listener abstract
{
public:
    virtual ~frame_listener() {}
    virtual void on_frame_start() = 0;
    virtual void on_frame_end() = 0;
    virtual bool on_frame_event(const frame_event& event) = 0;
};

class frame_dispatcher:
    public system_notify
{
public:
    virtual void on_show(bool b) override;
    virtual void on_create(system_driver* ptr, const rect& rc) override;
    virtual void on_close() override;
    virtual void on_resize(const rect& rc) override;
    virtual void on_paint(const rect& rc) override;
    virtual void on_halt() override;
    virtual void on_resume() override;
    virtual bool on_mouse_down(uint um, unikey uk, const point& pt) override;
    virtual bool on_mouse_up(uint um, unikey uk, const point& pt) override;
    virtual bool on_mouse_move(uint um, const point& pt) override;
    virtual bool on_key_down(uint um, unikey uk) override;
    virtual bool on_key_up(uint um, unikey uk) override;
    virtual bool on_char(uint um, uint ch) override;
    virtual void on_timer(uint tid) override;

protected:
    frame_listener*     _listener;

public:
    frame_dispatcher() { _listener = 0; }
    void set_listener(frame_listener* listenter) { _listener = listenter; }
    void do_draw();
    void on_frame_start();
    void on_frame_end();
};

struct frame_configs
{
    uint                handles;
    int                 width;
    int                 height;
    /* more to come.. */
};

class framesys:
    public system_driver
{
public:
    friend class frame_dispatcher;
    typedef void (framesys::*fn_empty_frame)();

public:
    void set_frame_strategy(frame_strategy stt);
    void set_notify(system_notify* notify) { _notify = notify; }
    void set_frame_listener(frame_listener* lis) { _dispatcher.set_listener(lis); }
    system_notify* get_notify() const { return _notify; }
    rendersys* get_rendersys() const { return _rendersys; }
    rose* get_rose() const { return _rose; }
    const frame_configs& get_configs() const { return _configs; }
    void initialize(const rect& rc);
    void refresh();

public:
    virtual ~framesys();
    virtual void initialize(const system_context& ctx) override;
    virtual void setup() override;
    virtual void close() override;
    virtual void set_timer(uint tid, int t) override;
    virtual void kill_timer(uint tid) override;
    virtual void update() override;
    virtual void emit(int msgid, void* msg, int size) override;
    virtual void set_ime(point pt, const font& ft) override;
    virtual void set_cursor(cursor_type curty) override;

private:
    framesys();

public:
    static int run();
    static framesys* get_framesys()
    {
        static framesys inst;
        return &inst;
    }

#if defined(WIN32) || defined(_WINDOWS)
    static void set_default_config(app_config& cfg);
    static void on_app_initialized(const app_env& env);
    static void on_app_windowed(HWND);
#endif

protected:
    void idle()
    {
        assert(_empty_frame_proc);
        (this->*_empty_frame_proc)();
    }
    void empty_frame_lazy();
    void empty_frame_busy();

protected:
    frame_strategy      _strategy;
    frame_configs       _configs;
    frame_dispatcher    _dispatcher;
    system_notify*      _notify;
    rendersys*          _rendersys;
    rose*               _rose;
    fn_empty_frame      _empty_frame_proc;
};

__ariel_end__

#endif


#ifndef VKST_PLAT_FS_NOTIFY_LINUX_H
#define VKST_PLAT_FS_NOTIFY_LINUX_H

#ifndef VKST_PLAT_FS_NOTIFY_H
#error "Include fs_notify.h only"
#endif

#include "fs_notify.h"
#include <gsl.h>
#include <vector>

namespace plat {

class fs_notify final : public impl::fs_notify<fs_notify> {
public:

private:
  //std::vector<gsl::unique_ptr<watch>> _watches;

  watch_id do_add(plat::filesystem::path path,
                  impl::fs_notify<fs_notify>::notify_delegate delegate,
                  bool recursive, std::error_code& ec) noexcept;

  void do_remove(watch_id id) noexcept;

  void do_tick() noexcept;

  friend class impl::fs_notify<fs_notify>;
}; // class fs_notify

} // namespace plat

#endif // VKST_PLAT_FS_NOTIFY_LINUX_H


#ifndef VKST_RENDERER_H
#define VKST_RENDERER_H

#include <plat/filesystem.h>
#include <wsi/window.h>
#include <vk/result.h>
#include <gsl.h>
#include <filesystem>
#include <vector>

// Holds all of the data for a surface. This includes the swapchain,
// renderpass, and framebuffers.
// Surfaces must be resized when the window is resized.
class surface {
public:
  std::size_t num_images() const noexcept { return _color_images.size(); }

  VkRenderPass render_pass() const noexcept { return _render_pass; }

  VkFramebuffer framebuffer(std::size_t index) const noexcept {
    return _framebuffers[index];
  }

  VkSampleCountFlagBits samples() const noexcept { return _samples; }

  VkViewport& viewport() noexcept { return _viewport; }
  VkViewport const& viewport() const noexcept { return _viewport; }

  VkRect2D& scissor() noexcept { return _scissor; }
  VkRect2D const& scissor() const noexcept { return _scissor; }

  surface() noexcept {}
  surface(surface const&) = delete;
  surface(surface&& other) noexcept;
  surface& operator=(surface const&) = delete;
  surface& operator=(surface&& rhs) noexcept;
  ~surface() noexcept = default;

private:
  VkSurfaceKHR _surface{VK_NULL_HANDLE};

  VkSurfaceFormatKHR _color_format{};
  VkFormat _depth_format{};
  VkSampleCountFlagBits _samples{};
  VkPresentModeKHR _present_mode{};

  VkSemaphore _image_available{VK_NULL_HANDLE};
  VkSemaphore _render_finished{VK_NULL_HANDLE};
  VkRenderPass _render_pass{VK_NULL_HANDLE};

  VkSurfaceCapabilitiesKHR _capabilities{};
  VkExtent2D _extent{};
  VkViewport _viewport{};
  VkRect2D _scissor{};
  VkSwapchainKHR _swapchain{VK_NULL_HANDLE};

  constexpr static uint32_t MAX_IMAGES = 4;
  std::vector<VkImage> _color_images{};
  std::vector<VkImageView> _color_image_views{};

  VkImage _depth_image{VK_NULL_HANDLE};
  VkDeviceMemory _depth_image_memory{VK_NULL_HANDLE};
  VkImageView _depth_image_view{VK_NULL_HANDLE};

  VkImage _color_target{VK_NULL_HANDLE};
  VkDeviceMemory _color_target_memory{VK_NULL_HANDLE};
  VkImageView _color_target_view{VK_NULL_HANDLE};

  VkImage _depth_target{VK_NULL_HANDLE};
  VkDeviceMemory _depth_target_memory{VK_NULL_HANDLE};
  VkImageView _depth_target_view{VK_NULL_HANDLE};

  std::vector<VkFramebuffer> _framebuffers{};

  friend class renderer;
}; // class surface

// Convenience class to hold both the VkShaderModule for a shader as well as
// any error message from compiling the shader.
class shader {
public:
  enum class types : uint8_t {
    vertex = 0,
    fragment = 1,
  }; // enum class types

  operator VkShaderModule() const noexcept { return _module; }

  std::string const& error_message() const noexcept { return _error_message; }

  shader() noexcept {}
  shader(shader const&) = delete;
  shader(shader&& other) noexcept;
  shader& operator=(shader const&) = delete;
  shader& operator=(shader&& rhs) noexcept;
  ~shader() noexcept = default;

private:
  VkShaderModule _module{VK_NULL_HANDLE};
  std::string _error_message{};

  friend class renderer;
}; // class shader

enum class renderer_result {
  success = 0,
  no_device = 1,
  initialization_failed = 2,
  surface_not_supported = 3,
  no_memory_type = 4,
}; // class renderer_result

class renderer_result_category_impl : public std::error_category {
public:
  virtual char const* name() const noexcept override {
    return "renderer_result";
  }

  virtual std::string message(int ev) const override;
}; // class renderer_result_category_impl

std::error_category const& renderer_result_category();

inline std::error_code make_error_code(renderer_result e) noexcept {
  return {static_cast<int>(e), renderer_result_category()};
}

enum class renderer_options : uint8_t {
  none = 0,
  use_integrated_gpu = (1 << 1),
}; // renderer_options

// Holds all of the data for rendering. Also provides methods for creating
// surfaces, shaders, pipelines, and command buffers, as well as submitting
// command buffers for execution on the device.
class renderer {
public:
  // Create a new renderer. If ec is true, then an error occurred during
  // creation and the renderer object is in an invalid state.
  static renderer create(gsl::czstring application_name, renderer_options opts,
                         PFN_vkDebugReportCallbackEXT debug_report_callback,
                         uint32_t push_constant_size,
                         std::error_code& ec) noexcept;

  // Create a new surface. If ec is true, then an error occurred during
  // creation and the surface object is in an invalid state.
  surface create_surface(wsi::window const& window,
                         std::error_code& ec) noexcept;

  // Resize a surface. Must be called when the window that was passed for
  // surface creation is resized. This is not automatically done to allow
  // the render loop to determine when to perform the resize. If ec is true,
  // then an error occurred during the resize.
  void resize(surface& s, wsi::extent2d const& extent,
              std::error_code& ec) noexcept;

  // Acquire the next ready image in the swapchain for rendering to. This
  // must be called and the returned index passed to submit_present. If
  // ec is true, then an error occurred and the returned index is invalid.
  uint32_t acquire_next_image(surface& s, std::error_code& ec) noexcept;

  // Submit a set of command buffers for execution and then present the
  // previously acquired swapchain image. image_index must come from an
  // immediately preceding call to acquire_next_image. fence can be
  // VK_NULL_HANDLE which indicates no fence should be signaled when the
  // command buffers can be reused. If ec is true, then an error occurred
  // during either the submit or present.
  void submit_present(gsl::span<VkCommandBuffer> buffers, surface& s,
                      uint32_t image_index, VkFence fence,
                      std::error_code& ec) noexcept;

  void destroy(surface& s) noexcept;

private:
  void release(surface& s) noexcept;

public:
  // Allocate a set of command buffers. If ec is true, then an error occurred
  // and the vector is invalid.
  std::vector<VkCommandBuffer>
  allocate_command_buffers(uint32_t count, std::error_code& ec) noexcept;

  // Submit a set of command buffers. onetime indicates that the submit should
  // use the onetime fence and wait for the submit to complete before
  // continuing. If ec is true, then an error occurred.
  void submit(gsl::span<VkCommandBuffer> command_buffers, bool onetime,
              std::error_code& ec) noexcept;

  void free(std::vector<VkCommandBuffer>& command_buffers) noexcept;

  // Create a new shader from the given source code. path is expected to hold
  // GLSL source code which will be compiled before creating the shader. If ec
  // is true, then an error occurred and the shader is valid such that
  // shader::error_message can be called to get any compilation errors.
  shader create_shader(plat::filesystem::path const& path, shader::types type,
                       std::error_code& ec) noexcept;

  void destroy(shader& s) noexcept;

  // Create a new pipeline layout. If ec is true, then an error occurred and
  // the pipeline layout object is invalid.
  VkPipelineLayout create_pipeline_layout(
    gsl::span<VkDescriptorSetLayout> descriptor_set_layouts,
    gsl::span<VkPushConstantRange> push_constant_ranges,
    std::error_code& ec) noexcept;

  void destroy(VkPipelineLayout layout) noexcept;

  // Create a new set of pipelines. A single pipeline can also be created.
  // If ec is true, then an error occurred and the vector of pipelines is
  // invalid.
  std::vector<VkPipeline>
  create_pipelines(gsl::span<VkGraphicsPipelineCreateInfo> cinfos,
                   std::error_code& ec) noexcept;

  void destroy(gsl::span<VkPipeline> pipes) noexcept;

  // Create a new fence. If ec is true, then an error occurred and the fence
  // is invalid.
  VkFence create_fence(bool signaled, std::error_code& ec) noexcept;

  // Wait for a set of fences to be signaled. If wait_all is true, then all
  // fences must be signaled before the call will return, if wait_all is
  // false, then a single fence will cause the call to return. timeout
  // specifies how long to wait, 0 returns immediately, and UINT64_MAX will
  // wait indefinitely. If ec is true, and error occurred while waiting.
  void wait(gsl::span<VkFence> fences, bool wait_all, uint64_t timeout,
            std::error_code& ec) noexcept;

  // Reset a set of fences from the signaled state to unsignaled. If ec is
  // true then an error occured.
  void reset(gsl::span<VkFence> fences, std::error_code& ec) noexcept;

  void destroy(VkFence fence) noexcept;

  constexpr renderer() noexcept {};
  renderer(renderer const&) = delete;
  renderer(renderer&& other) noexcept;
  renderer& operator=(renderer const&) = delete;
  renderer& operator=(renderer&& rhs) noexcept;
  ~renderer() noexcept;

private:
  VkInstance _instance{VK_NULL_HANDLE};
  VkDebugReportCallbackEXT _callback{VK_NULL_HANDLE};

  VkPhysicalDevice _physical{VK_NULL_HANDLE};
  VkDevice _device{VK_NULL_HANDLE};

  uint32_t _graphics_queue_family_index{UINT32_MAX};
  VkQueue _graphics_queue{VK_NULL_HANDLE};
  VkCommandPool _graphics_command_pool{VK_NULL_HANDLE};
  VkFence _graphics_onetime_fence{VK_NULL_HANDLE};
}; // class renderer

inline constexpr auto operator|(renderer_options a,
                                renderer_options b) noexcept {
  using U = std::underlying_type_t<renderer_options>;
  return static_cast<renderer_options>(static_cast<U>(a) | static_cast<U>(b));
}

inline constexpr auto operator&(renderer_options a,
                                renderer_options b) noexcept {
  using U = std::underlying_type_t<renderer_options>;
  return static_cast<renderer_options>(static_cast<U>(a) & static_cast<U>(b));
}

#endif // VKST_RENDERER_H


#pragma once
#include <memory>
#include "halley/resources/resource.h"
#include "halley/utils/utils.h"
#include <gsl/gsl>
#include "halley/core/resources/resource_collection.h"

namespace Halley
{
	class ResourceLoader;

	class BinaryFile : public Resource
	{
	public:
		BinaryFile();
		explicit BinaryFile(const Bytes& data);
        explicit BinaryFile(Bytes&& data);
		explicit BinaryFile(gsl::span<const gsl::byte> data);
		explicit BinaryFile(std::unique_ptr<ResourceDataStream> stream);

		static std::unique_ptr<BinaryFile> loadResource(ResourceLoader& loader);
		constexpr static AssetType getAssetType() { return AssetType::BinaryFile; }
		void reload(Resource&& resource) override;

		const Bytes& getBytes() const;
		Bytes& getBytes();
		gsl::span<const gsl::byte> getSpan() const;
		gsl::span<gsl::byte> getSpan();

		std::shared_ptr<ResourceDataStream> getStream() const;

	private:
		Bytes data;
		std::shared_ptr<ResourceDataStream> stream;
		bool streaming = false;
	};
}


#ifndef TTT_GFX_BUTTON_H
#define TTT_GFX_BUTTON_H

#include <SDL_render.h>
#include <SDL_mouse.h>
#include <SDL_mixer.h>

#include <gsl/pointers>

#include <optional>

namespace ttt::gfx
{
	// A basic clickable image button.
	class Button final
	{
	public:
		Button() = default;
		Button(SDL_Texture *texture, const SDL_Rect &normalSrc, const SDL_FRect &dst, Mix_Chunk *sound) noexcept;
		Button(SDL_Texture *texture, const SDL_Rect &normalSrc, const std::optional<SDL_Rect> &hoverSrc, const std::optional<SDL_Rect> &pressedSrc, const SDL_FRect &dst, Mix_Chunk *sound) noexcept;

		void setTexture(SDL_Texture *texture, const SDL_Rect &normalSrc) noexcept;
		void setTexture(SDL_Texture *texture, const SDL_Rect &normalSrc, const std::optional<SDL_Rect> &hoverSrc, const std::optional<SDL_Rect> &pressedSrc) noexcept;
		void setDestination(const SDL_FRect &dst) noexcept;
		void setSound(Mix_Chunk *sound) noexcept;

		void update() noexcept;
		void render(gsl::not_null<SDL_Renderer *> renderer) const noexcept;

		[[nodiscard]] bool isHoveredOver() const noexcept;
		[[nodiscard]] bool isPressed() const noexcept;
		[[nodiscard]] bool wasReleased() const noexcept;

	private:
		SDL_Texture *texture{nullptr};

		SDL_Rect normalSrc{0, 0, 0, 0};
		std::optional<SDL_Rect> hoverSrc;
		std::optional<SDL_Rect> pressedSrc;

		SDL_FRect dst{0.0f, 0.0f, 0.0f, 0.0f};
		Mix_Chunk *sound{nullptr};

		bool hoveredOver{false};
		bool pressed{false};
		bool released{false};
		std::optional<SDL_Point> click;
	};

	inline void Button::setSound(Mix_Chunk *sound) noexcept { this->sound = sound; }

	inline bool Button::isHoveredOver() const noexcept { return hoveredOver; }
	inline bool Button::isPressed() const noexcept { return pressed; }
	inline bool Button::wasReleased() const noexcept { return released; }
}

#endif


#pragma once

#include <string>
#include <vector>
#include <set>
#include <map>

#include <gsl/gsl>

#include <gamesystems/map/sector.h>

#include "streams.h"
#include "animgoals/anim.h"

struct QuestSave {
	GameTime acceptedTime;
	uint32_t acceptedArea;
	uint32_t state;
};

struct ObjRefSave {
	ObjectId id;
	uint32_t mapId;
	locXY location;
};

struct AnimGoalSave {
	AnimGoalType type;
	ObjRefSave selfObj;
	ObjRefSave targetObj;
	ObjRefSave blockObj;
	ObjRefSave scratchObj;
	ObjRefSave parentObj;
	locXY targetLoc;
	locXY range;
	int animId;
	int animIdPrev;
	int animData;
	int animData2;
	int spellData;
	int skillData;
	int flagsData;
	int scratchVal1;
	int scratchVal2;
	int scratchVal3;
	int scratchVal4;
	int scratchVal5;
	int scratchVal6;
	int soundHandle;
};

struct AnimSlotSave {
	AnimSlotId id;
	uint32_t flags;
	int currentState;
	uint32_t field14;
	uint64_t field18;
	ObjRefSave animObj;
	std::vector<AnimGoalSave> goals;
	std::array<uint8_t, 0xF8> unk;
	uint64_t pathInfo;
	GameTime someTime;
	uint32_t field2c90;
};

struct SpellObjSave {
	ObjectId id;
	uint32_t casterPartSysName;
};

struct SpellSave {
	uint32_t spellId;
	uint32_t spellIdx;
	uint32_t active;

	uint32_t spellEnum;
	uint32_t spellEnumOrg;
	uint32_t flagsSth;
	ObjectId caster;
	uint32_t casterPartSysName; // 0 if none
	uint32_t spellClass;
	uint32_t spellLevel;
	uint32_t baseCasterLevel;
	uint32_t spellDc;
	uint32_t spellObjCount; // Guess
	objHndl unknownObj;
	std::array<SpellObjSave, 128> spellObjs; // Not really sure if this is spell objs
	size_t targetListNumItemsCopy;
	size_t targetListNumItems;
	std::array<SpellObjSave, 32> targets;
	uint32_t numProjectiles;
	std::array<ObjectId, 5> projectiles;
	LocFull aoeCenter;
	uint32_t duration;
	uint32_t durationRemaining;
	uint32_t spellRange;
	uint32_t savingThrowResult;
	uint32_t metaMagic;
	uint32_t spellId2;
};

struct TimeEventArgSave {
	int32_t intVal = 0;
	float floatVal = 0;
	ObjRefSave objectRefVal;
	locXY locVal = {0, 0};
	std::string pythonObj;
};

struct TimeEventSave {
	GameTime expiresAt;
	TimeEventType type;
	std::vector<TimeEventArgSave> args;
};

class SaveGameArchive {
public:
	static void Create(const std::string &folder,
		const std::string &filename);

	static void Unpack(const std::string &filename,
		const std::string &folder);

private:
	static void AddFolder(const std::string &folder,
		OutputStream &indexStream,
		OutputStream &dataStream);

	static void UnpackFolder(const std::string &folder,
		InputStream &indexStream,
		InputStream &dataStream);

	static void UnpackFile(const std::string &folder,
		InputStream &indexStream,
		InputStream &dataStream);
};

class SaveGame {
public:

	void Load(const std::string &path);

private:

	void LoadDataSav(InputStream &in);

	std::vector<std::string> mCustomDescriptions;
	bool mIronman = false;
	uint32_t mIronmanSaveNumber = 0;
	std::string mIronmanSaveName;
	std::vector<SectorTime> mSectorTimes;
	std::vector<uint8_t> mGlobalVars;
	std::vector<uint8_t> mGlobalFlags;
	uint32_t mStoryState = 0;
	std::vector<int32_t> mEncounterQueue;

	std::string mMapDataPath;
	std::string mMapSavePath;
	std::set<uint32_t> mVisitedMaps;

	uint32_t mNextSpellId = 0;
	std::vector<SpellSave> mSpells;

	uint32_t mLightSchemeId = 0;
	uint32_t mLightSchemeHourOfDay = 0;
	std::set<uint32_t> mKnownAreas;
	std::vector<uint32_t> mSoundSchemeIds; // At most 2
	bool mSoundGameOverSound = false;
	std::vector<uint32_t> mSoundGameOverStash; // 2 scheme ids
	bool mSoundGameInCombat = false;
	std::vector<uint32_t> mSoundGameCombatStash; // 2 scheme ids
	std::vector<QuestSave> mQuests;

	bool mInCombat = false;

	GameTime mRealTime;
	GameTime mGameTime;
	GameTime mAnimTime;
	std::vector<TimeEventSave> mTimeEvents;

	uint32_t mNextAnimId = 0;
	uint32_t mActiveGoalCount = 0;
	bool mAnimCatchup = false;
	uint32_t mAnimActionId = 0;
	std::vector<uint32_t> mUnkAnimVals;
	std::map<size_t, AnimSlotSave> mAnimSlots;

	static AnimSlotSave ReadAnimSlot(InputStream &in);
	static ObjRefSave ReadObjRef(InputStream &in);

	static void ReadSystemFooter(InputStream &in, const char *system);
};


/* The VCL screen capture library is released under the MIT license.
 *
 * Copyright(c) 2018 Basil Fierz
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files(the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions :
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
#pragma once

// Abseil
#include <absl/strings/string_view.h>

// C++ standard library
#include <array>
#include <utility>

// GSL
#include <gsl/gsl>

#ifdef VCL_GRAPHICS_RECORDER_EXPORTS
#	define VCL_GRAPHICS_RECORDER_API __declspec(dllexport)
#else
#	define VCL_GRAPHICS_RECORDER_API __declspec(dllimport)
#endif

extern "C"
{
	struct AVCodec;
	struct AVCodecContext;
	struct AVCodecParameters;
	struct AVFormatContext;
	struct AVFrame;
	struct AVStream;
}

namespace Vcl { namespace Graphics { namespace Recorder
{
	enum class OutputFormat
	{
		Avi,
		Mkv,
		Mp4
	};

	enum class CodecType
	{
		H264
	};

	class VCL_GRAPHICS_RECORDER_API Recorder
	{
	public:
		Recorder(OutputFormat out_fmt, CodecType codec);
		~Recorder();

	public:
		void open(absl::string_view sink_name, unsigned int width, unsigned int height, unsigned int frame_rate);
		void close();

		bool write(gsl::span<const uint8_t> Y, gsl::span<const uint8_t> U, gsl::span<const uint8_t> V);
		bool write(gsl::span<const uint8_t> Y, gsl::span<const std::array<uint8_t, 2>> UV);
		bool write(gsl::span<const std::array<uint8_t, 3>> rgb, unsigned int w, unsigned int h);

	private:
		//! Create the output format
		//! \param fmt Format to create
		//! \param ctx Context to assign the output format to
		void createOutputFormat(OutputFormat fmt, gsl::not_null<AVFormatContext*> ctx) const;

		//! Prepare codec
		//! \param codec Codec to create
		std::pair<AVCodec*, AVCodecContext*> createCodec(CodecType codec_cfg) const;

		//! Configure specific H264 parameters
		void configureH264();

		//! Write a single frame to the output
		//! \param frame Frame to write out. Use 'nullptr' to flush the codec.
		//! \note Notes about internal API used:
		//! * https://blogs.gentoo.org/lu_zero/2016/03/29/new-avcodec-api/
		//! * https://www.ffmpeg.org/doxygen/3.4/group__lavc__encdec.html
		bool write(AVFrame* frame);

		//! Hold the formating of the IO container
		AVFormatContext* _fmtCtx{nullptr};

		//! Recording stream
		AVStream* _videoStream{nullptr};

		//! Actual codec
		AVCodec* _codec{nullptr};

		//! Codec context
		AVCodecContext* _codecCtx{nullptr};

		//! Is the output open
		bool _isOpen{false};

		//! Temporary frames for data processing
		AVFrame* _processing_frame{nullptr};

		//! Current frame count
		int64_t _frames{0};
	};
}}}


#ifndef XYCO_RUNTIME_UTILS_SELECT_H
#define XYCO_RUNTIME_UTILS_SELECT_H

#include <gsl/pointers>
#include <utility>

#include "runtime/runtime.h"
#include "type_wrapper.h"

namespace xyco::runtime {
template <typename T1, typename T2>
class SelectFuture
    : public Future<std::variant<TypeWrapper<T1>, TypeWrapper<T2>>> {
  using CoOutput = std::variant<TypeWrapper<T1>, TypeWrapper<T2>>;

 public:
  auto poll(Handle<void> self) -> Poll<CoOutput> override {
    if (!ready_) {
      ready_ = true;
      auto [wrapper1, wrapper2] = std::pair<Future<void>, Future<void>>(
          future_wrapper<T1, 0>(std::move(futures_.first)),
          future_wrapper<T2, 1>(std::move(futures_.second)));
      wrappers_ = {wrapper1.get_handle(), wrapper2.get_handle()};
      RuntimeCtx::get_ctx()->spawn(std::move(wrapper1));
      RuntimeCtx::get_ctx()->spawn(std::move(wrapper2));
      return Pending();
    }

    if (result_.index() == 0) {
      RuntimeCtx::get_ctx()->cancel_future(wrappers_.second);
      return Ready<CoOutput>{
          CoOutput(std::in_place_index<0>, std::get<0>(result_))};
    }

    RuntimeCtx::get_ctx()->cancel_future(wrappers_.first);
    return Ready<CoOutput>{
        CoOutput(std::in_place_index<1>, std::get<1>(result_))};
  }

  SelectFuture(Future<T1> &&future1, Future<T2> &&future2)
      : Future<CoOutput>(nullptr),
        ready_(false),
        result_(std::in_place_index<2>, true),
        futures_(std::move(future1), std::move(future2)) {}

 private:
  template <typename T, int Index>
  auto future_wrapper(Future<T> future) -> Future<void>
  requires(!std::is_same_v<T, void>) {
    auto result = co_await future;
    if (result_.index() == 2) {
      result_ = std::variant<TypeWrapper<T1>, TypeWrapper<T2>, bool>(
          std::in_place_index<Index>, TypeWrapper<T>{result});
      RuntimeCtx::get_ctx()->register_future(this);
    }
  }

  template <typename T, int Index>
  auto future_wrapper(Future<T> future) -> Future<void>
  requires(std::is_same_v<T, void>) {
    co_await future;
    if (result_.index() == 2) {
      result_ = std::variant<TypeWrapper<T1>, TypeWrapper<T2>, bool>(
          std::in_place_index<Index>, TypeWrapper<T>());
      RuntimeCtx::get_ctx()->register_future(this);
    }
  }

  bool ready_;
  std::variant<TypeWrapper<T1>, TypeWrapper<T2>, bool> result_;
  std::pair<Future<T1>, Future<T2>> futures_;
  std::pair<Handle<PromiseBase>, Handle<PromiseBase>> wrappers_;
};

template <typename T1, typename T2>
auto select(Future<T1> future1, Future<T2> future2)
    -> Future<std::variant<TypeWrapper<T1>, TypeWrapper<T2>>> {
  co_return co_await SelectFuture<T1, T2>(std::move(future1),
                                          std::move(future2));
}
}  // namespace xyco::runtime

#endif  // XYCO_RUNTIME_UTILS_SELECT_H


#ifndef __GSL_VERSION_H__
#define __GSL_VERSION_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <gsl/gsl_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif
__BEGIN_DECLS

#define GSL_VERSION "2.4"
#define GSL_MAJOR_VERSION 2
#define GSL_MINOR_VERSION 4

GSL_VAR const char * gsl_version;

__END_DECLS

#endif /* __GSL_VERSION_H__ */


#pragma once

#include "../configuration.h"
#include <chrono>
#include <gsl/gsl-lite.hpp>
#include <string_view>
#include <utility>
#include <yaml-cpp/yaml.h>

namespace angonoka {
/**
    Load Configuration from a YAML string.

    @param text Null-terminated string

    @return An instance of Configuration
*/
Configuration load_text(gsl::czstring text);

/**
    Load Configuration from a YAML file.

    @param path YAML configuration location

    @return An instance of Configuration
*/
Configuration load_file(std::string_view path);
} // namespace angonoka

namespace angonoka::detail {
/**
    Finds or inserts a group into Configuration.groups.

    @param groups   An array of Groups
    @param group    Group name

    @return The index of the group in Configuration.groups
    and whether the insertion took place.
*/
std::pair<GroupIndex, bool>
find_or_insert_group(Groups& groups, std::string_view group);

/**
    Parses human-readable durations.

    Parses durations with resolution ranging from seconds
    up to and including months. Examples:

    1h 15min
    3 weeks and 5 months
    30 seconds

    @param text A string containing a duration.
    @return The duration in seconds.
*/
std::chrono::seconds parse_duration(std::string_view text);

/**
    Parses agents blocks.

    Parses blocks such as these:

    agents:
      agent 1:
      agent 2:

    @param node     "agents" node
    @param config   An instance of Configuration
*/
void parse_agents(const YAML::Node& node, Configuration& config);

/**
    Parses tasks blocks.

    Parses blocks such as these:

    tasks:
      - name: task 1
        duration:
          min: 1 h
          max: 3 h
      - name: task 2
        duration: 2h

    @param node     "tasks" node
    @param config   An instance of Configuration
*/
void parse_tasks(const YAML::Node& node, Configuration& config);
} // namespace angonoka::detail


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef thdpool_3a298e4f_792c_44ff_bc62_c639d1eb697c_h
#define thdpool_3a298e4f_792c_44ff_bc62_c639d1eb697c_h

#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <gslib/std.h>

__gslib_begin__

using std::thread;
using std::function;

class thread_pool
{
public:
    thread_pool(int threads)
    {
        for(int i = 0; i < threads; i ++) {
            _workers.emplace_back(
                [this]() {
                    for(;;) {
                        function<void()> task;
                        {
                            std::unique_lock<std::mutex> lock(_mtx_worker);
                            _cv_worker.wait(lock, [this] { return _stop || !_tasks.empty(); });
                            if(_stop && _tasks.empty())
                                return;
                            task = std::move(_tasks.front());
                            _tasks.pop();
                        }
                        task();
                        {
                            std::unique_lock<std::mutex> lock(_mtx_main);
                            -- _undone;
                        }
                        _cv_main.notify_one();
                    }
                }
            );
        }
    }
    ~thread_pool()
    {
        {
            std::unique_lock<std::mutex> lock(_mtx_worker);
            _stop = true;
        }
        _cv_worker.notify_all();
        for(thread& worker : _workers)
            worker.join();
    }

protected:
    vector<thread>          _workers;
    queue<function<void()>> _tasks;
    std::mutex              _mtx_worker;
    std::mutex              _mtx_main;
    std::condition_variable _cv_worker;
    std::condition_variable _cv_main;
    volatile bool           _stop = false;
    volatile int            _undone = 0;

public:
    template<class _func, class... _args>
    auto add(_func&& f, _args&&... args) ->
        std::future<typename std::result_of<_func(_args...)>::type>
    {
        using return_type = typename std::result_of<_func(_args...)>::type;
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<_func>(f), std::forward<_args>(args)...)
            );
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(_mtx_worker);
            if(_stop)
                throw std::runtime_error("enqueue on stopped thread pool.");
            _tasks.emplace([task]() { (*task)(); });
        }
        {
            std::unique_lock<std::mutex> lock(_mtx_main);
            ++ _undone;
        }
        _cv_worker.notify_one();
        return res;
    }
    void join()
    {
        std::unique_lock<std::mutex> lock(_mtx_main);
        _cv_main.wait(lock, [this] { return !_undone; });
    }
};

__gslib_end__

#endif


/****************************************************************************
** Copyright (c) 2021, Fougue Ltd. <http://www.fougue.pro>
** All rights reserved.
** See license at https://github.com/fougue/mayo/blob/master/LICENSE.txt
****************************************************************************/

#pragma once

#include <gsl/span>

namespace Mayo {

template<typename T> using Span = gsl::span<T>;

} // namespace Mayo


#pragma once

// Windows
#if defined(_WIN32)
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <windows.h>
#endif

// Standard
#include <cstdlib>
#include <exception>
#include <stdexcept>
#include <cassert>
#include <string>
#include <vector>
#include <map>
#include <memory>
#include <cstdint>
#include <sstream>
#include <fstream>
#include <chrono>
#include <functional>
#include <algorithm>
#include <iterator>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

// Guidline Support Library
#include <gsl/gsl>

// OpenGL
#include <GL/gl3w.h>
#define GLFW_INCLUDE_GLCOREARB
#include <GLFW/glfw3.h>

#if defined(_LINUX)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif

#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#if defined(_LINUX)
#pragma GCC diagnostic pop
#endif

//#include <SOIL/SOIL.h>

// Assimp
#if defined(_LINUX)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#if defined(_LINUX)
#pragma GCC diagnostic pop
#endif

// Local
#include "ServiceContainer.h"

namespace Library
{
	extern ServiceContainer GlobalServices;
}


#pragma once

#include <atomic>
#include <array>
#include <cstdint>
#include <future>
#include <functional>
#include <optional>
#include <bitset>

#ifndef SPDLOG_FMT_EXTERNAL
#define SPDLOG_FMT_EXTERNAL 1
#endif
#include <spdlog/spdlog.h>

#include <gsl/gsl-lite.hpp>

// TODO: leaf pulls in Windows.h via common.hpp, so we bypass that here to avoid the mess
// if something did pull in windows.h, this is fine. custom_formatmessage is just a wrapper.
#ifndef _WINDOWS_

#ifndef LPVOID
#define LPVOID void*
#define LPVOID_redefined
#endif
#ifndef LPCSTR
#define LPCSTR char*
#define LPCSTR_redefined
#endif

namespace mu
{
	auto custom_formatmessage(
		unsigned long dwFlags,
		const void*	  lpSource,
		unsigned long dwMessageId,
		unsigned long dwLanguageId,
		char*		  lpBuffer,
		unsigned long nSize,
		va_list*	  Arguments) noexcept -> unsigned long;
}

#define FormatMessageA(DWORD_dwFlags, LPCVOID_lpSource, DWORD_dwMessageId, DWORD_dwLanguageId, LPSTR_lpBuffer, DWORD_nSize, va_list_Arguments)                                     \
	mu::custom_formatmessage(DWORD_dwFlags, LPCVOID_lpSource, DWORD_dwMessageId, DWORD_dwLanguageId, LPSTR_lpBuffer, DWORD_nSize, va_list_Arguments)
#define _WINDOWS_
#define _WINDOWS_redefined			   true
#define FORMAT_MESSAGE_ALLOCATE_BUFFER 0x00000100
#define FORMAT_MESSAGE_FROM_SYSTEM	   0x00001000
#define FORMAT_MESSAGE_IGNORE_INSERTS  0x00000200
#define MAKELANGID(p, s)			   ((((unsigned short)(s)) << 10) | (unsigned short)(p))
#define LANG_NEUTRAL				   0x00
#define SUBLANG_DEFAULT				   0x01
#define LPSTR						   char*
#else // #ifndef _WINDOWS_
#define _WINDOWS_redefined false
#endif // #else // #ifndef _WINDOWS_
#include <boost/leaf.hpp>
#if _WINDOWS_redefined
#ifdef LPVOID_redefined
#undef LPVOID
#undef LPVOID_redefined
#endif
#ifdef LPCSTR_redefined
#undef LPCSTR_redefined
#undef LPCSTR
#endif
#undef FormatMessageA
#undef _WINDOWS_
#undef _WINDOWS_redefined
#undef FORMAT_MESSAGE_ALLOCATE_BUFFER
#undef FORMAT_MESSAGE_FROM_SYSTEM
#undef FORMAT_MESSAGE_IGNORE_INSERTS
#undef MAKELANGID
#undef LANG_NEUTRAL
#undef SUBLANG_DEFAULT
#undef LPSTR
#endif // #if _WINDOWS_redefined

namespace mu
{
	template<typename E>
	constexpr auto underlying_cast(E e) noexcept -> typename std::underlying_type<E>::type
	{
		return static_cast<typename std::underlying_type<E>::type>(e);
	}
} // namespace mu

namespace mu
{
	namespace leaf = boost::leaf;

	struct common_error
	{
	};

	struct runtime_error
	{
		struct not_specified : common_error
		{
		};
	};
} // namespace mu

#define MU_LEAF_IF_REPORT(r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value, "MU_LEAF_CHECK requires a result object (see is_result_type)");       \
	if (!BOOST_LEAF_TMP)                                                                                \
		/*TODO: REPORT*/;                                                                                \
	else

#define MU_LEAF_RETHROW(r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(::boost::leaf::is_result_type<typename std::decay<decltype(BOOST_LEAF_TMP)>::type>::value, "MU_LEAF_CHECK requires a result object (see is_result_type)");       \
	if (BOOST_LEAF_TMP)                                                                                \
		;                                                                                \
	else                                                                                \
	{                                                                                \
		throw BOOST_LEAF_TMP.error();                                                                                \
	}

#define MU_LEAF_ASSIGN(v, r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(                                                                                \
		::boost::leaf::is_result_type<std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,                                                                                \
		"MU_LEAF_ASSIGN and MU_LEAF_AUTO require a result object as the second argument (see is_result_type)");                                                                    \
	if (!BOOST_LEAF_TMP)                                                                                \
	{                                                                                \
		return BOOST_LEAF_TMP.error();                                                                                \
	}                                                                                \
	v = std::forward<decltype(BOOST_LEAF_TMP)>(BOOST_LEAF_TMP).value()

#define MU_LEAF_PUSH_BACK(v, r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(                                                                                \
		::boost::leaf::is_result_type<std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,                                                                                \
		"MU_LEAF_ASSIGN and MU_LEAF_AUTO require a result object as the second argument (see is_result_type)");                                                                    \
	if (!BOOST_LEAF_TMP)                                                                                \
	{                                                                                \
		return BOOST_LEAF_TMP.error();                                                                                \
	}                                                                                \
	v.push_back(std::forward<decltype(BOOST_LEAF_TMP)>(BOOST_LEAF_TMP).value())

#define MU_LEAF_EMPLACE_BACK(v, r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(                                                                                \
		::boost::leaf::is_result_type<std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,                                                                                \
		"MU_LEAF_ASSIGN and MU_LEAF_AUTO require a result object as the second argument (see is_result_type)");                                                                    \
	if (!BOOST_LEAF_TMP)                                                                                \
	{                                                                                \
		return BOOST_LEAF_TMP.error();                                                                                \
	}                                                                                \
	v.emplace_back(std::forward<decltype(BOOST_LEAF_TMP)>(BOOST_LEAF_TMP).value())

#define MU_LEAF_AUTO(v, r) MU_LEAF_ASSIGN(auto v, r)

#define MU_LEAF_ASSIGN_THROW(v, r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(                                                                                \
		::boost::leaf::is_result_type<std::decay<decltype(BOOST_LEAF_TMP)>::type>::value,                                                                                \
		"MU_LEAF_ASSIGN and MU_LEAF_AUTO require a result object as the second argument (see is_result_type)");                                                                    \
	if (!BOOST_LEAF_TMP)                                                                                \
	{                                                                                \
		throw BOOST_LEAF_TMP.error();                                                                                \
	}                                                                                \
	v = std::forward<decltype(BOOST_LEAF_TMP)>(BOOST_LEAF_TMP).value()

#define MU_LEAF_AUTO_THROW(v, r) MU_LEAF_ASSIGN_THROW(auto v, r)

#define MU_LEAF_CHECK(r)                                                                                \
	auto&& BOOST_LEAF_TMP = r;                                                                                \
	static_assert(::boost::leaf::is_result_type<std::decay<decltype(BOOST_LEAF_TMP)>::type>::value, "MU_LEAF_CHECK requires a result object (see is_result_type)");                \
	if (BOOST_LEAF_TMP)                                                                                \
		;                                                                                \
	else                                                                                \
	{                                                                                \
		return BOOST_LEAF_TMP.error();                                                                                \
	}

#define MU_LEAF_NEW_ERROR		::boost::leaf::leaf_detail::inject_loc{__FILE__, __LINE__, __FUNCTION__} + ::boost::leaf::new_error
#define MU_LEAF_EXCEPTION		::boost::leaf::leaf_detail::inject_loc{__FILE__, __LINE__, __FUNCTION__} + ::boost::leaf::exception
#define MU_LEAF_THROW_EXCEPTION ::boost::leaf::leaf_detail::throw_with_loc{__FILE__, __LINE__, __FUNCTION__} + ::boost::leaf::exception
#define MU_LEAF_LOG_ERROR(...)	((void)0)

namespace mu
{
	template<typename T>
	using optional_future = std::future<std::optional<T>>;

	template<typename T>
	inline auto future_is_ready(T const& f) noexcept -> bool
	{
		return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
	}
} // namespace mu

namespace mu
{
	namespace details
	{
		template<typename T>
		class static_root_singleton
		{
		public:
			auto operator->() noexcept -> T*
			{
				return s_instance;
			}

			auto operator->() const noexcept -> const T*
			{
				return s_instance;
			}

			auto operator*() noexcept -> T&
			{
				return *s_instance;
			}

			auto operator*() const noexcept -> const T&
			{
				return *s_instance;
			}

			static_root_singleton() noexcept
			{
				static bool static_init = []() -> bool
				{
					s_instance = new (&s_instance_memory[0]) T();
					std::atexit(destroy);
					return true;
				}();
			}

		protected:
			static void destroy() noexcept
			{
				if (s_instance)
				{
					s_instance->~T();
					s_instance = nullptr;
				}
			}

		private:
			static inline uint64_t s_instance_memory[1 + (sizeof(T) / sizeof(uint64_t))];
			static inline T*	   s_instance = nullptr;
		};

		template<size_t POOL_COUNT>
		class singleton_cleanup_list
		{
		public:
			using cleanup_func = std::function<void()>;

			void push(cleanup_func f) noexcept
			{
				s_cleanup_funcs[s_cleanup_index.fetch_add(1)] = f;
			}

			~singleton_cleanup_list() noexcept
			{
				for (auto i = s_cleanup_index.load(); i > 0; --i)
				{
					s_cleanup_funcs[i - 1]();
				}
				s_cleanup_index.store(0);
			}

		private:
			std::array<cleanup_func, POOL_COUNT> s_cleanup_funcs;
			std::atomic_size_t					 s_cleanup_index = 0;
		};

		using singleton_cleanup_root = details::static_root_singleton<details::singleton_cleanup_list<1024>>;

		template<typename... T_DEPS>
		struct singleton_dependencies
		{
		private:
			template<typename T_ARG, typename... T_ARGS>
			static inline void update_dependencies_impl() noexcept
			{
				const typename T_ARG::type* t = T_ARG().get();
				if constexpr (sizeof...(T_ARGS) > 0)
				{
					update_dependencies_impl<T_ARGS...>();
				}
			}

		public:
			static inline void update() noexcept
			{
				if constexpr (sizeof...(T_DEPS) > 0)
				{
					update_dependencies_impl<T_DEPS...>();
				}
			}
		};

		template<typename T, typename... T_DEPS>
		struct singleton_factory
		{
			static inline auto create() noexcept -> T*
			{
				if constexpr (sizeof...(T_DEPS) > 0)
				{
					singleton_dependencies<T_DEPS...>::update();
				}
				return new T();
			}
		};

		template<typename T>
		struct virtual_singleton_factory
		{
			static auto create() noexcept -> T*;
		};
	} // namespace details

#define MU_DEFINE_VIRTUAL_SINGLETON(T, T_DERIVED)                                                                                \
	namespace mu                                                                                \
	{                                                                                \
		namespace details                                                                                \
		{                                                                                \
			template<>                                                                                \
			auto virtual_singleton_factory<T>::create() noexcept -> T*                                                                                \
			{                                                                                \
				return new T_DERIVED;                                                                                \
			}                                                                                \
		}                                                                                \
	}                                                                                \
	/**/

#define MU_DEFINE_VIRTUAL_SINGLETON_DEPS(T, T_DERIVED, ...)                                                                                \
	namespace mu                                                                                \
	{                                                                                \
		namespace details                                                                                \
		{                                                                                \
			template<>                                                                                \
			auto virtual_singleton_factory<T>::create() noexcept -> T*                                                                                \
			{                                                                                \
				singleton_dependencies<__VA_ARGS__>::update();                                                                                \
				return new T_DERIVED;                                                                                \
			}                                                                                \
		}                                                                                \
	}                                                                                \
	/**/

	namespace details
	{
		template<typename T, typename T_FACTORY>
		class singleton_base
		{
		public:
			using factory = T_FACTORY;
			using type	  = T;

			auto operator->() noexcept -> T*
			{
				return s_instance;
			}

			auto operator->() const noexcept -> const T*
			{
				return s_instance;
			}

			auto operator*() noexcept -> T&
			{
				return *s_instance;
			}

			auto operator*() const noexcept -> const T&
			{
				return *s_instance;
			}

			auto get() noexcept -> T*
			{
				return s_instance;
			}

			auto get() const noexcept -> const T*
			{
				return s_instance;
			}

			singleton_base() noexcept
			{
				static bool static_init = []() -> bool
				{
					s_instance = factory::create();
					singleton_cleanup_root()->push(
						[]() -> void
						{
							delete s_instance;
							s_instance = nullptr;
						});
					return true;
				}();
			}

		private:
			static inline T* s_instance;
		};
	} // namespace details

	template<typename T, typename... T_DEPENDENCIES>
	using singleton = details::singleton_base<T, details::singleton_factory<T, T_DEPENDENCIES...>>;

	template<typename T, typename... T_DEPENDENCIES>
	using virtual_singleton = details::singleton_base<T, details::virtual_singleton_factory<T>>;

	template<typename T_SINGLETON>
	class exported_singleton
	{
	public:
		using singleton_type = T_SINGLETON;
		using type			 = typename singleton_type::type;

		auto operator->() noexcept -> type*
		{
			return s_instance;
		}

		auto operator->() const noexcept -> const type*
		{
			return s_instance;
		}

		auto operator*() noexcept -> type&
		{
			return *s_instance;
		}

		auto operator*() const noexcept -> const type&
		{
			return *s_instance;
		}

		auto get() noexcept -> type*
		{
			return s_instance;
		}

		auto get() const noexcept -> const type*
		{
			return s_instance;
		}

		exported_singleton() noexcept
		{
			static bool static_init = []() -> bool
			{
				s_instance = get_instance();
				return true;
			}();
		}

	private:
		static inline type* s_instance;
		static type*		get_instance() noexcept;
	};

#define MU_EXPORT_SINGLETON(T)                                                                                \
	template<>                                                                                \
	auto T::get_instance() noexcept->T::type*                                                                                \
	{                                                                                \
		return singleton_type().get();                                                                                \
	}                                                                                \
	/**/

#define MU_EXPORT_SINGLETON_DEPS(T, ...)                                                                                \
	template<>                                                                                \
	auto T::get_instance() noexcept->T::type*                                                                                \
	{                                                                                \
		::mu::details::singleton_dependencies<__VA_ARGS__>::update();                                                                                \
		return singleton_type().get();                                                                                \
	}                                                                                \
	/**/

	namespace details
	{
		template<typename T>
		class static_root_thread_local_singleton
		{
		public:
			auto operator->() noexcept -> T*
			{
				return s_instance;
			}

			auto operator->() const noexcept -> const T*
			{
				return s_instance;
			}

			auto operator*() noexcept -> T&
			{
				return *s_instance;
			}

			auto operator*() const noexcept -> const T&
			{
				return *s_instance;
			}

			static_root_thread_local_singleton() noexcept
			{
				static bool static_init = []() -> bool
				{
					s_instance = new (&s_instance_memory[0]) T();
					std::atexit(destroy);
					return true;
				}();
			}

		protected:
			static void destroy() noexcept
			{
				if (s_instance)
				{
					s_instance->~T();
					s_instance = nullptr;
				}
			}

		private:
			static inline thread_local uint64_t s_instance_memory[1 + (sizeof(T) / sizeof(uint64_t))];
			static inline thread_local T*		s_instance = nullptr;
		};

		using thread_local_singleton_cleanup_root = static_root_thread_local_singleton<singleton_cleanup_list<1024>>;

		template<typename T, typename T_FACTORY>
		class thread_local_singleton_base
		{
		public:
			using type = T;
			auto operator->() noexcept -> T*
			{
				return s_instance;
			}

			auto operator->() const noexcept -> const T*
			{
				return s_instance;
			}

			auto operator*() noexcept -> T&
			{
				return *s_instance;
			}

			auto operator*() const noexcept -> const T&
			{
				return *s_instance;
			}

			auto get() noexcept -> T*
			{
				return s_instance;
			}

			auto get() const noexcept -> const T*
			{
				return s_instance;
			}

			thread_local_singleton_base() noexcept
			{
				static thread_local bool static_init = []() -> bool
				{
					s_instance = new T();
					thread_local_singleton_cleanup_root()->push(
						[]() -> void
						{
							delete s_instance;
							s_instance = nullptr;
						});
					return true;
				}();
			}

			static inline thread_local T* s_instance;
		};
	} // namespace details

	template<typename T, typename... T_DEPENDENCIES>
	using thread_local_singleton = details::thread_local_singleton_base<T, details::singleton_factory<T, T_DEPENDENCIES...>>;

	template<typename T, typename... T_DEPENDENCIES>
	using thread_local_virtual_singleton = details::thread_local_singleton_base<T, details::virtual_singleton_factory<T>>;

	template<typename T_SINGLETON>
	class exported_thread_local_singleton
	{
	public:
		using singleton_type = T_SINGLETON;
		using type			 = typename singleton_type::type;

		auto operator->() noexcept -> type*
		{
			return s_instance;
		}

		auto operator->() const noexcept -> const type*
		{
			return s_instance;
		}

		auto operator*() noexcept -> type&
		{
			return *s_instance;
		}

		auto operator*() const noexcept -> const type&
		{
			return *s_instance;
		}

		auto get() noexcept -> type*
		{
			return s_instance;
		}

		auto get() const noexcept -> const type*
		{
			return s_instance;
		}

		exported_thread_local_singleton() noexcept
		{
			static thread_local bool static_init = []() -> bool
			{
				s_instance = get_instance();
				return true;
			}();
		}

	private:
		static inline thread_local type* s_instance;
		static type*					 get_instance() noexcept;
	};

#define MU_EXPORT_THREAD_LOCAL_SINGLETON(T)                                                                                \
	template<>                                                                                \
	auto ::mu::exported_thread_local_singleton<T>::get_instance() noexcept->::mu::exported_thread_local_singleton<T>::type*                                                        \
	{                                                                                \
		return singleton_type().get();                                                                                \
	}                                                                                \
	/**/
} // namespace mu

namespace mu
{
	namespace time
	{
		auto performance_frequency() noexcept -> int64_t;
		auto get_now() noexcept -> int64_t;
		void sleep(const int64_t milliseconds) noexcept;
		void micro_sleep(const int64_t tx) noexcept;
		void init() noexcept;
		void calibrate() noexcept;
		void set_high_resolution_timer() noexcept;
		auto release_high_resolution_timer() noexcept -> leaf::result<void>;

		class moment
		{
			friend class run_time;
			friend class long_clock;

		protected:
			int64_t value;

			moment(const int64_t v) noexcept : value(v) { }

			moment(const long double v) noexcept : value(static_cast<int64_t>(v)) { }

			friend auto now() noexcept -> moment;

			template<typename T>
			friend auto seconds(const T&) noexcept -> moment;

			template<typename T>
			friend auto milliseconds(const T&) noexcept -> moment;

			template<typename T>
			friend auto microseconds(const T&) noexcept -> moment;

			template<typename T>
			friend auto nanoseconds(const T&) noexcept -> moment;

		public:
			moment() noexcept : value(0) { }

			inline auto operator=(const moment& rhs) noexcept -> moment&
			{
				value = rhs.value;
				return *this;
			}

			inline auto operator+=(const moment& rhs) noexcept -> moment&
			{
				value += rhs.value;
				return *this;
			}

			inline auto operator-=(const moment& rhs) noexcept -> moment&
			{
				value += rhs.value;
				return *this;
			}

			template<typename T>
			inline auto operator*=(const T& rhs) noexcept -> moment&
			{
				value = static_cast<int64_t>(static_cast<long double>(value) * static_cast<long double>(rhs));
				return *this;
			}

			template<typename T>
			inline auto operator/=(const T& rhs) noexcept -> moment&
			{
				value = static_cast<int64_t>(static_cast<long double>(value) / static_cast<long double>(rhs));
				return *this;
			}

			inline auto add(const moment& rhs) const noexcept -> moment
			{
				return moment{value + rhs.value};
			}

			inline auto sub(const moment& rhs) const noexcept -> moment
			{
				return moment{value - rhs.value};
			}

			template<typename T>
			inline auto div(const T& rhs) const noexcept -> moment
			{
				return moment{static_cast<long double>(value) / static_cast<long double>(rhs)};
			}

			template<typename T>
			inline auto mul(const T& rhs) const noexcept -> moment
			{
				return moment{static_cast<long double>(value) * static_cast<long double>(rhs)};
			}

			template<typename T>
			inline auto as_seconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value) / static_cast<long double>(performance_frequency()));
			}

			template<typename T>
			inline auto as_milliseconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value) / ((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull))));
			}

			template<typename T>
			inline auto as_microseconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value) / ((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull * 1000ull))));
			}

			template<typename T>
			inline auto as_nanoseconds() const noexcept -> T
			{
				return static_cast<T>(
					static_cast<long double>(value) / ((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull * 1000ull * 1000ull))));
			}

			template<typename T>
			inline auto as_ticks() const noexcept -> T
			{
				return static_cast<T>(value);
			}

			template<typename T>
			inline auto set_seconds(const T& seconds) noexcept -> moment&
			{
				value = static_cast<int64_t>((static_cast<long double>(performance_frequency())) * static_cast<long double>(seconds));
				return *this;
			}

			template<typename T>
			inline auto set_milliseconds(const T& seconds) noexcept -> moment&
			{
				value = static_cast<int64_t>((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull)) * static_cast<long double>(seconds));
				return *this;
			}

			template<typename T>
			inline auto set_microseconds(const T& seconds) noexcept -> moment&
			{
				value = static_cast<int64_t>((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull * 1000ull)) * static_cast<long double>(seconds));
				return *this;
			}

			template<typename T>
			inline auto set_nanoseconds(const T& seconds) noexcept -> moment&
			{
				value = static_cast<int64_t>(
					(static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull * 1000ull * 1000ull)) * static_cast<long double>(seconds));
				return *this;
			}

			template<typename T>
			inline auto set_ticks(const T& tx) noexcept -> moment&
			{
				value = static_cast<int64_t>(tx);
				return *this;
			}

			inline auto now() noexcept -> moment&
			{
				value = get_now();
				return *this;
			}
		};

		class long_clock
		{
			uint64_t value;
			moment	 last_moment;

		public:
			long_clock() noexcept : value(0) { }

			template<typename T>
			inline auto as_seconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value) / static_cast<long double>(1000ull));
			}

			template<typename T>
			inline auto as_milliseconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value));
			}

			template<typename T>
			inline auto as_microseconds() const noexcept -> T
			{
				return static_cast<T>(static_cast<long double>(value) * static_cast<long double>(1000ull));
			}

			void update() noexcept;
		};

		inline auto operator<(const moment& lhs, const moment& rhs) noexcept -> bool
		{
			return lhs.as_ticks<int64_t>() < rhs.as_ticks<int64_t>();
		}

		inline auto operator<=(const moment& lhs, const moment& rhs) noexcept -> bool
		{
			return lhs.as_ticks<int64_t>() <= rhs.as_ticks<int64_t>();
		}

		inline auto operator>(const moment& lhs, const moment& rhs) noexcept -> bool
		{
			return lhs.as_ticks<int64_t>() > rhs.as_ticks<int64_t>();
		}

		inline auto operator>=(const moment& lhs, const moment& rhs) noexcept -> bool
		{
			return lhs.as_ticks<int64_t>() >= rhs.as_ticks<int64_t>();
		}

		inline auto operator==(const moment& lhs, const moment& rhs) noexcept -> bool
		{
			return lhs.as_ticks<int64_t>() == rhs.as_ticks<int64_t>();
		}

		inline auto operator+(const moment& lhs, const moment& rhs) noexcept -> moment
		{
			return lhs.add(rhs);
		}

		inline auto operator-(const moment& lhs, const moment& rhs) noexcept -> moment
		{
			return lhs.sub(rhs);
		}

		template<typename T>
		inline auto operator/(const moment& lhs, const T& rhs) noexcept -> moment
		{
			return lhs.div(rhs);
		}

		template<typename T>
		inline auto operator*(const moment& lhs, const T& rhs) noexcept -> moment
		{
			return lhs.mul(rhs);
		}

		inline auto now() noexcept -> moment
		{
			moment t;
			return t.now();
		}

		template<typename T>
		inline auto seconds(const T& val) noexcept -> moment
		{
			moment t;
			return t.set_seconds(val);
		}

		template<typename T>
		inline auto milliseconds(const T& val) noexcept -> moment
		{
			moment t;
			return t.set_milliseconds(val);
		}

		template<typename T>
		inline auto microseconds(const T& val) noexcept -> moment
		{
			moment t;
			return t.set_microseconds(val);
		}

		template<typename T>
		inline auto nanoseconds(const T& val) noexcept -> moment
		{
			moment t;
			return t.set_nanoseconds(val);
		}

		template<typename T>
		inline auto ticks(const T& val) noexcept -> moment
		{
			moment t;
			return t.set_ticks(val);
		}

		template<typename T>
		inline void micro_sleep_seconds(const T& seconds) noexcept
		{
			micro_sleep(static_cast<int64_t>((static_cast<long double>(performance_frequency())) * static_cast<long double>(seconds)));
		}

		template<typename T>
		inline void micro_sleep_milliseconds(const T& seconds) noexcept
		{
			micro_sleep(static_cast<int64_t>((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull)) * static_cast<long double>(seconds)));
		}

		template<typename T>
		inline void micro_sleep_microseconds(const T& seconds) noexcept
		{
			micro_sleep(
				static_cast<int64_t>((static_cast<long double>(performance_frequency()) / static_cast<long double>(1000ull * 1000ull)) * static_cast<long double>(seconds)));
		}

		template<typename T>
		inline void micro_sleep_ticks(const T& tx) noexcept
		{
			micro_sleep(static_cast<int64_t>(tx));
		}

		inline void micro_sleep(const moment& t) noexcept
		{
			micro_sleep(t.as_ticks<int64_t>());
		}

		template<typename T>
		inline void sleepseconds(const T& seconds) noexcept
		{
			sleep(moment().set_seconds(seconds).template as_milliseconds<int64_t>());
		}

		template<typename T>
		inline void sleepmilliseconds(const T& seconds) noexcept
		{
			sleep(moment().set_milliseconds(seconds).template as_milliseconds<int64_t>());
		}

		template<typename T>
		inline void sleepmicroseconds(const T& seconds) noexcept
		{
			sleep(moment().set_microseconds(seconds).template as_milliseconds<int64_t>());
		}

		template<typename T>
		inline void sleepticks(const T& tx) noexcept
		{
			sleep(moment().set_ticks(tx).template as_milliseconds<int64_t>());
		}

		inline void sleep(const moment& t) noexcept
		{
			sleep(t.template as_milliseconds<int64_t>());
		}

		inline void long_clock::update() noexcept
		{
			const moment n = now();
			const moment d = n - last_moment;
			last_moment	   = n;
			value += d.template as_milliseconds<int64_t>();
		}
	} // namespace time
} // namespace mu

namespace mu
{
	enum class messagebox_result : int
	{
		ok = 0,
		cancel,
		yes,
		no,
		quit,
		none,
		error
	};

	enum class messagebox_style : int
	{
		info = 0,
		warning,
		error,
		question
	};

	enum class messagebox_buttons : int
	{
		ok = 0,
		okcancel,
		yesno,
		quit
	};

	namespace details
	{
		template<typename T>
		struct future_helper
		{
			bool m_state = false;
			T	 m_future;

			using value_type = decltype(m_future.get());

			auto is_active() const noexcept -> bool
			{
				return m_state;
			}

			auto is_ready() const noexcept -> bool
			{
				if (m_state)
				{
					return future_is_ready(m_future);
				}
				return false;
			}

			auto acquire_value() noexcept -> value_type
			{
				m_state = false;
				return m_future.get();
			}

			auto get_value() noexcept -> std::optional<value_type>
			{
				if (is_ready())
				{
					return acquire_value();
				}
				return std::nullopt;
			}
		};
	} // namespace details

	namespace details
	{
		auto show_messagebox(const char* message, const char* title, messagebox_style style, messagebox_buttons buttons) noexcept -> std::future<messagebox_result>;
	}

	using messagebox_future = details::future_helper<decltype(details::show_messagebox("", "", messagebox_style(), messagebox_buttons()))>;
	inline auto show_messagebox(const char* message, const char* title, messagebox_style style, messagebox_buttons buttons) noexcept -> messagebox_future
	{
		return {true, details::show_messagebox(message, title, style, buttons)};
	}

	namespace details
	{
		auto show_file_open_dialog(std::string_view origin, std::string_view filter) noexcept -> optional_future<std::string>;
		auto show_file_open_multiple_dialog(std::string_view origin, std::string_view filter) noexcept -> optional_future<std::vector<std::string>>;
		auto show_file_save_dialog(std::string_view origin, std::string_view filter) noexcept -> optional_future<std::string>;
		auto show_path_dialog(std::string_view origin, std::string_view filter) noexcept -> optional_future<std::string>;
	} // namespace details

	using file_open_dialog_future = details::future_helper<decltype(details::show_file_open_dialog(std::string_view(), std::string_view()))>;
	inline auto show_file_open_dialog(std::string_view origin, std::string_view filter) noexcept -> file_open_dialog_future
	{
		return {true, details::show_file_open_dialog(origin, filter)};
	}

	using file_open_multiple_dialog_future = details::future_helper<decltype(details::show_file_open_multiple_dialog(std::string_view(), std::string_view()))>;
	inline auto show_file_open_multiple_dialog(std::string_view origin, std::string_view filter) noexcept -> file_open_multiple_dialog_future
	{
		return {true, details::show_file_open_multiple_dialog(origin, filter)};
	}

	using file_save_dialog_future = details::future_helper<decltype(details::show_file_save_dialog(std::string_view(), std::string_view()))>;
	inline auto show_file_save_dialog(std::string_view origin, std::string_view filter) noexcept -> file_save_dialog_future
	{
		return {true, details::show_file_save_dialog(origin, filter)};
	}

	using path_dialog_future = details::future_helper<decltype(details::show_path_dialog(std::string_view(), std::string_view()))>;
	inline auto show_path_dialog(std::string_view origin, std::string_view filter) noexcept -> path_dialog_future
	{
		return {true, details::show_path_dialog(origin, filter)};
	}
} // namespace mu

namespace backward
{
	class StackTrace;
}

namespace mu
{
	namespace debug
	{
		namespace details
		{
			struct logger_interface
			{
				logger_interface()			= default;
				virtual ~logger_interface() = default;

				virtual auto stdout_logger() noexcept -> std::shared_ptr<spdlog::logger> = 0;
				virtual auto stderr_logger() noexcept -> std::shared_ptr<spdlog::logger> = 0;
			};
		} // namespace details

		using logger = mu::exported_singleton<mu::virtual_singleton<details::logger_interface>>;

		void log_stack_trace(spdlog::logger& l, spdlog::level::level_enum lvl, unsigned int level_skip) noexcept;

	} // namespace debug

	static inline auto error_handlers = std::make_tuple(
		[](runtime_error::not_specified x, leaf::e_source_location sl)
		{
			debug::logger()->stderr_logger()->log(spdlog::level::err, "{0} :: {1} -> {2} : runtime_error :: not_specified", sl.line, sl.file, sl.function);
		},
		[](common_error x, leaf::e_source_location sl)
		{
			debug::logger()->stderr_logger()->log(spdlog::level::err, "{0} :: {1} -> {2} : common_error", sl.line, sl.file, sl.function);
		},
		[]
		{
			debug::logger()->stderr_logger()->log(spdlog::level::err, "???");
		});

	template<class TryBlock>
	constexpr inline auto try_handle(TryBlock&& try_block) -> typename std::decay<decltype(std::declval<TryBlock>()().value())>::type
	{
		return leaf::try_handle_all(try_block, error_handlers);
	}

} // namespace mu

namespace mu
{
	template<typename T_CONTAINER, typename T_FUNC, typename T_FUNC_RET>
	auto for_some_return(T_CONTAINER& container, T_FUNC func, T_FUNC_RET&& true_return_value, T_FUNC_RET&& false_return_value) noexcept -> T_FUNC_RET
	{
		for (auto& itor : container)
		{
			if (func(itor))
			{
				return true_return_value;
			}
		}

		return false_return_value;
	}

	template<typename T_CONTAINER, typename T_FUNC, typename T_FUNC_RET>
	auto for_some_optional_return(T_CONTAINER& container, T_FUNC func, T_FUNC_RET&& false_return_value) noexcept -> T_FUNC_RET
	{
		for (auto& itor : container)
		{
			if (auto res = func(itor))
			{
				return *res;
			}
		}

		return false_return_value;
	}
} // namespace mu

namespace mu
{
	void enable_dpi_awareness() noexcept;
	auto get_dpi_scale_for_monitor(void* monitor) noexcept -> float;
	auto get_dpi_scale_for_hwnd(void* hwnd) noexcept -> float;
} // namespace mu

namespace mu
{

} // namespace mu