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

extern "C" {
#include <caml/mlvalues.h>
#include <caml/callback.h>
#include <caml/alloc.h>
#include <caml/memory.h>
}
#include "interface.hh"

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

template <>
value cxx2lang<value, int>(int in)
{
  CAMLparam0();
  CAMLreturn(Val_int(in));
}

template <>
value cxx2lang<value, double>(double in)
{
  CAMLparam0();
  CAMLreturn(caml_copy_double(in));
}

template<>
value cxx2lang<value, std::string>(std::string in)
{
  CAMLparam0();
  size_t l = in.length();
  char * out = (char *) malloc(l + 1);
  for (int i = 0; i < l; i++) out[i] = in[i];
  out[l] = 0;
  CAMLreturn(caml_copy_string(out));
}

template <>
value cxx2lang<value, bool>(bool in)
{
  CAMLparam0();
  CAMLreturn(Val_int(in));
}

template <typename Cxx>
value cxx2lang_array(const std::vector<Cxx>& in)
{
  CAMLparam0();
  CAMLlocal1(v);

  size_t size = in.size();
  if (size == 0)
    CAMLreturn(Atom(0));

  v = caml_alloc(size, 0);
  for (int i = 0; i < size; ++i)
    caml_initialize(&Field(v, i), cxx2lang<value, Cxx>(in[i]));

  CAMLreturn(v);
}

template <typename Lang, typename Cxx>
Cxx lang2cxx(Lang in)
{
  return in.__if_that_triggers_an_error_there_is_a_problem;
}

template<>
std::string lang2cxx<value, std::string>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(std::string, String_val(in));
}

template <>
int lang2cxx<value, int>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(int, Int_val(in));
}

template <>
double lang2cxx<value, double>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(double, Double_val(in));
}

template <>
bool lang2cxx<value, bool>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(bool, Int_val(in));
}

template <typename Cxx>
std::vector<Cxx> lang2cxx_array(value in)
{
  CAMLparam1(in);
  std::vector<Cxx> out;
  mlsize_t size = Wosize_val(in);

  for (int i = 0; i < size; ++i)
    out.push_back(lang2cxx<value, Cxx>(Field(in, i)));

  CAMLreturnT(std::vector<Cxx>, out);
}
/// Erreurs possibles
template <>
value cxx2lang<value, erreur>(erreur in)
{
  CAMLparam0();
  CAMLreturn(Val_int(in));
}

template <>
erreur lang2cxx<value, erreur>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(erreur, (erreur)Int_val(in));
}

/// Types de cases
template <>
value cxx2lang<value, case_type>(case_type in)
{
  CAMLparam0();
  CAMLreturn(Val_int(in));
}

template <>
case_type lang2cxx<value, case_type>(value in)
{
  CAMLparam1(in);
  CAMLreturnT(case_type, (case_type)Int_val(in));
}

/// Position sur la carte, donnée par deux coordonnées.
template <>
value cxx2lang<value, position>(position in)
{
  CAMLparam0();
  CAMLlocal1(out);
  out = caml_alloc(2, 0);
  caml_initialize(&Field(out, 0), cxx2lang<value, int>(in.x));
  caml_initialize(&Field(out, 1), cxx2lang<value, int>(in.y));
  CAMLreturn(out);
}

template <>
position lang2cxx<value, position>(value in)
{
  CAMLparam1(in);
  position out;
  out.x = lang2cxx<value, int>(Field(in, 0));
  out.y = lang2cxx<value, int>(Field(in, 1));
  CAMLreturnT(position, out);
}

/// Représente un pulsar existant.
template <>
value cxx2lang<value, pulsar_info>(pulsar_info in)
{
  CAMLparam0();
  CAMLlocal1(out);
  out = caml_alloc(4, 0);
  caml_initialize(&Field(out, 0), cxx2lang<value, int>(in.periode));
  caml_initialize(&Field(out, 1), cxx2lang<value, double>(in.puissance));
  caml_initialize(&Field(out, 2), cxx2lang<value, int>(in.pulsations_restantes));
  caml_initialize(&Field(out, 3), cxx2lang<value, int>(in.pulsations_totales));
  CAMLreturn(out);
}

template <>
pulsar_info lang2cxx<value, pulsar_info>(value in)
{
  CAMLparam1(in);
  pulsar_info out;
  out.periode = lang2cxx<value, int>(Field(in, 0));
  out.puissance = lang2cxx<value, double>(Field(in, 1));
  out.pulsations_restantes = lang2cxx<value, int>(Field(in, 2));
  out.pulsations_totales = lang2cxx<value, int>(Field(in, 3));
  CAMLreturnT(pulsar_info, out);
}

/*
** Inititialize caml
*/
static inline void _init_caml()
{
    static bool is_initialized = false;

    if (!is_initialized)
    {
        is_initialized = true;

        const char* argv[2] = {"./caml", NULL};
        caml_startup(const_cast<char**>(argv));
    }
}

/// Construit un tuyau sur une case donnée.
extern "C" value ml_construire(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, erreur>(api_construire(lang2cxx<value, position>(pos)))));
}

/// Améliore un tuyau en Super Tuyau™.
extern "C" value ml_ameliorer(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, erreur>(api_ameliorer(lang2cxx<value, position>(pos)))));
}

/// Détruit un tuyau sur une case donnée.
extern "C" value ml_detruire(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, erreur>(api_detruire(lang2cxx<value, position>(pos)))));
}

/// Déplace une unité de puissance d'aspiration d'une case de votre base vers une autre.
extern "C" value ml_deplacer_aspiration(value source, value destination)
{
  CAMLparam0();
  CAMLxparam2(source, destination);
  CAMLreturn((cxx2lang<value, erreur>(api_deplacer_aspiration(lang2cxx<value, position>(source), lang2cxx<value, position>(destination)))));
}

/// Déblaye une case de débris.
extern "C" value ml_deblayer(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, erreur>(api_deblayer(lang2cxx<value, position>(pos)))));
}

/// Renvoie le type d'une case donnée.
extern "C" value ml_type_case(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, case_type>(api_type_case(lang2cxx<value, position>(pos)))));
}

/// Renvoie la liste de tous les pulsars présents.
extern "C" value ml_liste_pulsars(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_liste_pulsars())));
}

/// Renvoie la liste des cases contenant du plasma.
extern "C" value ml_liste_plasmas(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_liste_plasmas())));
}

/// Renvoie la liste des cases contenant un tuyau ou Super Tuyau™.
extern "C" value ml_liste_tuyaux(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_liste_tuyaux())));
}

/// Renvoie la liste des cases contenant un Super Tuyau™.
extern "C" value ml_liste_super_tuyaux(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_liste_super_tuyaux())));
}

/// Renvoie la liste des cases contenant des débris.
extern "C" value ml_liste_debris(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_liste_debris())));
}

/// Renvoie vrai si et seulement si la case contient un pulsar.
extern "C" value ml_est_pulsar(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_pulsar(lang2cxx<value, position>(pos)))));
}

/// Renvoie vrai si et seulement si la case contient un tuyau ou un Super Tuyau™.
extern "C" value ml_est_tuyau(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_tuyau(lang2cxx<value, position>(pos)))));
}

/// Renvoie vrai si et seulement si la case contient un simple tuyau.
extern "C" value ml_est_simple_tuyau(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_simple_tuyau(lang2cxx<value, position>(pos)))));
}

/// Renvoie vrai si et seulement si la case contient un Super Tuyau™.
extern "C" value ml_est_super_tuyau(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_super_tuyau(lang2cxx<value, position>(pos)))));
}

/// Renvoie vrai si et seulement si la case contient un débris.
extern "C" value ml_est_debris(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_debris(lang2cxx<value, position>(pos)))));
}

/// Renvoie vrai si et seulement s'il est possible de construire sur cette case.
extern "C" value ml_est_libre(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, bool>(api_est_libre(lang2cxx<value, position>(pos)))));
}

/// Renvoie les informations propres au pulsar à la position donnée.
extern "C" value ml_info_pulsar(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, pulsar_info>(api_info_pulsar(lang2cxx<value, position>(pos)))));
}

/// Renvoie la quantité de plasma sur une case donnée.
extern "C" value ml_charges_presentes(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, double>(api_charges_presentes(lang2cxx<value, position>(pos)))));
}

/// Renvoie le numéro du joueur ayant dernièrement construit ou amélioré un tuyau sur une case.
extern "C" value ml_constructeur_tuyau(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, int>(api_constructeur_tuyau(lang2cxx<value, position>(pos)))));
}

/// Renvoie le numéro du propriétaire d'une case de base.
extern "C" value ml_proprietaire_base(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, int>(api_proprietaire_base(lang2cxx<value, position>(pos)))));
}

/// Renvoie la liste des cases composant votre base.
extern "C" value ml_ma_base(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_ma_base())));
}

/// Renvoie la liste des cases composant la base de votre ennemi.
extern "C" value ml_base_ennemie(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_base_ennemie())));
}

/// Renvoie la puissance d'aspiration d'une case de base donnée. Renvoie -1 si la position n'est pas celle d'une base.
extern "C" value ml_puissance_aspiration(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang<value, int>(api_puissance_aspiration(lang2cxx<value, position>(pos)))));
}

/// Renvoie pour une case donnée la liste des cases vers lesquelles le plasma se propagera. Renvoie la liste vide si la case n'est pas une case contenant un tuyau ou si elle n'est reliée à aucune base.
extern "C" value ml_directions_plasma(value pos)
{
  CAMLparam0();
  CAMLxparam1(pos);
  CAMLreturn((cxx2lang_array<position>(api_directions_plasma(lang2cxx<value, position>(pos)))));
}

/// Renvoie la valeur du coût de la prochaine modification de vos puissances d'aspiration.
extern "C" value ml_cout_prochaine_modification_aspiration(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, int>(api_cout_prochaine_modification_aspiration())));
}

/// Renvoie la liste des tuyaux construits par votre adversaire au dernier tour.
extern "C" value ml_hist_tuyaux_construits(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_tuyaux_construits())));
}

/// Renvoie la liste des tuyaux détruits par votre adversaire au dernier tour.
extern "C" value ml_hist_tuyaux_detruits(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_tuyaux_detruits())));
}

/// Renvoie la liste des tuyaux améliorés par votre adversaire au dernier tour.
extern "C" value ml_hist_tuyaux_ameliores(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_tuyaux_ameliores())));
}

/// Renvoie la liste des débris déblayés par votre adversaire au dernier tour.
extern "C" value ml_hist_debris_deblayes(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_debris_deblayes())));
}

/// Renvoie la liste des cases de base de votre adversaire qui ont reçu un point d'aspiration (une même case peut apparaître plusieurs fois).
extern "C" value ml_hist_points_aspiration_ajoutes(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_points_aspiration_ajoutes())));
}

/// Renvoie la liste des cases de base de votre adversaire qui ont perdu un point d'aspiration (une même case peut apparaître plusieurs fois).
extern "C" value ml_hist_points_aspiration_retires(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang_array<position>(api_hist_points_aspiration_retires())));
}

/// Renvoie votre numéro de joueur.
extern "C" value ml_moi(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, int>(api_moi())));
}

/// Renvoie le numéro de votre adversaire.
extern "C" value ml_adversaire(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, int>(api_adversaire())));
}

/// Indique votre nombre de points d'actions restants pour ce tour-ci.
extern "C" value ml_points_action(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, int>(api_points_action())));
}

/// Renvoie le score du joueur désigné par le numéro ``id_joueur``. Renvoie -1 si ``id_joueur`` est invalide.
extern "C" value ml_score(value id_joueur)
{
  CAMLparam0();
  CAMLxparam1(id_joueur);
  CAMLreturn((cxx2lang<value, int>(api_score(lang2cxx<value, int>(id_joueur)))));
}

/// Renvoie le numéro du tour actuel.
extern "C" value ml_tour_actuel(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, int>(api_tour_actuel())));
}

/// Annule la dernière action. Renvoie ``false`` quand il n'y a pas d'action à annuler ce tour-ci.
extern "C" value ml_annuler(value unit)
{
  CAMLparam0();
  CAMLxparam1(unit);
  CAMLreturn((cxx2lang<value, bool>(api_annuler())));
}

/// Affiche le contenu d'une valeur de type erreur
extern "C" value ml_afficher_erreur(value v)
{
  CAMLparam0();
  CAMLxparam1(v);
  api_afficher_erreur(lang2cxx<value, erreur>(v));
  CAMLreturn(Val_unit);
}

/// Affiche le contenu d'une valeur de type case_type
extern "C" value ml_afficher_case_type(value v)
{
  CAMLparam0();
  CAMLxparam1(v);
  api_afficher_case_type(lang2cxx<value, case_type>(v));
  CAMLreturn(Val_unit);
}

/// Affiche le contenu d'une valeur de type position
extern "C" value ml_afficher_position(value v)
{
  CAMLparam0();
  CAMLxparam1(v);
  api_afficher_position(lang2cxx<value, position>(v));
  CAMLreturn(Val_unit);
}

/// Affiche le contenu d'une valeur de type pulsar_info
extern "C" value ml_afficher_pulsar_info(value v)
{
  CAMLparam0();
  CAMLxparam1(v);
  api_afficher_pulsar_info(lang2cxx<value, pulsar_info>(v));
  CAMLreturn(Val_unit);
}

/// Fonction appelée au début de la partie.
void partie_init()
{
  _init_caml();
  CAMLparam0();
  CAMLlocal1(_ret);
  static value *closure = NULL;
  if (closure == NULL)
    closure = caml_named_value("ml_partie_init");
  _ret = callback(*closure, Val_unit);
  CAMLreturn0;
}


/// Fonction appelée à chaque tour.
void jouer_tour()
{
  _init_caml();
  CAMLparam0();
  CAMLlocal1(_ret);
  static value *closure = NULL;
  if (closure == NULL)
    closure = caml_named_value("ml_jouer_tour");
  _ret = callback(*closure, Val_unit);
  CAMLreturn0;
}


/// Fonction appelée à la fin de la partie.
void partie_fin()
{
  _init_caml();
  CAMLparam0();
  CAMLlocal1(_ret);
  static value *closure = NULL;
  if (closure == NULL)
    closure = caml_named_value("ml_partie_fin");
  _ret = callback(*closure, Val_unit);
  CAMLreturn0;
}


