// Generated by RcppR6 (0.2.3): do not edit by hand -*-c++-*-
#ifndef _GLMMSR_RCPPR6_SUPPORT_HPP_
#define _GLMMSR_RCPPR6_SUPPORT_HPP_

// These functions are all used in RcppR6_post.hpp, and require Rcpp
// to be loaded.
//
// It's possible that this will move into the RcppR6_post.hpp file
// itself at some point.
#include <Rcpp.h>

namespace glmmsr {
namespace RcppR6 {

// Not quite traits, but should be close enough.  This is non-API!
namespace traits {
template <typename T>
std::string class_name_r() {
  Rcpp::stop("Unknown class");
  return ""; // never get here.
}
template <typename T>
std::string package_name() {
  Rcpp::stop("Unknown class");
  return ""; // never get here.
}
template <typename T>
std::string generator_name() {
  Rcpp::stop("Unknown class");
  return ""; // never get here.
}

template <typename T>
std::string class_name_r(const T&) {
  return class_name_r<T>();
}
template <typename T>
std::string package_name(const T&) {
  return package_name<T>();
}
template <typename T>
std::string generator_name(const T&) {
  return generator_name<T>();
}
}

// In parallel/contrast with Rcpp::is :)
//
// This will throw a *compile time* error if T is not a type known to
// RcppR6.  It will return false at runtime if x is not the required
// type.
//
// Note that this does not actually check that we inherit from R6.
// Strictly we should look for the classname, then from that position
// check that we hit R6.  But omelettes and eggs and all that.
//
// It would be nice to roll this into Rcpp::is, but I don't really see
// how to do that -- Rcpp::is mostly seems to use things in
// Rcpp::internal and I don't see any examples of extending things.
//
// Use this by doing:
//    glmmsr::RcppR6::is<mytype>(x)
template <typename T>
bool is(Rcpp::RObject x) {
  return x.inherits(traits::class_name_r<T>().c_str());
}

template <typename T>
void check_ptr_valid(Rcpp::XPtr<T> p) {
  T* test = p;
  if (test == NULL) {
    Rcpp::stop("Pointer is NULL");
  }
}

// This is absolutely basic, and does not do much more than XPtr.
// Differences are:
//   - pointer validity checking when an externalptr is recieved.
//   - knows what "type" of pointer it contains and checks this when
//     recieved
//   - can generate a special R6 object on return
//
// Possible improvements:
//   - the appropriate generator should really be a static member of
//     the class, which we generate only when it's not been
//     initialised yet.  That will save a few calls, but will take
//     care through a load/unload cycle.
//   - first-class access to *general* R6 classes would be preferable
//     to the dodgy 'find' call.
template <typename T>
class RcppR6 {
public:
  RcppR6(SEXP x) : ptr(ptr_from_R6(x)) {}
  RcppR6(const T& x) : ptr(new T(x), true) {}
  T& operator*() const {
    return *ptr;
  }
  inline operator T*(){ return (T*)(ptr); }
  T* operator->() const {
    return &(*ptr);
  }
  Rcpp::XPtr<T> ptr;
  // Convert from an R6 object from R:
  static Rcpp::XPtr<T> ptr_from_R6(Rcpp::RObject x) {
    if (is<T>(x)) {
      Rcpp::Environment xe = Rcpp::as<Rcpp::Environment>(x);
      Rcpp::XPtr<T> ptr = Rcpp::as<Rcpp::XPtr<T> >(xe[".ptr"]);
      check_ptr_valid<T>(ptr);
      return ptr;
    } else {
      Rcpp::stop("Expected an object of type R6 / " +
                 traits::class_name_r<T>());
      return Rcpp::as<Rcpp::XPtr<T> >(x); // Won't get here
    }
  }
  SEXP to_R6() const {
    const std::string packagename(traits::package_name<T>());
    const std::string generatorname(traits::generator_name<T>());
    Rcpp::Environment base("package:base");
    Rcpp::Function getNamespace = base["getNamespace"];
    Rcpp::Environment pkg = getNamespace(packagename);
    // TODO: need to deal with failure here (i.e. R_NilValue), or we
    // get a really hard to diagnose error message.
    Rcpp::Environment Generator = pkg[generatorname];
    Rcpp::Function Generator_new = Generator["new"];
    return Generator_new(ptr);
  }
};

}
}

#endif
