
#ifndef PLS_INTERNAL_DATA_STRUCTURES_OPTIONAL_H_
#define PLS_INTERNAL_DATA_STRUCTURES_OPTIONAL_H_

#include <utility>
#include <type_traits>

#include "pls/internal/data_structures/delayed_initialization.h"

namespace pls {
namespace internal {
namespace data_structures {

template<typename T>
class optional {
 public:
  optional() = default;
  optional(optional &other) noexcept : optional(const_cast<const optional &>(other)) {};
  optional(const optional &other) noexcept {
    if (other) {
      data_.initialize(other.data_.object());
    }
  }
  optional(optional &&other) noexcept {
    data_ = std::move(other.data_);
  }
  optional &operator=(const optional &other) {
    if (&other == this) {
      return *this;
    }

    if (data_.initialized()) {
      data_.destroy();
    }
    if (other) {
      data_.initialize(other.data_.object());
    }

    return *this;
  }
  optional &operator=(optional &&other) noexcept {
    if (&other == this) {
      return *this;
    }

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

    return *this;
  }

  template<typename ...ARGS>
  explicit optional(ARGS &&...args): data_{std::forward<ARGS>(args)...} {}

  operator bool() const {
    return data_.initialized();
  }

  T &operator*() {
    return *data_;
  }

 private:
  delayed_initialization<T> data_;
};

}
}
}

#endif //PLS_INTERNAL_DATA_STRUCTURES_OPTIONAL_H_
