// This file was generated by $generator from $template

#pragma once

#include <atomic>
#include <cstdint>
#include <cstring>
#include "anari/anari_cpp.hpp"

$begin_namespaces

class ParameterPack;

void anariDeleteInternal(ANARIDevice, ANARIObject);

template <class T>
struct base_flags
{
  static const uint32_t value = 0;
};

class ObjectBase
{
  std::atomic<uint64_t> refcount;

 protected:
  ANARIDevice device;
  ANARIObject handle;

 public:
  ObjectBase(ANARIDevice d, ANARIObject handle)
      : refcount(1), device(d), handle(handle)
  {}

  virtual void retain()
  {
    refcount += 1;
  }
  virtual void release()
  {
    uint64_t c = refcount.fetch_sub(1);
    if (c == 1) {
      anariDeleteInternal(device, handle);
    } else if ((c & UINT64_C(0xFFFFFFFF)) == 1) {
      releasePublic();
    }
  }
  virtual void retainInternal(ANARIObject)
  {
    refcount += UINT64_C(0x100000000);
  }
  virtual void releaseInternal(ANARIObject)
  {
    uint64_t c = refcount.fetch_sub(UINT64_C(0x100000000));
    if (c == UINT64_C(0x100000000)) {
      anariDeleteInternal(device, handle);
    }
  }
  virtual void releasePublic() {}

  virtual bool set(
      const char *paramname, ANARIDataType type, const void *mem) = 0;
  virtual void unset(const char *paramname) = 0;
  virtual void commit() = 0;
  virtual int getProperty(const char *propname,
      ANARIDataType type,
      void *mem,
      uint64_t size,
      ANARIWaitMask mask) = 0;
  virtual ~ObjectBase() {}
  virtual ANARIDataType type() const = 0;
  virtual const char *subtype() const = 0;
  virtual uint32_t id() const = 0;
  virtual ParameterPack &parameters() = 0;
  template <class T, class H>
  T handle_cast(H h);
};

class ArrayObjectBase : public ObjectBase
{
 public:
  ArrayObjectBase(ANARIDevice d, ANARIObject handle) : ObjectBase(d, handle) {}
  virtual void *map() = 0;
  virtual void unmap() = 0;
};

template <>
struct base_flags<ArrayObjectBase>
{
  static const uint32_t value = 0x10000000u;
};

class FrameObjectBase : public ObjectBase
{
 public:
  FrameObjectBase(ANARIDevice d, ANARIObject handle) : ObjectBase(d, handle) {}
  virtual void *mapFrame(
      const char *, uint32_t *, uint32_t *, ANARIDataType *) = 0;
  virtual void unmapFrame(const char *) = 0;
  virtual void renderFrame() = 0;
  virtual void discardFrame() = 0;
  virtual int frameReady(ANARIWaitMask) = 0;
};

template <>
struct base_flags<FrameObjectBase>
{
  static const uint32_t value = 0x20000000u;
};

template <class T, class B = ObjectBase>
class DefaultObject : public B
{
 protected:
  T staging;
  DefaultObject(ANARIDevice d, ANARIObject handle)
      : B(d, handle), staging(d, handle), current(d, handle)
  {}

 public:
  T current;

  bool set(const char *paramname, ANARIDataType type, const void *mem) override
  {
    return staging.set(paramname, type, mem);
  }
  void unset(const char *paramname) override
  {
    staging.unset(paramname);
  }
  void commit() override
  {
    current = staging;
  }
  int getProperty(const char *propname,
      ANARIDataType type,
      void *mem,
      uint64_t size,
      ANARIWaitMask mask) override
  {
    (void)propname;
    (void)type;
    (void)mem;
    (void)size;
    (void)mask;
    return 0;
  }

  ANARIDataType type() const final
  {
    return T::type;
  }
  const char *subtype() const final
  {
    return T::subtype;
  }
  uint32_t id() const final
  {
    return T::id | base_flags<B>::value;
  }
  ParameterPack &parameters() override
  {
    return current;
  }
};

template <class T>
class Object : public DefaultObject<T>
{
 public:
  Object(ANARIDevice d, ANARIObject handle) : DefaultObject<T>(d, handle) {}
};

template <class T>
struct is_convertible
{
  static bool check(ObjectBase *base)
  {
    uint32_t mask = base_flags<T>::value;
    return (base->id() & mask) == mask;
  }
};

template <class T>
struct is_convertible<Object<T>>
{
  static bool check(ObjectBase *base)
  {
    return (base->id() & 0x00FFFFFFu) == T::id;
  }
};

$end_namespaces
