// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2005-2020 Association Prologin <association@prologin.org>

// This file contains the code to call the API functions from the Java language.
// This file was generated by stechec2-generator. DO NOT EDIT.

#include <cstdlib>
#include <string>
#include <vector>

#include <jni.h>

struct Champion
{
    static jclass Class();
};

/// Erreurs possibles après avoir effectué une action
typedef enum erreur
{
    OK, ///< L'action a été effectuée avec succès
    TROUPE_INVALIDE, ///< Mauvais identifiant de troupe
    HORS_TOUR, ///< Aucune action n'est possible hors de joueur_tour
    MOUVEMENTS_INSUFFISANTS, ///< Il ne reste plus assez de points de mouvements pour effectuer l'action demandée
    TROP_GRANDI, ///< La troupe a déjà trop grandi pendant le tour
    TROP_CREUSE, ///< Trop de trous ont déjà été creusés pendant le tour
    NON_CREUSABLE, ///< Il n'est pas possible de creuser à la position demandée
    NON_CONSTRUCTIBLE, ///< La zone demandée n'est pas constructible
    SCORE_INSUFFISANT, ///< Le joueur n'a pas assez de points pour construire un buisson
    POSITION_INVALIDE, ///< La position demandée est hors du parc
    DIRECTION_INVALIDE, ///< La direction spécifiée n'existe pas.
    PIGEON_INVALIDE, ///< Le pigeon spécifié n'existe pas.
} erreur;

struct Erreur
{
    static jclass Class();
};

/// Directions possibles
typedef enum direction
{
    NORD, ///< Sens positif pour les lignes
    SUD, ///< Sens négatif pour les lignes
    EST, ///< Sens positif pour les colonnes
    OUEST, ///< Sens négatif pour les colonnes
    HAUT, ///< Sens positif pour le niveau
    BAS, ///< Sens négatif pour le niveau
} direction;

struct Direction
{
    static jclass Class();
};

/// Type de l'élément présent sur une case
typedef enum type_case
{
    GAZON, ///< Absence d'élément
    BUISSON, ///< Obstacle impossible à traverser
    BARRIERE, ///< Élément pouvant être ouvert ou fermé. Une barrière fermée est infranchissable alors qu'une barrière ouverte est analogue à une case vide
    NID, ///< Élément traversable permettant à la troupe de déposer son inventaire en échange de points
    PAPY, ///< Élément traversable générant de manière périodique des miches de pain
    TROU, ///< Interface entre le niveau principal est le niveau souterrain
    TUNNEL, ///< Bloc du souterrain ayant été creusé
    TERRE, ///< Bloc du souterrain n'ayant pas encore été creusé
} type_case;

struct TypeCase
{
    static jclass Class();
};

/// État d'une barrière, soit ouvert, soit fermé, soit non-applicable
typedef enum etat_barriere
{
    OUVERTE, ///< La barrière est ouverte
    FERMEE, ///< La barrière est fermée
    PAS_DE_BARRIERE, ///< L'élément dont on requiert l'état n'est pas une barrière
} etat_barriere;

struct EtatBarriere
{
    static jclass Class();
};

/// Joueur auquel appartient un nid
typedef enum etat_nid
{
    LIBRE, ///< Le nid n'a pas été attribué
    JOUEUR_0, ///< Joueur 0
    JOUEUR_1, ///< Joueur 1
    PAS_DE_NID, ///< L'élément dont on requiert l'état n'est pas un nid
} etat_nid;

struct EtatNid
{
    static jclass Class();
};

/// Type de pigeon de debug
typedef enum pigeon_debug
{
    PAS_DE_PIGEON, ///< Aucun pigeon, enlève le pigeon présent
    PIGEON_BLEU, ///< Pigeon bleu
    PIGEON_JAUNE, ///< Pigeon jaune
    PIGEON_ROUGE, ///< Pigeon rouge
} pigeon_debug;

struct PigeonDebug
{
    static jclass Class();
};

/// Types d'actions
typedef enum type_action
{
    ACTION_AVANCER, ///< Action ``avancer``
    ACTION_GRANDIR, ///< Action ``grandir``
    ACTION_CONSTRUIRE, ///< Action ``construire buisson``
    ACTION_CREUSER, ///< Action ``creuser tunnel``
} type_action;

struct TypeAction
{
    static jclass Class();
};

/// Position dans la carte, donnée par trois coordonnées
typedef struct position
{
    int colonne; ///< Abscisse
    int ligne; ///< Ordonnée
    int niveau; ///< Niveau
} position;

struct Position
{
    static jclass Class();
};

/// Une troupe, composée de la maman canard et de ses canetons
typedef struct troupe
{
    position maman; ///< Position de la maman canard
    std::vector<position> canards; ///< Position des différents canards de la troupe, incluant la maman en première position
    int taille; ///< Taille de la troupe
    direction dir; ///< Direction de la troupe
    int inventaire; ///< Nombre de pains de la troupe
    int pts_action; ///< Nombre de points d'action de la troupe
    int id; ///< Identifiant de la troupe
} troupe;

struct Troupe
{
    static jclass Class();
};

/// Élément constituant le parc
typedef struct etat_case
{
    position pos; ///< Position de la case. Le niveau vaut nécessairement 0
    type_case contenu; ///< Type de la case
    bool est_constructible; ///< La case est constructible
    int nb_pains; ///< Nombre de pains contenus sur la case
} etat_case;

struct EtatCase
{
    static jclass Class();
};

/// Action représentée dans l'historique
typedef struct action_hist
{
    type_action action_type; ///< Type de l'action
    int troupe_id; ///< Identifiant de la troupe
    direction action_dir; ///< Direction de l'action
    position action_pos; ///< Position de l'action
} action_hist;

struct ActionHist
{
    static jclass Class();
};

struct StechecJavaRunTime
{
    StechecJavaRunTime() = default;
    ~StechecJavaRunTime();
    void init();
    bool function_enter();
    void function_exit(bool attached);

    JavaVM* jvm;
    JNIEnv* env;
    jobject prologin;

private:
    bool is_init_ = false;
};
static struct StechecJavaRunTime jrt;

extern "C" erreur api_avancer(int id, direction dir);
extern "C" erreur api_grandir(int id);
extern "C" erreur api_construire_buisson(position pos);
extern "C" erreur api_creuser_tunnel(position pos);
extern "C" etat_case api_info_case(position pos);
extern "C" etat_barriere api_info_barriere(position pos);
extern "C" etat_nid api_info_nid(position pos);
extern "C" int api_papy_tours_restants(position pos);
extern "C" std::vector<troupe> api_troupes_joueur(int id_joueur);
extern "C" std::vector<position> api_pains();
extern "C" erreur api_debug_poser_pigeon(position pos, pigeon_debug pigeon);
extern "C" std::vector<action_hist> api_historique();
extern "C" int api_gain(int nb_pains);
extern "C" int api_inventaire(int taille);
extern "C" std::vector<direction> api_trouver_chemin(position depart, position arrivee);
extern "C" int api_moi();
extern "C" int api_adversaire();
extern "C" int api_score(int id_joueur);
extern "C" bool api_annuler();
extern "C" int api_tour_actuel();
extern "C" void api_afficher_erreur(erreur v);
extern "C" void api_afficher_direction(direction v);
extern "C" void api_afficher_type_case(type_case v);
extern "C" void api_afficher_etat_barriere(etat_barriere v);
extern "C" void api_afficher_etat_nid(etat_nid v);
extern "C" void api_afficher_pigeon_debug(pigeon_debug v);
extern "C" void api_afficher_type_action(type_action v);
extern "C" void api_afficher_position(position v);
extern "C" void api_afficher_troupe(troupe v);
extern "C" void api_afficher_etat_case(etat_case v);
extern "C" void api_afficher_action_hist(action_hist v);


// In case of errors...
template <typename Lang, typename CxxType>
CxxType java_to_cxx(Lang in)
{
    return in.error_should_not_happen;
}

template <typename CxxType, typename Lang>
Lang cxx_to_java(CxxType in)
{
    return in.error_should_not_happen;
}

template<typename Lang, typename CxxType>
std::vector<CxxType> java_to_cxx_array(jobject in);

template<typename CxxType, typename Lang>
jarray cxx_to_java_array(std::vector<CxxType> in);

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

template <>
bool java_to_cxx<jboolean, bool>(jboolean in)
{
    return (bool)in;
}

template <>
jarray cxx_to_java_array<bool, jboolean>(std::vector<bool> in)
{
    jbooleanArray out = jrt.env->NewBooleanArray((jsize)in.size());
    // std::vector<bool>::data() does not return a bool*, because the data is
    // packed, so we need to create a new bool array
    jboolean* in_cast = new jboolean[in.size()];
    std::copy(std::begin(in), std::end(in), in_cast);
    jrt.env->SetBooleanArrayRegion(out, (jsize)0, (jsize)in.size(), in_cast);
    delete[] in_cast;
    return (jarray)out;
}

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

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

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

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

template <>
std::vector<int> java_to_cxx_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 <>
jdouble cxx_to_java<double, jdouble>(double in)
{
    return (jdouble)in;
}

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

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

template <>
std::vector<double> java_to_cxx_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 cxx_to_java<std::string, jstring>(std::string in)
{
    return jrt.env->NewStringUTF(in.data());
}

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

template <>
jarray cxx_to_java_array<std::string, jstring>(std::vector<std::string> in)
{
    jobjectArray out = jrt.env->NewObjectArray(
            (jsize)in.size(), jrt.env->FindClass("java/lang/String"), NULL);
    for (size_t i = 0; i < in.size(); i++)
        jrt.env->SetObjectArrayElement(out, (jsize)i, cxx_to_java<std::string, jstring>(in[i]));
    return out;
}

template <>
std::vector<std::string> java_to_cxx_array<jstring, std::string>(jobject in)
{
    jobjectArray array = (jobjectArray)in;
    size_t size = (size_t)jrt.env->GetArrayLength(array);
    std::vector<std::string> out;
    for (size_t i = 0; i < size; i++)
        out.push_back(java_to_cxx<jstring, std::string>(
            (jstring)jrt.env->GetObjectArrayElement(array, (jsize)i)));
    return out;
}

// Object array wrappers (assume Lang::class exists)
template <typename CxxType, typename Lang>
jarray cxx_to_java_array(std::vector<CxxType> 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, cxx_to_java<CxxType, jobject>(in[i]));
    return out;
}

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

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

template<>
jobject cxx_to_java<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);
}

template<>
direction java_to_cxx<jobject, direction>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(Direction::Class(), "ordinal", "()I");
    return direction(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<direction, jobject>(direction in)
{
    jmethodID method = jrt.env->GetStaticMethodID(Direction::Class(), "values", "()[LDirection;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(Direction::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template<>
type_case java_to_cxx<jobject, type_case>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(TypeCase::Class(), "ordinal", "()I");
    return type_case(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<type_case, jobject>(type_case in)
{
    jmethodID method = jrt.env->GetStaticMethodID(TypeCase::Class(), "values", "()[LTypeCase;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(TypeCase::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template<>
etat_barriere java_to_cxx<jobject, etat_barriere>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(EtatBarriere::Class(), "ordinal", "()I");
    return etat_barriere(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<etat_barriere, jobject>(etat_barriere in)
{
    jmethodID method = jrt.env->GetStaticMethodID(EtatBarriere::Class(), "values", "()[LEtatBarriere;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(EtatBarriere::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template<>
etat_nid java_to_cxx<jobject, etat_nid>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(EtatNid::Class(), "ordinal", "()I");
    return etat_nid(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<etat_nid, jobject>(etat_nid in)
{
    jmethodID method = jrt.env->GetStaticMethodID(EtatNid::Class(), "values", "()[LEtatNid;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(EtatNid::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template<>
pigeon_debug java_to_cxx<jobject, pigeon_debug>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(PigeonDebug::Class(), "ordinal", "()I");
    return pigeon_debug(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<pigeon_debug, jobject>(pigeon_debug in)
{
    jmethodID method = jrt.env->GetStaticMethodID(PigeonDebug::Class(), "values", "()[LPigeonDebug;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(PigeonDebug::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template<>
type_action java_to_cxx<jobject, type_action>(jobject in)
{
    jmethodID ordinal = jrt.env->GetMethodID(TypeAction::Class(), "ordinal", "()I");
    return type_action(java_to_cxx<jint, int>(jrt.env->CallIntMethod(in, ordinal)));
}

template<>
jobject cxx_to_java<type_action, jobject>(type_action in)
{
    jmethodID method = jrt.env->GetStaticMethodID(TypeAction::Class(), "values", "()[LTypeAction;");
    jobjectArray values = (jobjectArray)jrt.env->CallStaticObjectMethod(TypeAction::Class(), method);
    return jrt.env->GetObjectArrayElement(values, (jsize)in);
}

template <>
position java_to_cxx<jobject, position>(jobject in)
{
    position out;
    out.colonne = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Position::Class(), "colonne", "I")));
    out.ligne = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Position::Class(), "ligne", "I")));
    out.niveau = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Position::Class(), "niveau", "I")));
    return out;
}

template <>
jobject cxx_to_java<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(), "colonne", "I"), cxx_to_java<int, jint>(in.colonne));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Position::Class(), "ligne", "I"), cxx_to_java<int, jint>(in.ligne));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Position::Class(), "niveau", "I"), cxx_to_java<int, jint>(in.niveau));
    return out;
}

template <>
troupe java_to_cxx<jobject, troupe>(jobject in)
{
    troupe out;
    out.maman = java_to_cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(Troupe::Class(), "maman", "LPosition;")));
    out.canards = java_to_cxx_array<Position, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(Troupe::Class(), "canards", "[LPosition;")));
    out.taille = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Troupe::Class(), "taille", "I")));
    out.dir = java_to_cxx<jobject, direction>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(Troupe::Class(), "dir", "LDirection;")));
    out.inventaire = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Troupe::Class(), "inventaire", "I")));
    out.pts_action = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Troupe::Class(), "pts_action", "I")));
    out.id = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(Troupe::Class(), "id", "I")));
    return out;
}

template <>
jobject cxx_to_java<troupe, jobject>(troupe in)
{
    jobject out = jrt.env->NewObject(Troupe::Class(), jrt.env->GetMethodID(Troupe::Class(), "<init>", "()V"));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(Troupe::Class(), "maman", "LPosition;"), cxx_to_java<position, jobject>(in.maman));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(Troupe::Class(), "canards", "[LPosition;"), cxx_to_java_array<position, Position>(in.canards));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Troupe::Class(), "taille", "I"), cxx_to_java<int, jint>(in.taille));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(Troupe::Class(), "dir", "LDirection;"), cxx_to_java<direction, jobject>(in.dir));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Troupe::Class(), "inventaire", "I"), cxx_to_java<int, jint>(in.inventaire));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Troupe::Class(), "pts_action", "I"), cxx_to_java<int, jint>(in.pts_action));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(Troupe::Class(), "id", "I"), cxx_to_java<int, jint>(in.id));
    return out;
}

template <>
etat_case java_to_cxx<jobject, etat_case>(jobject in)
{
    etat_case out;
    out.pos = java_to_cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(EtatCase::Class(), "pos", "LPosition;")));
    out.contenu = java_to_cxx<jobject, type_case>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(EtatCase::Class(), "contenu", "LTypeCase;")));
    out.est_constructible = java_to_cxx<jboolean, bool>(jrt.env->GetBooleanField(in, jrt.env->GetFieldID(EtatCase::Class(), "est_constructible", "Z")));
    out.nb_pains = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(EtatCase::Class(), "nb_pains", "I")));
    return out;
}

template <>
jobject cxx_to_java<etat_case, jobject>(etat_case in)
{
    jobject out = jrt.env->NewObject(EtatCase::Class(), jrt.env->GetMethodID(EtatCase::Class(), "<init>", "()V"));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(EtatCase::Class(), "pos", "LPosition;"), cxx_to_java<position, jobject>(in.pos));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(EtatCase::Class(), "contenu", "LTypeCase;"), cxx_to_java<type_case, jobject>(in.contenu));
    jrt.env->SetBooleanField(out, jrt.env->GetFieldID(EtatCase::Class(), "est_constructible", "Z"), cxx_to_java<bool, jboolean>(in.est_constructible));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(EtatCase::Class(), "nb_pains", "I"), cxx_to_java<int, jint>(in.nb_pains));
    return out;
}

template <>
action_hist java_to_cxx<jobject, action_hist>(jobject in)
{
    action_hist out;
    out.action_type = java_to_cxx<jobject, type_action>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "action_type", "LTypeAction;")));
    out.troupe_id = java_to_cxx<jint, int>(jrt.env->GetIntField(in, jrt.env->GetFieldID(ActionHist::Class(), "troupe_id", "I")));
    out.action_dir = java_to_cxx<jobject, direction>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "action_dir", "LDirection;")));
    out.action_pos = java_to_cxx<jobject, position>(jrt.env->GetObjectField(in, jrt.env->GetFieldID(ActionHist::Class(), "action_pos", "LPosition;")));
    return out;
}

template <>
jobject cxx_to_java<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(), "action_type", "LTypeAction;"), cxx_to_java<type_action, jobject>(in.action_type));
    jrt.env->SetIntField(out, jrt.env->GetFieldID(ActionHist::Class(), "troupe_id", "I"), cxx_to_java<int, jint>(in.troupe_id));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "action_dir", "LDirection;"), cxx_to_java<direction, jobject>(in.action_dir));
    jrt.env->SetObjectField(out, jrt.env->GetFieldID(ActionHist::Class(), "action_pos", "LPosition;"), cxx_to_java<position, jobject>(in.action_pos));
    return out;
}

jobject avancer(JNIEnv*, jobject, jint id, jobject dir)
{
    return cxx_to_java<erreur, jobject>(api_avancer(java_to_cxx<jint, int>(id), java_to_cxx<jobject, direction>(dir)));
}

jobject grandir(JNIEnv*, jobject, jint id)
{
    return cxx_to_java<erreur, jobject>(api_grandir(java_to_cxx<jint, int>(id)));
}

jobject construire_buisson(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<erreur, jobject>(api_construire_buisson(java_to_cxx<jobject, position>(pos)));
}

jobject creuser_tunnel(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<erreur, jobject>(api_creuser_tunnel(java_to_cxx<jobject, position>(pos)));
}

jobject info_case(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<etat_case, jobject>(api_info_case(java_to_cxx<jobject, position>(pos)));
}

jobject info_barriere(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<etat_barriere, jobject>(api_info_barriere(java_to_cxx<jobject, position>(pos)));
}

jobject info_nid(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<etat_nid, jobject>(api_info_nid(java_to_cxx<jobject, position>(pos)));
}

jint papy_tours_restants(JNIEnv*, jobject, jobject pos)
{
    return cxx_to_java<int, jint>(api_papy_tours_restants(java_to_cxx<jobject, position>(pos)));
}

jarray troupes_joueur(JNIEnv*, jobject, jint id_joueur)
{
    return cxx_to_java_array<troupe, Troupe>(api_troupes_joueur(java_to_cxx<jint, int>(id_joueur)));
}

jarray pains(JNIEnv*, jobject)
{
    return cxx_to_java_array<position, Position>(api_pains());
}

jobject debug_poser_pigeon(JNIEnv*, jobject, jobject pos, jobject pigeon)
{
    return cxx_to_java<erreur, jobject>(api_debug_poser_pigeon(java_to_cxx<jobject, position>(pos), java_to_cxx<jobject, pigeon_debug>(pigeon)));
}

jarray historique(JNIEnv*, jobject)
{
    return cxx_to_java_array<action_hist, ActionHist>(api_historique());
}

jint gain(JNIEnv*, jobject, jint nb_pains)
{
    return cxx_to_java<int, jint>(api_gain(java_to_cxx<jint, int>(nb_pains)));
}

jint inventaire(JNIEnv*, jobject, jint taille)
{
    return cxx_to_java<int, jint>(api_inventaire(java_to_cxx<jint, int>(taille)));
}

jarray trouver_chemin(JNIEnv*, jobject, jobject depart, jobject arrivee)
{
    return cxx_to_java_array<direction, Direction>(api_trouver_chemin(java_to_cxx<jobject, position>(depart), java_to_cxx<jobject, position>(arrivee)));
}

jint moi(JNIEnv*, jobject)
{
    return cxx_to_java<int, jint>(api_moi());
}

jint adversaire(JNIEnv*, jobject)
{
    return cxx_to_java<int, jint>(api_adversaire());
}

jint score(JNIEnv*, jobject, jint id_joueur)
{
    return cxx_to_java<int, jint>(api_score(java_to_cxx<jint, int>(id_joueur)));
}

jboolean annuler(JNIEnv*, jobject)
{
    return cxx_to_java<bool, jboolean>(api_annuler());
}

jint tour_actuel(JNIEnv*, jobject)
{
    return cxx_to_java<int, jint>(api_tour_actuel());
}

void afficher_erreur(JNIEnv*, jobject, jobject v)
{
    (api_afficher_erreur(java_to_cxx<jobject, erreur>(v)));
}

void afficher_direction(JNIEnv*, jobject, jobject v)
{
    (api_afficher_direction(java_to_cxx<jobject, direction>(v)));
}

void afficher_type_case(JNIEnv*, jobject, jobject v)
{
    (api_afficher_type_case(java_to_cxx<jobject, type_case>(v)));
}

void afficher_etat_barriere(JNIEnv*, jobject, jobject v)
{
    (api_afficher_etat_barriere(java_to_cxx<jobject, etat_barriere>(v)));
}

void afficher_etat_nid(JNIEnv*, jobject, jobject v)
{
    (api_afficher_etat_nid(java_to_cxx<jobject, etat_nid>(v)));
}

void afficher_pigeon_debug(JNIEnv*, jobject, jobject v)
{
    (api_afficher_pigeon_debug(java_to_cxx<jobject, pigeon_debug>(v)));
}

void afficher_type_action(JNIEnv*, jobject, jobject v)
{
    (api_afficher_type_action(java_to_cxx<jobject, type_action>(v)));
}

void afficher_position(JNIEnv*, jobject, jobject v)
{
    (api_afficher_position(java_to_cxx<jobject, position>(v)));
}

void afficher_troupe(JNIEnv*, jobject, jobject v)
{
    (api_afficher_troupe(java_to_cxx<jobject, troupe>(v)));
}

void afficher_etat_case(JNIEnv*, jobject, jobject v)
{
    (api_afficher_etat_case(java_to_cxx<jobject, etat_case>(v)));
}

void afficher_action_hist(JNIEnv*, jobject, jobject v)
{
    (api_afficher_action_hist(java_to_cxx<jobject, action_hist>(v)));
}

extern "C" void partie_init()
{
    jrt.init();
    bool attached = jrt.function_enter();
    jmethodID method = jrt.env->GetMethodID(Champion::Class(), "partie_init", "()V");
jrt.env->CallVoidMethod(jrt.prologin, method);
    if (jrt.env->ExceptionOccurred())
    {
        jrt.env->ExceptionDescribe();
        exit(1);
    }
    jrt.function_exit(attached);
}

extern "C" void jouer_tour()
{
    jrt.init();
    bool attached = jrt.function_enter();
    jmethodID method = jrt.env->GetMethodID(Champion::Class(), "jouer_tour", "()V");
jrt.env->CallVoidMethod(jrt.prologin, method);
    if (jrt.env->ExceptionOccurred())
    {
        jrt.env->ExceptionDescribe();
        exit(1);
    }
    jrt.function_exit(attached);
}

extern "C" void partie_fin()
{
    jrt.init();
    bool attached = jrt.function_enter();
    jmethodID method = jrt.env->GetMethodID(Champion::Class(), "partie_fin", "()V");
jrt.env->CallVoidMethod(jrt.prologin, method);
    if (jrt.env->ExceptionOccurred())
    {
        jrt.env->ExceptionDescribe();
        exit(1);
    }
    jrt.function_exit(attached);
}

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

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

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

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

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

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

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

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

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

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

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

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


static void _register_native_methods(JNIEnv* env)
{
    JNINativeMethod methods[] = {
        {(char*)"avancer", (char*)"(ILDirection;)LErreur;", (void*)&avancer},
        {(char*)"grandir", (char*)"(I)LErreur;", (void*)&grandir},
        {(char*)"construire_buisson", (char*)"(LPosition;)LErreur;", (void*)&construire_buisson},
        {(char*)"creuser_tunnel", (char*)"(LPosition;)LErreur;", (void*)&creuser_tunnel},
        {(char*)"info_case", (char*)"(LPosition;)LEtatCase;", (void*)&info_case},
        {(char*)"info_barriere", (char*)"(LPosition;)LEtatBarriere;", (void*)&info_barriere},
        {(char*)"info_nid", (char*)"(LPosition;)LEtatNid;", (void*)&info_nid},
        {(char*)"papy_tours_restants", (char*)"(LPosition;)I", (void*)&papy_tours_restants},
        {(char*)"troupes_joueur", (char*)"(I)[LTroupe;", (void*)&troupes_joueur},
        {(char*)"pains", (char*)"()[LPosition;", (void*)&pains},
        {(char*)"debug_poser_pigeon", (char*)"(LPosition;LPigeonDebug;)LErreur;", (void*)&debug_poser_pigeon},
        {(char*)"historique", (char*)"()[LActionHist;", (void*)&historique},
        {(char*)"gain", (char*)"(I)I", (void*)&gain},
        {(char*)"inventaire", (char*)"(I)I", (void*)&inventaire},
        {(char*)"trouver_chemin", (char*)"(LPosition;LPosition;)[LDirection;", (void*)&trouver_chemin},
        {(char*)"moi", (char*)"()I", (void*)&moi},
        {(char*)"adversaire", (char*)"()I", (void*)&adversaire},
        {(char*)"score", (char*)"(I)I", (void*)&score},
        {(char*)"annuler", (char*)"()Z", (void*)&annuler},
        {(char*)"tour_actuel", (char*)"()I", (void*)&tour_actuel},
        {(char*)"afficher_erreur", (char*)"(LErreur;)V", (void*)&afficher_erreur},
        {(char*)"afficher_direction", (char*)"(LDirection;)V", (void*)&afficher_direction},
        {(char*)"afficher_type_case", (char*)"(LTypeCase;)V", (void*)&afficher_type_case},
        {(char*)"afficher_etat_barriere", (char*)"(LEtatBarriere;)V", (void*)&afficher_etat_barriere},
        {(char*)"afficher_etat_nid", (char*)"(LEtatNid;)V", (void*)&afficher_etat_nid},
        {(char*)"afficher_pigeon_debug", (char*)"(LPigeonDebug;)V", (void*)&afficher_pigeon_debug},
        {(char*)"afficher_type_action", (char*)"(LTypeAction;)V", (void*)&afficher_type_action},
        {(char*)"afficher_position", (char*)"(LPosition;)V", (void*)&afficher_position},
        {(char*)"afficher_troupe", (char*)"(LTroupe;)V", (void*)&afficher_troupe},
        {(char*)"afficher_etat_case", (char*)"(LEtatCase;)V", (void*)&afficher_etat_case},
        {(char*)"afficher_action_hist", (char*)"(LActionHist;)V", (void*)&afficher_action_hist},
    };
    env->RegisterNatives(Champion::Class(), methods, sizeof(methods)/sizeof(methods[0]));
}

StechecJavaRunTime::~StechecJavaRunTime()
{
}

void StechecJavaRunTime::init()
{
    if (is_init_)
        return;

    is_init_ = true;

    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(Champion::Class(), env->GetMethodID(Champion::Class(), "<init>", "()V"));
    _register_native_methods(env);
}

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

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