/// This file has been generated, if you wish to
/// modify it in a permanent way, please refer
/// to the script file : gen/generator_java.rb

#include "interface.hh"

// In case of errors...
template<typename Lang, typename Cxx>
Cxx lang2cxx(Lang in)
{
  return in.error_should_not_happens;
}

template<typename Cxx, typename Lang>
Lang cxx2lang(Cxx in)
{
  return in.error_should_not_happens;
}

template<typename Lang, typename Cxx>
std::vector<Cxx> lang2cxx_array(jobject in);

template<typename Cxx, typename Lang>
jarray cxx2lang_array(std::vector<Cxx> in);

// Basic type wrappers
template <>
jboolean cxx2lang<boolean, jboolean>(boolean in)
{
  return (jboolean)in;
}

template <>
boolean lang2cxx<jboolean, boolean>(jboolean in)
{
  return (boolean)in;
}

template <>
jarray cxx2lang_array<boolean, jboolean>(std::vector<boolean> in)
{
  jbooleanArray out = jrt.env->NewBooleanArray((jsize)in.size());
  jrt.env->SetBooleanArrayRegion(out, (jsize)0, (jsize)in.size(), (const jboolean*)in.data());
  return (jarray)out;
}

template <>
std::vector<boolean> lang2cxx_array<jboolean, boolean>(jobject in)
{
  jbooleanArray array = (jbooleanArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jboolean* datas = jrt.env->GetBooleanArrayElements(array, NULL);
  std::vector<boolean> out(datas, datas + size);
  jrt.env->ReleaseBooleanArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jbyte cxx2lang<byte, jbyte>(byte in)
{
  return (jbyte)in;
}

template <>
byte lang2cxx<jbyte, byte>(jbyte in)
{
  return (byte)in;
}

template <>
jarray cxx2lang_array<byte, jbyte>(std::vector<byte> in)
{
  jbyteArray out = jrt.env->NewByteArray((jsize)in.size());
  jrt.env->SetByteArrayRegion(out, (jsize)0, (jsize)in.size(), (const jbyte*)in.data());
  return (jarray)out;
}

template <>
std::vector<byte> lang2cxx_array<jbyte, byte>(jobject in)
{
  jbyteArray array = (jbyteArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jbyte* datas = jrt.env->GetByteArrayElements(array, NULL);
  std::vector<byte> out(datas, datas + size);
  jrt.env->ReleaseByteArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jchar cxx2lang<char, jchar>(char in)
{
  return (jchar)in;
}

template <>
char lang2cxx<jchar, char>(jchar in)
{
  return (char)in;
}

template <>
jarray cxx2lang_array<char, jchar>(std::vector<char> in)
{
  jcharArray out = jrt.env->NewCharArray((jsize)in.size());
  jrt.env->SetCharArrayRegion(out, (jsize)0, (jsize)in.size(), (const jchar*)in.data());
  return (jarray)out;
}

template <>
std::vector<char> lang2cxx_array<jchar, char>(jobject in)
{
  jcharArray array = (jcharArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jchar* datas = jrt.env->GetCharArrayElements(array, NULL);
  std::vector<char> out(datas, datas + size);
  jrt.env->ReleaseCharArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jshort cxx2lang<short, jshort>(short in)
{
  return (jshort)in;
}

template <>
short lang2cxx<jshort, short>(jshort in)
{
  return (short)in;
}

template <>
jarray cxx2lang_array<short, jshort>(std::vector<short> in)
{
  jshortArray out = jrt.env->NewShortArray((jsize)in.size());
  jrt.env->SetShortArrayRegion(out, (jsize)0, (jsize)in.size(), (const jshort*)in.data());
  return (jarray)out;
}

template <>
std::vector<short> lang2cxx_array<jshort, short>(jobject in)
{
  jshortArray array = (jshortArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jshort* datas = jrt.env->GetShortArrayElements(array, NULL);
  std::vector<short> out(datas, datas + size);
  jrt.env->ReleaseShortArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jint cxx2lang<int, jint>(int in)
{
  return (jint)in;
}

template <>
int lang2cxx<jint, int>(jint in)
{
  return (int)in;
}

template <>
jarray cxx2lang_array<int, jint>(std::vector<int> in)
{
  jintArray out = jrt.env->NewIntArray((jsize)in.size());
  jrt.env->SetIntArrayRegion(out, (jsize)0, (jsize)in.size(), (const jint*)in.data());
  return (jarray)out;
}

template <>
std::vector<int> lang2cxx_array<jint, int>(jobject in)
{
  jintArray array = (jintArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jint* datas = jrt.env->GetIntArrayElements(array, NULL);
  std::vector<int> out(datas, datas + size);
  jrt.env->ReleaseIntArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jlong cxx2lang<long, jlong>(long in)
{
  return (jlong)in;
}

template <>
long lang2cxx<jlong, long>(jlong in)
{
  return (long)in;
}

template <>
jarray cxx2lang_array<long, jlong>(std::vector<long> in)
{
  jlongArray out = jrt.env->NewLongArray((jsize)in.size());
  jrt.env->SetLongArrayRegion(out, (jsize)0, (jsize)in.size(), (const jlong*)in.data());
  return (jarray)out;
}

template <>
std::vector<long> lang2cxx_array<jlong, long>(jobject in)
{
  jlongArray array = (jlongArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jlong* datas = jrt.env->GetLongArrayElements(array, NULL);
  std::vector<long> out(datas, datas + size);
  jrt.env->ReleaseLongArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jfloat cxx2lang<float, jfloat>(float in)
{
  return (jfloat)in;
}

template <>
float lang2cxx<jfloat, float>(jfloat in)
{
  return (float)in;
}

template <>
jarray cxx2lang_array<float, jfloat>(std::vector<float> in)
{
  jfloatArray out = jrt.env->NewFloatArray((jsize)in.size());
  jrt.env->SetFloatArrayRegion(out, (jsize)0, (jsize)in.size(), (const jfloat*)in.data());
  return (jarray)out;
}

template <>
std::vector<float> lang2cxx_array<jfloat, float>(jobject in)
{
  jfloatArray array = (jfloatArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jfloat* datas = jrt.env->GetFloatArrayElements(array, NULL);
  std::vector<float> out(datas, datas + size);
  jrt.env->ReleaseFloatArrayElements(array, datas, JNI_ABORT);
  return out;
}

template <>
jdouble cxx2lang<double, jdouble>(double in)
{
  return (jdouble)in;
}

template <>
double lang2cxx<jdouble, double>(jdouble in)
{
  return (double)in;
}

template <>
jarray cxx2lang_array<double, jdouble>(std::vector<double> in)
{
  jdoubleArray out = jrt.env->NewDoubleArray((jsize)in.size());
  jrt.env->SetDoubleArrayRegion(out, (jsize)0, (jsize)in.size(), (const jdouble*)in.data());
  return (jarray)out;
}

template <>
std::vector<double> lang2cxx_array<jdouble, double>(jobject in)
{
  jdoubleArray array = (jdoubleArray)in;
  jsize size = jrt.env->GetArrayLength(array);
  jdouble* datas = jrt.env->GetDoubleArrayElements(array, NULL);
  std::vector<double> out(datas, datas + size);
  jrt.env->ReleaseDoubleArrayElements(array, datas, JNI_ABORT);
  return out;
}

// String wrappers
template <>
jstring cxx2lang<std::string, jstring>(std::string in)
{
  return jrt.env->NewStringUTF(in.data());
}

template <>
std::string lang2cxx<jstring, std::string>(jstring in)
{
  jboolean is_copy;
  const jchar* datas = jrt.env->GetStringChars(in, &is_copy);
  jsize size = jrt.env->GetStringLength(in);
  std::string out((const char*)datas, (size_t)size);
  if (is_copy)
    jrt.env->ReleaseStringChars(in, datas);
  return out;
}

// Object array wrappers (assume Lang::class exists)
template <typename Cxx, typename Lang>
jarray cxx2lang_array(std::vector<Cxx> in)
{
  jobjectArray out = jrt.env->NewObjectArray((jsize)in.size(), Lang::Class(), NULL);
  for (size_t i = 0; i < in.size(); i++)
    jrt.env->SetObjectArrayElement(out, (jsize)i, cxx2lang<Cxx, jobject>(in[i]));
  return out;
}

template <typename Lang, typename Cxx>
std::vector<Cxx> lang2cxx_array(jobject in)
{
  jobjectArray array = (jobjectArray)in;
  size_t size = (size_t)jrt.env->GetArrayLength(array);
  std::vector<Cxx> out;
  for (size_t i = 0; i < size; i++)
    out.push_back(lang2cxx<jobject, Cxx>(jrt.env->GetObjectArrayElement(array, (jsize)i)));
  return out;
}

/// Types de cases
template<>
case_type lang2cxx<jobject, case_type>(jobject in)
{
  jmethodID ordinal = jrt.env->GetMethodID(CaseType::Class(), "ordinal", "()I");
  return case_type(lang2cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx2lang<case_type, jobject>(case_type in)
{
  jmethodID method = jrt.env->GetStaticMethodID(CaseType::Class(), "values", "()[LCaseType;");
  jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(CaseType::Class(), method);
  return jrt.env->GetObjectArrayElement(values, (jsize)in);
}


/// Types de propriétés des éléments
template<>
element_propriete lang2cxx<jobject, element_propriete>(jobject in)
{
  jmethodID ordinal = jrt.env->GetMethodID(ElementPropriete::Class(), "ordinal", "()I");
  return element_propriete(lang2cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx2lang<element_propriete, jobject>(element_propriete in)
{
  jmethodID method = jrt.env->GetStaticMethodID(ElementPropriete::Class(), "values", "()[LElementPropriete;");
  jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(ElementPropriete::Class(), method);
  return jrt.env->GetObjectArrayElement(values, (jsize)in);
}


/// Erreurs possibles
template<>
erreur lang2cxx<jobject, erreur>(jobject in)
{
  jmethodID ordinal = jrt.env->GetMethodID(Erreur::Class(), "ordinal", "()I");
  return erreur(lang2cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx2lang<erreur, jobject>(erreur in)
{
  jmethodID method = jrt.env->GetStaticMethodID(Erreur::Class(), "values", "()[LErreur;");
  jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(Erreur::Class(), method);
  return jrt.env->GetObjectArrayElement(values, (jsize)in);
}


/// Types d’actions
template<>
action_type lang2cxx<jobject, action_type>(jobject in)
{
  jmethodID ordinal = jrt.env->GetMethodID(ActionType::Class(), "ordinal", "()I");
  return action_type(lang2cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx2lang<action_type, jobject>(action_type in)
{
  jmethodID method = jrt.env->GetStaticMethodID(ActionType::Class(), "values", "()[LActionType;");
  jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(ActionType::Class(), method);
  return jrt.env->GetObjectArrayElement(values, (jsize)in);
}


/// Position sur la carte, donnée par deux coordonnées
template <>
position lang2cxx<jobject, position>(jobject in)
{
  position out;
  out.ligne = lang2cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Position::Class(), "ligne", "I")));
  out.colonne = lang2cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Position::Class(), "colonne", "I")));
  return out;
}

template <>
jobject cxx2lang<position, jobject>(position in)
{
  jobject out = jrt.env->NewObject(Position::Class(), jrt.env->GetMethodID(Position::Class(), "<init>", "()V"));
  jrt.env->SetIntField(out, jrt.env->GetFieldID(Position::Class(), "ligne", "I"), cxx2lang<int, jint>(in.ligne));
  jrt.env->SetIntField(out, jrt.env->GetFieldID(Position::Class(), "colonne", "I"), cxx2lang<int, jint>(in.colonne));
  return out;
}


/// Échantillon, défini par deux types d’éléments
template <>
echantillon lang2cxx<jobject, echantillon>(jobject in)
{
  echantillon out;
  out.element1 = lang2cxx<jobject, case_type>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(Echantillon::Class(), "element1", "LCaseType;")));
  out.element2 = lang2cxx<jobject, case_type>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(Echantillon::Class(), "element2", "LCaseType;")));
  return out;
}

template <>
jobject cxx2lang<echantillon, jobject>(echantillon in)
{
  jobject out = jrt.env->NewObject(Echantillon::Class(), jrt.env->GetMethodID(Echantillon::Class(), "<init>", "()V"));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(Echantillon::Class(), "element1", "LCaseType;"), cxx2lang<case_type, jobject>(in.element1));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(Echantillon::Class(), "element2", "LCaseType;"), cxx2lang<case_type, jobject>(in.element2));
  return out;
}


/// Position d’un échantillon, donnée par deux positions adjacentes
template <>
position_echantillon lang2cxx<jobject, position_echantillon>(jobject in)
{
  position_echantillon out;
  out.pos1 = lang2cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(PositionEchantillon::Class(), "pos1", "LPosition;")));
  out.pos2 = lang2cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(PositionEchantillon::Class(), "pos2", "LPosition;")));
  return out;
}

template <>
jobject cxx2lang<position_echantillon, jobject>(position_echantillon in)
{
  jobject out = jrt.env->NewObject(PositionEchantillon::Class(), jrt.env->GetMethodID(PositionEchantillon::Class(), "<init>", "()V"));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(PositionEchantillon::Class(), "pos1", "LPosition;"), cxx2lang<position, jobject>(in.pos1));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(PositionEchantillon::Class(), "pos2", "LPosition;"), cxx2lang<position, jobject>(in.pos2));
  return out;
}


/// Action représentée dans l’historique. L’action ``placer_echantillon`` utilise ``pos1`` et ``pos2``. L’action ``transmuter`` utilise ``pos1``. L’action ``catalyser`` utilise ``pos1``, ``id_apprenti`` et ``nouvelle_case``. L’action ``donner_echantillon`` n’est pas représentée dans l’historique, car ``echantillon_tour`` donne l’information.
template <>
action_hist lang2cxx<jobject, action_hist>(jobject in)
{
  action_hist out;
  out.atype = lang2cxx<jobject, action_type>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "atype", "LActionType;")));
  out.pos1 = lang2cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "pos1", "LPosition;")));
  out.pos2 = lang2cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "pos2", "LPosition;")));
  out.id_apprenti = lang2cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(ActionHist::Class(), "id_apprenti", "I")));
  out.nouvelle_case = lang2cxx<jobject, case_type>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "nouvelle_case", "LCaseType;")));
  return out;
}

template <>
jobject cxx2lang<action_hist, jobject>(action_hist in)
{
  jobject out = jrt.env->NewObject(ActionHist::Class(), jrt.env->GetMethodID(ActionHist::Class(), "<init>", "()V"));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "atype", "LActionType;"), cxx2lang<action_type, jobject>(in.atype));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "pos1", "LPosition;"), cxx2lang<position, jobject>(in.pos1));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "pos2", "LPosition;"), cxx2lang<position, jobject>(in.pos2));
  jrt.env->SetIntField(out, jrt.env->GetFieldID(ActionHist::Class(), "id_apprenti", "I"), cxx2lang<int, jint>(in.id_apprenti));
  jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "nouvelle_case", "LCaseType;"), cxx2lang<case_type, jobject>(in.nouvelle_case));
  return out;
}


/// Place l’échantillon du tour sur l’établi, avec les coordonnées de deux cases adjacentes.
jobject placer_echantillon(JNIEnv* _env, jobject _obj, jobject pos1, jobject pos2)
{
  return cxx2lang<erreur, jobject>(api_placer_echantillon(lang2cxx<jobject, position>(pos1), lang2cxx<jobject, position>(pos2)));
}


/// Provoque la transformation chimique de l’élément à la case ciblée, ainsi que tous les éléments adjacents du même type, ceux du même type adjacents à ces derniers, etc. Ils disparaissent alors tous dans leur transmutation en or ou en catalyseur.
jobject transmuter(JNIEnv* _env, jobject _obj, jobject pos)
{
  return cxx2lang<erreur, jobject>(api_transmuter(lang2cxx<jobject, position>(pos)));
}


/// Utilise un catalyseur sur la case ciblée de l'apprenti indiqué. Transforme l’ancien élément en l’élément indiqué.
jobject catalyser(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti, jobject terrain)
{
  return cxx2lang<erreur, jobject>(api_catalyser(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti), lang2cxx<jobject, case_type>(terrain)));
}


/// Définit l’échantillon que l’adversaire recevra à son prochain tour.
jobject donner_echantillon(JNIEnv* _env, jobject _obj, jobject echantillon_donne)
{
  return cxx2lang<erreur, jobject>(api_donner_echantillon(lang2cxx<jobject, echantillon>(echantillon_donne)));
}


/// Renvoie le type d’une case donnée, ou 0 si la case est invaide.
jobject type_case(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti)
{
  return cxx2lang<case_type, jobject>(api_type_case(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti)));
}


/// Indique si une case donnée est vide ou contient un élément. Renvoie faux en cas d'erreur.
jboolean est_vide(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti)
{
  return cxx2lang<boolean, jboolean>(api_est_vide(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie la propriété de l’élément sur une case donnée. Un élément invalide n'a pas de propriété.
jobject propriete_case(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti)
{
  return cxx2lang<element_propriete, jobject>(api_propriete_case(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie la propriété d’un type de case donné.
jobject propriete_case_type(JNIEnv* _env, jobject _obj, jobject ctype)
{
  return cxx2lang<element_propriete, jobject>(api_propriete_case_type(lang2cxx<jobject, case_type>(ctype)));
}


/// Renvoie la taille de la région à laquelle appartient un élément. Renvoie -1 si la position est invalide.
jint taille_region(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti)
{
  return cxx2lang<int, jint>(api_taille_region(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie la liste des positions des cases composant la région à laquelle appartient un élément donné. Renvoie une liste vide en cas d'erreur.
jarray positions_region(JNIEnv* _env, jobject _obj, jobject pos, jint id_apprenti)
{
  return cxx2lang_array<position, Position>(api_positions_region(lang2cxx<jobject, position>(pos), lang2cxx<jint, int>(id_apprenti)));
}


/// Détermine si le placement d’un échantillon est valide.
jboolean placement_possible_echantillon(JNIEnv* _env, jobject _obj, jobject echantillon_a_placer, jobject pos1, jobject pos2, jint id_apprenti)
{
  return cxx2lang<boolean, jboolean>(api_placement_possible_echantillon(lang2cxx<jobject, echantillon>(echantillon_a_placer), lang2cxx<jobject, position>(pos1), lang2cxx<jobject, position>(pos2), lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie la liste des placements possibles pour un échantillon donné sur l’établi d’un apprenti donné. Renvoie une liste vide en cas d'erreur.
jarray placements_possible_echantillon(JNIEnv* _env, jobject _obj, jobject echantillon_a_placer, jint id_apprenti)
{
  return cxx2lang_array<position_echantillon, PositionEchantillon>(api_placements_possible_echantillon(lang2cxx<jobject, echantillon>(echantillon_a_placer), lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie la liste des actions jouées par l’adversaire pendant son tour, dans l’ordre chronologique.
jarray historique(JNIEnv* _env, jobject _obj)
{
  return cxx2lang_array<action_hist, ActionHist>(api_historique());
}


/// Renvoie votre numéro d’apprenti.
jint moi(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<int, jint>(api_moi());
}


/// Renvoie le numéro d’apprenti de votre adversaire.
jint adversaire(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<int, jint>(api_adversaire());
}


/// Renvoie la quantité d’or amassée par l’apprenti désigné par le numéro ``id_apprenti``. Renvoie 0 si ``id_apprenti`` est invalide (attention, le score d’un apprenti valide peut aussi être 0).
jint score(JNIEnv* _env, jobject _obj, jint id_apprenti)
{
  return cxx2lang<int, jint>(api_score(lang2cxx<jint, int>(id_apprenti)));
}


/// Renvoie le numéro du tour actuel.
jint tour_actuel(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<int, jint>(api_tour_actuel());
}


/// Annule la dernière action. Renvoie ``false`` quand il n’y a pas d’action à annuler ce tour-ci.
jboolean annuler(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<boolean, jboolean>(api_annuler());
}


/// Indique le nombre de catalyseurs en votre possession.
jint nombre_catalyseurs(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<int, jint>(api_nombre_catalyseurs());
}


/// Indique l’échantillon reçu pour ce tour.
jobject echantillon_tour(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<echantillon, jobject>(api_echantillon_tour());
}


/// Indique si l’échantillon reçu pour ce tour a déjà été posé.
jboolean a_pose_echantillon(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<boolean, jboolean>(api_a_pose_echantillon());
}


/// Indique si un échantillon a déjà été donné ce tour.
jboolean a_donne_echantillon(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<boolean, jboolean>(api_a_donne_echantillon());
}


/// Renvoie la quantité d’or (et donc le score) obtenue par la transmutation de ``taille_region`` éléments transmutables en or.
jint quantite_transmutation_or(JNIEnv* _env, jobject _obj, jint taille_region)
{
  return cxx2lang<int, jint>(api_quantite_transmutation_or(lang2cxx<jint, int>(taille_region)));
}


/// Renvoie la quantité de catalyseurs obtenue par la transmutation de ``taille_region`` éléments transmutables en catalyseur.
jint quantite_transmutation_catalyseur(JNIEnv* _env, jobject _obj, jint taille_region)
{
  return cxx2lang<int, jint>(api_quantite_transmutation_catalyseur(lang2cxx<jint, int>(taille_region)));
}


/// Renvoie la quantité d’or obtenue par la transmutation de ``taille_region`` éléments transmutables en catalyseur.
jint quantite_transmutation_catalyseur_or(JNIEnv* _env, jobject _obj, jint taille_region)
{
  return cxx2lang<int, jint>(api_quantite_transmutation_catalyseur_or(lang2cxx<jint, int>(taille_region)));
}


/// Indique l’échantillon par défaut lors du premier tour
jobject echantillon_defaut_premier_tour(JNIEnv* _env, jobject _obj)
{
  return cxx2lang<echantillon, jobject>(api_echantillon_defaut_premier_tour());
}


/// Affiche l'état actuel des deux établis dans la console.
void afficher_etablis(JNIEnv* _env, jobject _obj)
{
  api_afficher_etablis();
}


/// Affiche le contenu d'une valeur de type case_type
void afficher_case_type(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_case_type(lang2cxx<jobject, case_type>(v));
}


/// Affiche le contenu d'une valeur de type element_propriete
void afficher_element_propriete(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_element_propriete(lang2cxx<jobject, element_propriete>(v));
}


/// Affiche le contenu d'une valeur de type erreur
void afficher_erreur(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_erreur(lang2cxx<jobject, erreur>(v));
}


/// Affiche le contenu d'une valeur de type action_type
void afficher_action_type(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_action_type(lang2cxx<jobject, action_type>(v));
}


/// Affiche le contenu d'une valeur de type position
void afficher_position(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_position(lang2cxx<jobject, position>(v));
}


/// Affiche le contenu d'une valeur de type echantillon
void afficher_echantillon(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_echantillon(lang2cxx<jobject, echantillon>(v));
}


/// Affiche le contenu d'une valeur de type position_echantillon
void afficher_position_echantillon(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_position_echantillon(lang2cxx<jobject, position_echantillon>(v));
}


/// Affiche le contenu d'une valeur de type action_hist
void afficher_action_hist(JNIEnv* _env, jobject _obj, jobject v)
{
  api_afficher_action_hist(lang2cxx<jobject, action_hist>(v));
}


/// Fonction appelée au début de la partie.
extern "C" void partie_init()
{
  bool attached = jrt.function_enter();
  jmethodID method = jrt.env->GetMethodID(Prologin::Class(), "partie_init", "()V");
  jrt.env->CallVoidMethod(jrt.prologin, method);
  if (jrt.env->ExceptionOccurred())
  {
    jrt.env->ExceptionDescribe();
    exit(1);
  }
  jrt.function_exit(attached);
}


/// Fonction appelée à chaque tour.
extern "C" void jouer_tour()
{
  bool attached = jrt.function_enter();
  jmethodID method = jrt.env->GetMethodID(Prologin::Class(), "jouer_tour", "()V");
  jrt.env->CallVoidMethod(jrt.prologin, method);
  if (jrt.env->ExceptionOccurred())
  {
    jrt.env->ExceptionDescribe();
    exit(1);
  }
  jrt.function_exit(attached);
}


/// Fonction appelée à la fin de la partie.
extern "C" void partie_fin()
{
  bool attached = jrt.function_enter();
  jmethodID method = jrt.env->GetMethodID(Prologin::Class(), "partie_fin", "()V");
  jrt.env->CallVoidMethod(jrt.prologin, method);
  if (jrt.env->ExceptionOccurred())
  {
    jrt.env->ExceptionDescribe();
    exit(1);
  }
  jrt.function_exit(attached);
}


jclass Prologin::Class()
{
  return jrt.env->FindClass("Prologin");
}

jclass CaseType::Class()
{
  return jrt.env->FindClass("CaseType");
}

jclass ElementPropriete::Class()
{
  return jrt.env->FindClass("ElementPropriete");
}

jclass Erreur::Class()
{
  return jrt.env->FindClass("Erreur");
}

jclass ActionType::Class()
{
  return jrt.env->FindClass("ActionType");
}

jclass Position::Class()
{
  return jrt.env->FindClass("Position");
}

jclass Echantillon::Class()
{
  return jrt.env->FindClass("Echantillon");
}

jclass PositionEchantillon::Class()
{
  return jrt.env->FindClass("PositionEchantillon");
}

jclass ActionHist::Class()
{
  return jrt.env->FindClass("ActionHist");
}

static void _register_native_methods(JNIEnv* env)
{
  JNINativeMethod methods[] = {
    {(char*)"placer_echantillon", (char*)"(LPosition;LPosition;)LErreur;", (void*)&placer_echantillon},
    {(char*)"transmuter", (char*)"(LPosition;)LErreur;", (void*)&transmuter},
    {(char*)"catalyser", (char*)"(LPosition;ILCaseType;)LErreur;", (void*)&catalyser},
    {(char*)"donner_echantillon", (char*)"(LEchantillon;)LErreur;", (void*)&donner_echantillon},
    {(char*)"type_case", (char*)"(LPosition;I)LCaseType;", (void*)&type_case},
    {(char*)"est_vide", (char*)"(LPosition;I)Z", (void*)&est_vide},
    {(char*)"propriete_case", (char*)"(LPosition;I)LElementPropriete;", (void*)&propriete_case},
    {(char*)"propriete_case_type", (char*)"(LCaseType;)LElementPropriete;", (void*)&propriete_case_type},
    {(char*)"taille_region", (char*)"(LPosition;I)I", (void*)&taille_region},
    {(char*)"positions_region", (char*)"(LPosition;I)[LPosition;", (void*)&positions_region},
    {(char*)"placement_possible_echantillon", (char*)"(LEchantillon;LPosition;LPosition;I)Z", (void*)&placement_possible_echantillon},
    {(char*)"placements_possible_echantillon", (char*)"(LEchantillon;I)[LPositionEchantillon;", (void*)&placements_possible_echantillon},
    {(char*)"historique", (char*)"()[LActionHist;", (void*)&historique},
    {(char*)"moi", (char*)"()I", (void*)&moi},
    {(char*)"adversaire", (char*)"()I", (void*)&adversaire},
    {(char*)"score", (char*)"(I)I", (void*)&score},
    {(char*)"tour_actuel", (char*)"()I", (void*)&tour_actuel},
    {(char*)"annuler", (char*)"()Z", (void*)&annuler},
    {(char*)"nombre_catalyseurs", (char*)"()I", (void*)&nombre_catalyseurs},
    {(char*)"echantillon_tour", (char*)"()LEchantillon;", (void*)&echantillon_tour},
    {(char*)"a_pose_echantillon", (char*)"()Z", (void*)&a_pose_echantillon},
    {(char*)"a_donne_echantillon", (char*)"()Z", (void*)&a_donne_echantillon},
    {(char*)"quantite_transmutation_or", (char*)"(I)I", (void*)&quantite_transmutation_or},
    {(char*)"quantite_transmutation_catalyseur", (char*)"(I)I", (void*)&quantite_transmutation_catalyseur},
    {(char*)"quantite_transmutation_catalyseur_or", (char*)"(I)I", (void*)&quantite_transmutation_catalyseur_or},
    {(char*)"echantillon_defaut_premier_tour", (char*)"()LEchantillon;", (void*)&echantillon_defaut_premier_tour},
    {(char*)"afficher_etablis", (char*)"()V", (void*)&afficher_etablis},
    {(char*)"afficher_case_type", (char*)"(LCaseType;)V", (void*)&afficher_case_type},
    {(char*)"afficher_element_propriete", (char*)"(LElementPropriete;)V", (void*)&afficher_element_propriete},
    {(char*)"afficher_erreur", (char*)"(LErreur;)V", (void*)&afficher_erreur},
    {(char*)"afficher_action_type", (char*)"(LActionType;)V", (void*)&afficher_action_type},
    {(char*)"afficher_position", (char*)"(LPosition;)V", (void*)&afficher_position},
    {(char*)"afficher_echantillon", (char*)"(LEchantillon;)V", (void*)&afficher_echantillon},
    {(char*)"afficher_position_echantillon", (char*)"(LPositionEchantillon;)V", (void*)&afficher_position_echantillon},
    {(char*)"afficher_action_hist", (char*)"(LActionHist;)V", (void*)&afficher_action_hist}
  };
  env->RegisterNatives(Prologin::Class(), methods, sizeof(methods)/sizeof(methods[0]));
}

ProloginJavaRunTime jrt;

ProloginJavaRunTime::ProloginJavaRunTime()
{
 std::string classpath = "-Djava.class.path=";
  char* champion_path = getenv("CHAMPION_PATH");
  if (champion_path == NULL)
    champion_path = (char*)"./";
  classpath.append(champion_path);

  JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
  JavaVMOption options[4];
  options[0].optionString = (char*) classpath.c_str();
  options[1].optionString = (char*) "-ea";
  options[2].optionString = (char*) "-XX:MaxHeapSize=512m";
  options[3].optionString = (char*) "-XX:CompressedClassSpaceSize=64m";
  vm_args.version = JNI_VERSION_1_6;
  vm_args.nOptions = 4;
  vm_args.options = options;
  vm_args.ignoreUnrecognized = false;
  JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
  prologin = env->NewObject(Prologin::Class(), env->GetMethodID(Prologin::Class(), "<init>", "()V"));
  _register_native_methods(env);
}

ProloginJavaRunTime::~ProloginJavaRunTime()
{
  jvm->DestroyJavaVM();
}

bool ProloginJavaRunTime::function_enter()
{
  if (jvm->GetEnv((void**)&env, JNI_VERSION_1_6) == JNI_OK)
    return false;
  jvm->AttachCurrentThread((void**)&env, NULL);
  return true;
}

void ProloginJavaRunTime::function_exit(bool attached)
{
  if (attached)
    jvm->DetachCurrentThread();
}

