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

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

#include <assert.h>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <type_traits>
#include <vector>
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/threads.h>

typedef int32_t gint32;

class CSharpInterface
{
    public:
        CSharpInterface();
        ~CSharpInterface();
        MonoObject* callCSharpMethod(const char* name);
        MonoImage* getImage();
        MonoDomain* getDomain();

    private:

        MonoDomain* _domain;
        MonoAssembly* _assembly;
        MonoImage* _image;
        MonoClass* _class;
        MonoObject* _object;
};


/// 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;

/// 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;

/// 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;

/// É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;

/// 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;

/// 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;

/// 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;

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

/// 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;

/// É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;

/// 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;

extern "C" {

/// La troupe avance d'une case vers une direction donnée
erreur api_avancer(int id, direction dir);

/// La troupe grandit
erreur api_grandir(int id);

/// Construit un buisson à la position donnée
erreur api_construire_buisson(position pos);

/// Creuse un tunnel à la position donnée
erreur api_creuser_tunnel(position pos);

/// Renvoie les informations concernant une case
etat_case api_info_case(position pos);

/// Renvoie les informations d'état d'une barrière
etat_barriere api_info_barriere(position pos);

/// Renvoie les informations d'état d'un nid
etat_nid api_info_nid(position pos);

/// Renvoie le nombre de tours restants avant qu'un papy dépose une miche de
/// pain. Retourne -1 si aucun papy ne se trouve à la position demandée
int api_papy_tours_restants(position pos);

/// Renvoie les troupes d'un joueur. Si le joueur est invalide, tous les champs
/// valent -1.
std::vector<troupe> api_troupes_joueur(int id_joueur);

/// Renvoie la position des pains récupérables
std::vector<position> api_pains();

/// Pose un pigeon de debug sur la case indiquée
erreur api_debug_poser_pigeon(position pos, pigeon_debug pigeon);

/// Renvoie la liste des actions effectuées par l'adversaire durant son tour,
/// dans l'ordre chronologique. Les actions de débug n'apparaissent pas dans
/// cette liste.
std::vector<action_hist> api_historique();

/// Renvoie le gain en score que le nombre de pains passé en entrée
/// rapporterait s'ils étaient tous déposés d'un coup dans un nid
int api_gain(int nb_pains);

/// Renvoie la taille de l'inventaire d'une troupe de taille donnée
int api_inventaire(int taille);

/// Trouve un plus court chemin ouvert entre deux positions. Renvoie une liste
/// vide si les deux positions sont égales ou si aucun chemin n'existe.
std::vector<direction> api_trouver_chemin(position depart, position arrivee);

/// Renvoie votre numéro de joueur.
int api_moi();

/// Renvoie le numéro du joueur adverse.
int api_adversaire();

/// Renvoie le score du joueur `id_joueur`. Renvoie -1 si le joueur est
/// invalide.
int api_score(int id_joueur);

/// Annule la dernière action. Renvoie faux quand il n'y a pas d'action à
/// annuler ce tour-ci
bool api_annuler();

/// Retourne le numéro du tour actuel.
int api_tour_actuel();

/// Affiche le contenu d'une valeur de type erreur
void api_afficher_erreur(erreur v);

/// Affiche le contenu d'une valeur de type direction
void api_afficher_direction(direction v);

/// Affiche le contenu d'une valeur de type type_case
void api_afficher_type_case(type_case v);

/// Affiche le contenu d'une valeur de type etat_barriere
void api_afficher_etat_barriere(etat_barriere v);

/// Affiche le contenu d'une valeur de type etat_nid
void api_afficher_etat_nid(etat_nid v);

/// Affiche le contenu d'une valeur de type pigeon_debug
void api_afficher_pigeon_debug(pigeon_debug v);

/// Affiche le contenu d'une valeur de type type_action
void api_afficher_type_action(type_action v);

/// Affiche le contenu d'une valeur de type position
void api_afficher_position(position v);

/// Affiche le contenu d'une valeur de type troupe
void api_afficher_troupe(troupe v);

/// Affiche le contenu d'une valeur de type etat_case
void api_afficher_etat_case(etat_case v);

/// Affiche le contenu d'une valeur de type action_hist
void api_afficher_action_hist(action_hist v);
}

CSharpInterface gl_csharp;

// Static mapping between C++ types and mono_get_*_class.
template <typename T>
MonoClass* get_mono_class() = delete;
template <>
MonoClass* get_mono_class<int>() { return mono_get_int32_class(); };
template <>
MonoClass* get_mono_class<double>() { return mono_get_double_class(); };
template <>
MonoClass* get_mono_class<bool>() { return mono_get_boolean_class(); };
template <>
MonoClass* get_mono_class<std::string>() { return mono_get_string_class(); };

template <>
MonoClass* get_mono_class<erreur>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<direction>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<type_case>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<etat_barriere>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<etat_nid>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<pigeon_debug>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<type_action>()
{
    return mono_get_int32_class();
}

template <>
MonoClass* get_mono_class<position>()
{
    return mono_class_from_name(gl_csharp.getImage(), "Champion", "Position");
}

template <>
MonoClass* get_mono_class<troupe>()
{
    return mono_class_from_name(gl_csharp.getImage(), "Champion", "Troupe");
}

template <>
MonoClass* get_mono_class<etat_case>()
{
    return mono_class_from_name(gl_csharp.getImage(), "Champion", "EtatCase");
}

template <>
MonoClass* get_mono_class<action_hist>()
{
    return mono_class_from_name(gl_csharp.getImage(), "Champion", "ActionHist");
}

template <class Out, class Cxx>
Out cxx2lang(Cxx in)
{
    return (Out)in;
}

template <>
MonoString* cxx2lang<MonoString*, std::string>(std::string in)
{
    return mono_string_new(gl_csharp.getDomain(), in.c_str());
}

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

template <>
gint32 cxx2lang<gint32, bool>(bool in)
{
    return (gint32)in;
}

template <class Out, class Cxx>
Cxx lang2cxx(Out in)
{
    return (Cxx)in;
}

template <>
std::string lang2cxx<MonoString*, std::string>(MonoString* in)
{
    std::string s_out;
    MonoError error;
    char* c_out;

    if (!in)
        return std::string("(null)");
    c_out = mono_string_to_utf8_checked(in, &error);
    if (!mono_error_ok(&error))
    {
        s_out = std::string(mono_error_get_message(&error));
        mono_error_cleanup(&error);
        return s_out;
    }
    else
    {
        s_out = std::string(c_out);
        mono_free(c_out);
        return s_out;
    }
}

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

template <>
bool lang2cxx<gint32, bool>(gint32 in)
{
    return (bool)in;
}

template <typename CsType, typename CxxType>
MonoArray* cxx2lang_array(const std::vector<CxxType>& in)
{
    gint32 size = in.size();
    MonoClass* mcKlass = get_mono_class<CxxType>();
    MonoArray* maArray = mono_array_new(gl_csharp.getDomain(), mcKlass, size);
    for (int i = 0; i < size; ++i)
    {
        CsType item = (cxx2lang<CsType, CxxType>(in[i]));
        if constexpr (std::is_same_v<CsType, MonoObject*> ||
                      std::is_same_v<CsType, MonoString*>)
            mono_array_setref(maArray, i, item);
        else if constexpr (std::is_enum<CxxType>::value)
            mono_array_set(maArray, int, i, item);
        else
            mono_array_set(maArray, CxxType, i, item);
    }
    return maArray;
}

template <typename CsType, typename CxxType>
std::vector<CxxType> lang2cxx_array(MonoArray* in)
{
    std::vector<CxxType> out;
    gint32 size = mono_array_length(in);
    for (int i = 0; i < size; ++i)
    {
        CsType item;
        if constexpr (std::is_same_v<CsType, MonoObject*> ||
                      std::is_same_v<CsType, MonoString*>)
            item = mono_array_get(in, CsType, i);
        else
            item = mono_array_get(in, CxxType, i);
        out.push_back(lang2cxx<CsType, CxxType>(item));
    }
    return out;
}

// Erreurs possibles après avoir effectué une action
template <>
gint32 cxx2lang<gint32, erreur>(erreur in)
{
    return (gint32)in;
}

template <>
erreur lang2cxx<gint32, erreur>(gint32 in)
{
    return (erreur)in;
}

// Directions possibles
template <>
gint32 cxx2lang<gint32, direction>(direction in)
{
    return (gint32)in;
}

template <>
direction lang2cxx<gint32, direction>(gint32 in)
{
    return (direction)in;
}

// Type de l'élément présent sur une case
template <>
gint32 cxx2lang<gint32, type_case>(type_case in)
{
    return (gint32)in;
}

template <>
type_case lang2cxx<gint32, type_case>(gint32 in)
{
    return (type_case)in;
}

// État d'une barrière, soit ouvert, soit fermé, soit non-applicable
template <>
gint32 cxx2lang<gint32, etat_barriere>(etat_barriere in)
{
    return (gint32)in;
}

template <>
etat_barriere lang2cxx<gint32, etat_barriere>(gint32 in)
{
    return (etat_barriere)in;
}

// Joueur auquel appartient un nid
template <>
gint32 cxx2lang<gint32, etat_nid>(etat_nid in)
{
    return (gint32)in;
}

template <>
etat_nid lang2cxx<gint32, etat_nid>(gint32 in)
{
    return (etat_nid)in;
}

// Type de pigeon de debug
template <>
gint32 cxx2lang<gint32, pigeon_debug>(pigeon_debug in)
{
    return (gint32)in;
}

template <>
pigeon_debug lang2cxx<gint32, pigeon_debug>(gint32 in)
{
    return (pigeon_debug)in;
}

// Types d'actions
template <>
gint32 cxx2lang<gint32, type_action>(type_action in)
{
    return (gint32)in;
}

template <>
type_action lang2cxx<gint32, type_action>(gint32 in)
{
    return (type_action)in;
}

// Position dans la carte, donnée par trois coordonnées

template <>
MonoObject* cxx2lang<MonoObject*, position>(position in)
{
    MonoClass* mcKlass = get_mono_class<position>();
    MonoObject* moObj = mono_object_new(gl_csharp.getDomain(), mcKlass);
    mono_runtime_object_init(moObj);
    auto arg_colonne = cxx2lang<gint32, int>(in.colonne);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Colonne"),
        &arg_colonne
    );
    auto arg_ligne = cxx2lang<gint32, int>(in.ligne);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Ligne"),
        &arg_ligne
    );
    auto arg_niveau = cxx2lang<gint32, int>(in.niveau);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Niveau"),
        &arg_niveau
    );
    return moObj;
}

template <>
position lang2cxx<MonoObject*, position>(MonoObject* in)
{
    position out;
    void* tmp;
    (void)tmp;
    MonoClass* mcKlass =
        mono_class_from_name(gl_csharp.getImage(), "Champion", "Position");

    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Colonne"),
        &out.colonne);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Ligne"),
        &out.ligne);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Niveau"),
        &out.niveau);
    return out;
}

// Une troupe, composée de la maman canard et de ses canetons

template <>
MonoObject* cxx2lang<MonoObject*, troupe>(troupe in)
{
    MonoClass* mcKlass = get_mono_class<troupe>();
    MonoObject* moObj = mono_object_new(gl_csharp.getDomain(), mcKlass);
    mono_runtime_object_init(moObj);
    auto arg_maman = cxx2lang<MonoObject*, position>(in.maman);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Maman"),
        arg_maman
    );
    auto arg_canards = cxx2lang_array<MonoObject*, position>(in.canards);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Canards"),
        arg_canards
    );
    auto arg_taille = cxx2lang<gint32, int>(in.taille);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Taille"),
        &arg_taille
    );
    auto arg_dir = cxx2lang<gint32, direction>(in.dir);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Dir"),
        &arg_dir
    );
    auto arg_inventaire = cxx2lang<gint32, int>(in.inventaire);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Inventaire"),
        &arg_inventaire
    );
    auto arg_pts_action = cxx2lang<gint32, int>(in.pts_action);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "PtsAction"),
        &arg_pts_action
    );
    auto arg_id = cxx2lang<gint32, int>(in.id);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Id"),
        &arg_id
    );
    return moObj;
}

template <>
troupe lang2cxx<MonoObject*, troupe>(MonoObject* in)
{
    troupe out;
    void* tmp;
    (void)tmp;
    MonoClass* mcKlass =
        mono_class_from_name(gl_csharp.getImage(), "Champion", "Troupe");

    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Maman"),
        &tmp);
    out.maman = lang2cxx<MonoObject*, position>(
        reinterpret_cast<MonoObject*>(tmp));
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Canards"),
        &tmp);
    out.canards = lang2cxx_array<MonoObject*, position>(
        reinterpret_cast<MonoArray*>(tmp));
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Taille"),
        &out.taille);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Dir"),
        &out.dir);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Inventaire"),
        &out.inventaire);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "PtsAction"),
        &out.pts_action);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Id"),
        &out.id);
    return out;
}

// Élément constituant le parc

template <>
MonoObject* cxx2lang<MonoObject*, etat_case>(etat_case in)
{
    MonoClass* mcKlass = get_mono_class<etat_case>();
    MonoObject* moObj = mono_object_new(gl_csharp.getDomain(), mcKlass);
    mono_runtime_object_init(moObj);
    auto arg_pos = cxx2lang<MonoObject*, position>(in.pos);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Pos"),
        arg_pos
    );
    auto arg_contenu = cxx2lang<gint32, type_case>(in.contenu);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "Contenu"),
        &arg_contenu
    );
    auto arg_est_constructible = cxx2lang<gint32, bool>(in.est_constructible);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "EstConstructible"),
        &arg_est_constructible
    );
    auto arg_nb_pains = cxx2lang<gint32, int>(in.nb_pains);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "NbPains"),
        &arg_nb_pains
    );
    return moObj;
}

template <>
etat_case lang2cxx<MonoObject*, etat_case>(MonoObject* in)
{
    etat_case out;
    void* tmp;
    (void)tmp;
    MonoClass* mcKlass =
        mono_class_from_name(gl_csharp.getImage(), "Champion", "EtatCase");

    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Pos"),
        &tmp);
    out.pos = lang2cxx<MonoObject*, position>(
        reinterpret_cast<MonoObject*>(tmp));
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "Contenu"),
        &out.contenu);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "EstConstructible"),
        &out.est_constructible);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "NbPains"),
        &out.nb_pains);
    return out;
}

// Action représentée dans l'historique

template <>
MonoObject* cxx2lang<MonoObject*, action_hist>(action_hist in)
{
    MonoClass* mcKlass = get_mono_class<action_hist>();
    MonoObject* moObj = mono_object_new(gl_csharp.getDomain(), mcKlass);
    mono_runtime_object_init(moObj);
    auto arg_action_type = cxx2lang<gint32, type_action>(in.action_type);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "ActionType"),
        &arg_action_type
    );
    auto arg_troupe_id = cxx2lang<gint32, int>(in.troupe_id);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "TroupeId"),
        &arg_troupe_id
    );
    auto arg_action_dir = cxx2lang<gint32, direction>(in.action_dir);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "ActionDir"),
        &arg_action_dir
    );
    auto arg_action_pos = cxx2lang<MonoObject*, position>(in.action_pos);
    mono_field_set_value(
        moObj,
        mono_class_get_field_from_name(mcKlass, "ActionPos"),
        arg_action_pos
    );
    return moObj;
}

template <>
action_hist lang2cxx<MonoObject*, action_hist>(MonoObject* in)
{
    action_hist out;
    void* tmp;
    (void)tmp;
    MonoClass* mcKlass =
        mono_class_from_name(gl_csharp.getImage(), "Champion", "ActionHist");

    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "ActionType"),
        &out.action_type);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "TroupeId"),
        &out.troupe_id);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "ActionDir"),
        &out.action_dir);
    mono_field_get_value(
        in,
        mono_class_get_field_from_name(mcKlass, "ActionPos"),
        &tmp);
    out.action_pos = lang2cxx<MonoObject*, position>(
        reinterpret_cast<MonoObject*>(tmp));
    return out;
}


// C# native wrapper for function avancer.
// La troupe avance d'une case vers une direction donnée
gint32 cs_avancer(gint32 id, gint32 dir)
{
    return cxx2lang<gint32, erreur>(api_avancer(lang2cxx<gint32, int>(id), lang2cxx<gint32, direction>(dir)));
}

// C# native wrapper for function grandir.
// La troupe grandit
gint32 cs_grandir(gint32 id)
{
    return cxx2lang<gint32, erreur>(api_grandir(lang2cxx<gint32, int>(id)));
}

// C# native wrapper for function construire_buisson.
// Construit un buisson à la position donnée
gint32 cs_construire_buisson(MonoObject* pos)
{
    return cxx2lang<gint32, erreur>(api_construire_buisson(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function creuser_tunnel.
// Creuse un tunnel à la position donnée
gint32 cs_creuser_tunnel(MonoObject* pos)
{
    return cxx2lang<gint32, erreur>(api_creuser_tunnel(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function info_case.
// Renvoie les informations concernant une case
MonoObject* cs_info_case(MonoObject* pos)
{
    return cxx2lang<MonoObject*, etat_case>(api_info_case(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function info_barriere.
// Renvoie les informations d'état d'une barrière
gint32 cs_info_barriere(MonoObject* pos)
{
    return cxx2lang<gint32, etat_barriere>(api_info_barriere(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function info_nid.
// Renvoie les informations d'état d'un nid
gint32 cs_info_nid(MonoObject* pos)
{
    return cxx2lang<gint32, etat_nid>(api_info_nid(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function papy_tours_restants.
// Renvoie le nombre de tours restants avant qu'un papy dépose une miche de
// pain. Retourne -1 si aucun papy ne se trouve à la position demandée
gint32 cs_papy_tours_restants(MonoObject* pos)
{
    return cxx2lang<gint32, int>(api_papy_tours_restants(lang2cxx<MonoObject*, position>(pos)));
}

// C# native wrapper for function troupes_joueur.
// Renvoie les troupes d'un joueur. Si le joueur est invalide, tous les champs
// valent -1.
MonoArray* cs_troupes_joueur(gint32 id_joueur)
{
    return cxx2lang_array<MonoObject*, troupe>(api_troupes_joueur(lang2cxx<gint32, int>(id_joueur)));
}

// C# native wrapper for function pains.
// Renvoie la position des pains récupérables
MonoArray* cs_pains()
{
    return cxx2lang_array<MonoObject*, position>(api_pains());
}

// C# native wrapper for function debug_poser_pigeon.
// Pose un pigeon de debug sur la case indiquée
gint32 cs_debug_poser_pigeon(MonoObject* pos, gint32 pigeon)
{
    return cxx2lang<gint32, erreur>(api_debug_poser_pigeon(lang2cxx<MonoObject*, position>(pos), lang2cxx<gint32, pigeon_debug>(pigeon)));
}

// C# native wrapper for function historique.
// Renvoie la liste des actions effectuées par l'adversaire durant son tour,
// dans l'ordre chronologique. Les actions de débug n'apparaissent pas dans
// cette liste.
MonoArray* cs_historique()
{
    return cxx2lang_array<MonoObject*, action_hist>(api_historique());
}

// C# native wrapper for function gain.
// Renvoie le gain en score que le nombre de pains passé en entrée rapporterait
// s'ils étaient tous déposés d'un coup dans un nid
gint32 cs_gain(gint32 nb_pains)
{
    return cxx2lang<gint32, int>(api_gain(lang2cxx<gint32, int>(nb_pains)));
}

// C# native wrapper for function inventaire.
// Renvoie la taille de l'inventaire d'une troupe de taille donnée
gint32 cs_inventaire(gint32 taille)
{
    return cxx2lang<gint32, int>(api_inventaire(lang2cxx<gint32, int>(taille)));
}

// C# native wrapper for function trouver_chemin.
// Trouve un plus court chemin ouvert entre deux positions. Renvoie une liste
// vide si les deux positions sont égales ou si aucun chemin n'existe.
MonoArray* cs_trouver_chemin(MonoObject* depart, MonoObject* arrivee)
{
    return cxx2lang_array<gint32, direction>(api_trouver_chemin(lang2cxx<MonoObject*, position>(depart), lang2cxx<MonoObject*, position>(arrivee)));
}

// C# native wrapper for function moi.
// Renvoie votre numéro de joueur.
gint32 cs_moi()
{
    return cxx2lang<gint32, int>(api_moi());
}

// C# native wrapper for function adversaire.
// Renvoie le numéro du joueur adverse.
gint32 cs_adversaire()
{
    return cxx2lang<gint32, int>(api_adversaire());
}

// C# native wrapper for function score.
// Renvoie le score du joueur `id_joueur`. Renvoie -1 si le joueur est
// invalide.
gint32 cs_score(gint32 id_joueur)
{
    return cxx2lang<gint32, int>(api_score(lang2cxx<gint32, int>(id_joueur)));
}

// C# native wrapper for function annuler.
// Annule la dernière action. Renvoie faux quand il n'y a pas d'action à
// annuler ce tour-ci
gint32 cs_annuler()
{
    return cxx2lang<gint32, bool>(api_annuler());
}

// C# native wrapper for function tour_actuel.
// Retourne le numéro du tour actuel.
gint32 cs_tour_actuel()
{
    return cxx2lang<gint32, int>(api_tour_actuel());
}

// C# native wrapper for function afficher_erreur.
// Affiche le contenu d'une valeur de type erreur
void cs_afficher_erreur(gint32 v)
{
    (api_afficher_erreur(lang2cxx<gint32, erreur>(v)));
}

// C# native wrapper for function afficher_direction.
// Affiche le contenu d'une valeur de type direction
void cs_afficher_direction(gint32 v)
{
    (api_afficher_direction(lang2cxx<gint32, direction>(v)));
}

// C# native wrapper for function afficher_type_case.
// Affiche le contenu d'une valeur de type type_case
void cs_afficher_type_case(gint32 v)
{
    (api_afficher_type_case(lang2cxx<gint32, type_case>(v)));
}

// C# native wrapper for function afficher_etat_barriere.
// Affiche le contenu d'une valeur de type etat_barriere
void cs_afficher_etat_barriere(gint32 v)
{
    (api_afficher_etat_barriere(lang2cxx<gint32, etat_barriere>(v)));
}

// C# native wrapper for function afficher_etat_nid.
// Affiche le contenu d'une valeur de type etat_nid
void cs_afficher_etat_nid(gint32 v)
{
    (api_afficher_etat_nid(lang2cxx<gint32, etat_nid>(v)));
}

// C# native wrapper for function afficher_pigeon_debug.
// Affiche le contenu d'une valeur de type pigeon_debug
void cs_afficher_pigeon_debug(gint32 v)
{
    (api_afficher_pigeon_debug(lang2cxx<gint32, pigeon_debug>(v)));
}

// C# native wrapper for function afficher_type_action.
// Affiche le contenu d'une valeur de type type_action
void cs_afficher_type_action(gint32 v)
{
    (api_afficher_type_action(lang2cxx<gint32, type_action>(v)));
}

// C# native wrapper for function afficher_position.
// Affiche le contenu d'une valeur de type position
void cs_afficher_position(MonoObject* v)
{
    (api_afficher_position(lang2cxx<MonoObject*, position>(v)));
}

// C# native wrapper for function afficher_troupe.
// Affiche le contenu d'une valeur de type troupe
void cs_afficher_troupe(MonoObject* v)
{
    (api_afficher_troupe(lang2cxx<MonoObject*, troupe>(v)));
}

// C# native wrapper for function afficher_etat_case.
// Affiche le contenu d'une valeur de type etat_case
void cs_afficher_etat_case(MonoObject* v)
{
    (api_afficher_etat_case(lang2cxx<MonoObject*, etat_case>(v)));
}

// C# native wrapper for function afficher_action_hist.
// Affiche le contenu d'une valeur de type action_hist
void cs_afficher_action_hist(MonoObject* v)
{
    (api_afficher_action_hist(lang2cxx<MonoObject*, action_hist>(v)));
}

// Inititialize Mono and load the DLL file.
CSharpInterface::CSharpInterface()
{
    const char* champion_path = getenv("CHAMPION_PATH");
    std::string champion;

    if (!champion_path)
        champion = "./champion.dll";
    else
    {
        champion = champion_path;
        champion += "/champion.dll";
    }

    mono_config_parse(NULL);

    _domain = mono_jit_init(champion.c_str());
    assert(_domain != NULL);

    mono_domain_set_config(_domain, ".", ".mono.conf");

    _assembly = mono_domain_assembly_open(_domain, champion.c_str());
    assert(_assembly != NULL);

    _image = mono_assembly_get_image(_assembly);
    assert(_image != NULL);

    _class = mono_class_from_name(_image, "Champion", "Champion");
    assert(_class != NULL);

    _object = mono_object_new(_domain, _class);
    assert(_object);

    mono_runtime_object_init(_object);

    // Register API functions as internal Mono functions
    mono_add_internal_call("Champion.Api::Avancer", (const void*)cs_avancer);
    mono_add_internal_call("Champion.Api::Grandir", (const void*)cs_grandir);
    mono_add_internal_call("Champion.Api::ConstruireBuisson", (const void*)cs_construire_buisson);
    mono_add_internal_call("Champion.Api::CreuserTunnel", (const void*)cs_creuser_tunnel);
    mono_add_internal_call("Champion.Api::InfoCase", (const void*)cs_info_case);
    mono_add_internal_call("Champion.Api::InfoBarriere", (const void*)cs_info_barriere);
    mono_add_internal_call("Champion.Api::InfoNid", (const void*)cs_info_nid);
    mono_add_internal_call("Champion.Api::PapyToursRestants", (const void*)cs_papy_tours_restants);
    mono_add_internal_call("Champion.Api::TroupesJoueur", (const void*)cs_troupes_joueur);
    mono_add_internal_call("Champion.Api::Pains", (const void*)cs_pains);
    mono_add_internal_call("Champion.Api::DebugPoserPigeon", (const void*)cs_debug_poser_pigeon);
    mono_add_internal_call("Champion.Api::Historique", (const void*)cs_historique);
    mono_add_internal_call("Champion.Api::Gain", (const void*)cs_gain);
    mono_add_internal_call("Champion.Api::Inventaire", (const void*)cs_inventaire);
    mono_add_internal_call("Champion.Api::TrouverChemin", (const void*)cs_trouver_chemin);
    mono_add_internal_call("Champion.Api::Moi", (const void*)cs_moi);
    mono_add_internal_call("Champion.Api::Adversaire", (const void*)cs_adversaire);
    mono_add_internal_call("Champion.Api::Score", (const void*)cs_score);
    mono_add_internal_call("Champion.Api::Annuler", (const void*)cs_annuler);
    mono_add_internal_call("Champion.Api::TourActuel", (const void*)cs_tour_actuel);
    mono_add_internal_call("Champion.Api::AfficherErreur", (const void*)cs_afficher_erreur);
    mono_add_internal_call("Champion.Api::AfficherDirection", (const void*)cs_afficher_direction);
    mono_add_internal_call("Champion.Api::AfficherTypeCase", (const void*)cs_afficher_type_case);
    mono_add_internal_call("Champion.Api::AfficherEtatBarriere", (const void*)cs_afficher_etat_barriere);
    mono_add_internal_call("Champion.Api::AfficherEtatNid", (const void*)cs_afficher_etat_nid);
    mono_add_internal_call("Champion.Api::AfficherPigeonDebug", (const void*)cs_afficher_pigeon_debug);
    mono_add_internal_call("Champion.Api::AfficherTypeAction", (const void*)cs_afficher_type_action);
    mono_add_internal_call("Champion.Api::AfficherPosition", (const void*)cs_afficher_position);
    mono_add_internal_call("Champion.Api::AfficherTroupe", (const void*)cs_afficher_troupe);
    mono_add_internal_call("Champion.Api::AfficherEtatCase", (const void*)cs_afficher_etat_case);
    mono_add_internal_call("Champion.Api::AfficherActionHist", (const void*)cs_afficher_action_hist);
}

MonoImage* CSharpInterface::getImage()
{
    return _image;
}

MonoDomain* CSharpInterface::getDomain()
{
    return _domain;
}

CSharpInterface::~CSharpInterface()
{
    mono_image_close(_image);
    mono_assembly_close(_assembly);
    // XXX -- mono segfaults when calling this. Seems to be a known bug
    //        appearing when mono_jit_clean() is called from a dtor. ???
    // mono_jit_cleanup(_domain);
}

// Calls C# functions from C++
MonoObject* CSharpInterface::callCSharpMethod(const char* name)
{
    MonoThread* thread = mono_thread_attach(_domain);
    MonoMethod* method = mono_class_get_method_from_name(_class, name, 0);
    MonoObject* object = mono_runtime_invoke(method, _object, NULL, NULL);

    mono_thread_detach(thread);

    return object;
}


// Functions called from Stechec to C#.

extern "C" void partie_init()
{
    gl_csharp.callCSharpMethod("PartieInit");
}

extern "C" void jouer_tour()
{
    gl_csharp.callCSharpMethod("JouerTour");
}

extern "C" void partie_fin()
{
    gl_csharp.callCSharpMethod("PartieFin");
}
