/// These are automatically generated checked C++ bindings for isl.
///
/// isl is a library for computing with integer sets and maps described by
/// Presburger formulas. On top of this, isl provides various tools for
/// polyhedral compilation, ranging from dependence analysis over scheduling
/// to AST generation.

#ifndef ISL_CPP_CHECKED
#define ISL_CPP_CHECKED

#include <stdio.h>
#include <stdlib.h>

#include <functional>
#include <memory>
#include <ostream>
#include <string>
#include <type_traits>

#if __cplusplus >= 201703L
#include <any>
#include <optional>
#endif

namespace isl {
namespace checked {

#define ISLPP_STRINGIZE_(X) #X
#define ISLPP_STRINGIZE(X) ISLPP_STRINGIZE_(X)

#define ISLPP_ASSERT(test, message)                          \
  do {                                                       \
    if (test)                                                \
      break;                                                 \
    fputs("Assertion \"" #test "\" failed at " __FILE__      \
      ":" ISLPP_STRINGIZE(__LINE__) "\n  " message "\n",     \
      stderr);                                               \
    abort();                                                 \
  } while (0)

/* Class used to check that isl::checked::boolean,
 * isl::checked::stat and isl::checked::size values are checked for errors.
 */
struct checker {
	bool checked = false;
	~checker() {
		ISLPP_ASSERT(checked, "IMPLEMENTATION ERROR: Unchecked state");
	}
};

class boolean {
private:
  mutable std::shared_ptr<checker> check = std::make_shared<checker>();
  isl_bool val;

  friend boolean manage(isl_bool val);
  boolean(isl_bool val): val(val) {}
public:
  static boolean error() {
    return boolean(isl_bool_error);
  }
  boolean()
      : val(isl_bool_error) {}

  /* implicit */ boolean(bool val)
      : val(val ? isl_bool_true : isl_bool_false) {}

  isl_bool release() {
    auto tmp = val;
    val = isl_bool_error;
    check->checked = true;
    return tmp;
  }

  bool is_error() const { check->checked = true; return val == isl_bool_error; }
  bool is_false() const { check->checked = true; return val == isl_bool_false; }
  bool is_true() const { check->checked = true; return val == isl_bool_true; }

  explicit operator bool() const {
    ISLPP_ASSERT(check->checked, "IMPLEMENTATION ERROR: Unchecked error state");
    ISLPP_ASSERT(!is_error(), "IMPLEMENTATION ERROR: Unhandled error state");
    return is_true();
  }

  boolean negate() {
    if (val == isl_bool_true)
      val = isl_bool_false;
    else if (val == isl_bool_false)
      val = isl_bool_true;
    return *this;
  }

  boolean operator!() const {
    return boolean(*this).negate();
  }
};

inline boolean manage(isl_bool val) {
  return boolean(val);
}

class ctx {
	isl_ctx *ptr;
public:
	/* implicit */ ctx(isl_ctx *ctx) : ptr(ctx) {}
	isl_ctx *release() {
		auto tmp = ptr;
		ptr = nullptr;
		return tmp;
	}
	isl_ctx *get() {
		return ptr;
	}
#if __cplusplus >= 201703L
	static void free_user(void *user) {
		std::any *p = static_cast<std::any *>(user);
		delete p;
	}
#endif
};

/* Class encapsulating an isl_stat value.
 */
class stat {
private:
	mutable std::shared_ptr<checker> check = std::make_shared<checker>();
	isl_stat val;

	friend stat manage(isl_stat val);
	stat(isl_stat val) : val(val) {}
public:
	static stat ok() {
		return stat(isl_stat_ok);
	}
	static stat error() {
		return stat(isl_stat_error);
	}
	stat() : val(isl_stat_error) {}

	isl_stat release() {
		check->checked = true;
		return val;
	}

	bool is_error() const {
		check->checked = true;
		return val == isl_stat_error;
	}
	bool is_ok() const {
		check->checked = true;
		return val == isl_stat_ok;
	}
};

inline stat manage(isl_stat val)
{
	return stat(val);
}

/* Class encapsulating an isl_size value.
 */
class size {
private:
	mutable std::shared_ptr<checker> check = std::make_shared<checker>();
	isl_size val;

	friend size manage(isl_size val);
	size(isl_size val) : val(val) {}
public:
	size() : val(isl_size_error) {}

	isl_size release() {
		auto tmp = val;
		val = isl_size_error;
		check->checked = true;
		return tmp;
	}

	bool is_error() const {
		check->checked = true;
		return val == isl_size_error;
	}

	explicit operator unsigned() const {
		ISLPP_ASSERT(check->checked,
			    "IMPLEMENTATION ERROR: Unchecked error state");
		ISLPP_ASSERT(!is_error(),
			    "IMPLEMENTATION ERROR: Unhandled error state");
		return val;
	}
};

inline size manage(isl_size val)
{
	return size(val);
}

}
} // namespace isl

