#ifndef TABLEAU_HYPER_IMPL_PARAMETERS_IMPL_HPP
#define TABLEAU_HYPER_IMPL_PARAMETERS_IMPL_HPP

#include "hyperapi/impl/HyperException.impl.hpp"
#include <hyperapi/impl/Parameters.hpp>

#include <cassert>

// NOLINTNEXTLINE We can't use nested namespaces as it has to remain C++11 compatible
namespace hyperapi {
namespace internal {
inline Parameters::Parameters(hyper_parameters_t* handle) noexcept
   : handle_(handle) {
   assert(handle_);
}

inline Parameters::~Parameters() noexcept {
   if (handle_) {
      hyper_parameters_destroy(handle_);
   }
}

inline Parameters::Parameters(const Parameters& other) {
   assert(other.handle_);

   handle_ = hyper_parameters_copy(other.handle_);
   if (!handle_) {
      throw std::bad_alloc();
   }
}

inline Parameters::Parameters(Parameters&& other) noexcept
   : handle_(internal::exchange(other.handle_, nullptr)) {
}

inline Parameters& Parameters::operator=(Parameters&& other) noexcept {
   assert(other.handle_);

   if (&other != this) {
      if (handle_) {
         hyper_parameters_destroy(handle_);
      }

      handle_ = internal::exchange(other.handle_, nullptr);
   }

   return *this;
}

inline Parameters& Parameters::operator=(const Parameters& other) {
   assert(other.handle_);

   if (&other != this) {
      hyper_parameters_t* handle = hyper_parameters_copy(other.handle_);
      if (!handle) {
         throw std::bad_alloc();
      }

      if (handle_) {
         hyper_parameters_destroy(handle_);
      }

      handle_ = handle;
   }

   return *this;
}

inline void Parameters::set(const char* key, const char* value) {
   assert(key);
   assert(value);

   if (hyper_error_t* error = hyper_parameters_set(handle_, key, value)) {
      throw internal::makeHyperException(error);
   }
}

inline Parameters createConnectionParameters(hyper_instance_t* instance) {
   hyper_parameters_t* handle;
   if (hyper_error_t* error = hyper_create_connection_parameters(instance, &handle)) {
      assert(!handle);
      throw internal::makeHyperException(error);
   }

   return Parameters(handle);
}

inline Parameters Parameters::createConnectionParameters() {
   return internal::createConnectionParameters(nullptr);
}
}
}

#endif
