/**
 * @file src/sync.h
 * @brief todo
 */
#pragma once

#include <array>
#include <mutex>
#include <utility>

namespace sync_util {

  template <class T, class M = std::mutex>
  class sync_t {
  public:
    using value_t = T;
    using mutex_t = M;

    std::lock_guard<mutex_t>
    lock() {
      return std::lock_guard { _lock };
    }

    template <class... Args>
    sync_t(Args &&...args):
        raw { std::forward<Args>(args)... } {}

    sync_t &
    operator=(sync_t &&other) noexcept {
      std::lock(_lock, other._lock);

      raw = std::move(other.raw);

      _lock.unlock();
      other._lock.unlock();

      return *this;
    }

    sync_t &
    operator=(sync_t &other) noexcept {
      std::lock(_lock, other._lock);

      raw = other.raw;

      _lock.unlock();
      other._lock.unlock();

      return *this;
    }

    template <class V>
    sync_t &
    operator=(V &&val) {
      auto lg = lock();

      raw = val;

      return *this;
    }

    sync_t &
    operator=(const value_t &val) noexcept {
      auto lg = lock();

      raw = val;

      return *this;
    }

    sync_t &
    operator=(value_t &&val) noexcept {
      auto lg = lock();

      raw = std::move(val);

      return *this;
    }

    value_t *
    operator->() {
      return &raw;
    }

    value_t &
    operator*() {
      return raw;
    }

    const value_t &
    operator*() const {
      return raw;
    }

    value_t raw;

  private:
    mutex_t _lock;
  };

}  // namespace sync_util
