<?php

/**
 * Indexation des espèces. 
 *
 * Au fur et à mesure que des espèces sont identifiées, de nouvelles fiches
 * (en réalité des articles dont la 'composition' est 'espece') sont créées
 * manuellement par les administrateurs du site. 
 * Ces fiches/articles ont des identifiants SPIP purement internes qui ne permettent
 * pas de les rapprocher de classifications existantes. En revanche, le sous-titre
 * des fiches/articles contient le nom binominal latin reconnu par ces classifications. 
 * L'indexation consiste à comparer ces noms latins à la classification TAXREF gérée
 * par le Muséum National d'Histoire Naturelle, et de créer, pour chaque espèce 
 * ainsi identifiée, une entrée dans la table 'spip_taxrefs'. Cette entrée reprend
 * les éléments fournis par TAXREF (dont les identifiants 'cd_nom' et 'cd_ref'), et fait
 * le lien avec l'identifiant SPIP de la fiche/article correspondante.
 *
 * @plugin     Biodiv
 * @copyright  2008-2025
 * @author     Renaud LAURETTE
 * @licence    GNU/GPL
 * @see		   LICENCE-BIODIV.md
 * @package    SPIP\Biodiv\Genie
 * @subpackage taxref
 * @since	   3.0
 */

if (!defined('_ECRIRE_INC_VERSION')) return;
include_spip('inc/utils');

// On récupère la configuration de TAXREF
$txconf = unserialize($GLOBALS['meta']['taxref']);
//spip_log("TAXREF: configuration taxref : ". print_r($txconf,true), "biodiv."._LOG_INFO_IMPORTANTE);

/*
 * Paramétrage de l'indexation.
 * La classification TAXREF comprend des centaines de milliers d'entrées. Pour ne
 * pas encombrer la base du site, on garde ces entrées dans des fichiers (environ
 * 300), qui vont être scannés à raison d'un par itération de la tache de fond.
 * Les constantes définies ici permettent de gérér ces itérations nécessaires
 */

/** Validité en secondes de l'extraction de la liste des espèces */
define('_TAXREF_DELAY', 180);	
/** Taille max d'une ligne du catalogue TAXREF */
define('_CSV_LINESIZE', 8000);		
/** Index du champ contenant le nom binomial (binominal) dans le référentiel TAXREF */
define('_NOM_BINOMIAL', $txconf['binomial']);	
/** Index du champ contenant le CD_NOM dans le référentiel TAXREF*/
define('_CD_NOM', $txconf['cdnom']);
/** Index du champ contenant le CD_REF dans le référentiel TAXREF*/
define('_CD_REF', $txconf['cdref']);
/** Index du champ contenant le REGNE dans le référentiel TAXREF*/
define('_REGNE', $txconf['regne']);	
/** Index du champ contenant le GROUP1 dans le référentiel TAXREF*/
define('_GROUP1', $txconf['group1']);
/** Index du champ contenant le GROUP2 dans le référentiel TAXREF */
define('_GROUP2', $txconf['group2']);
/** Index du champ contenant le RANG dans le référentiel TAXREF */
define('_RANG', $txconf['rang']);			
/** Nombre de lignes de référentiel TAXREF lues par iteration */
define('_TAXREF_ITERATION', $txconf['iteration']);  
/** Template du nom de fichier pour un fragment TAXREF. %d sera le numero du fragment */
define('_TAXREF_PATTERN',$txconf['pattern']);		 
/** Nombre de fragments dans le référentiel TAXREF */
define('_TAXREF_FRAGMENTS',$txconf['fragments']);	

/**
 * Charger un extrait de TAXREF.
 *
 * Itération unitaire de l'indexation. La fonction retrouve la liste des fiches
 * espèces, et lit la tranche suivante du catalogue pour tenter de nouvelles
 * indexations. Elle renvoi un statut terminé (1) si la dernière transche du 
 * catalogue TAXREF a été lue, et (0) sinon.
 *
 * @pipeline		taches_generales_cron
 * @uses			taxref_lister_especes()
 * @uses			taxref_lire_catalogue()
 * @param int $t
 * @return int		Status de fin : 0 = non fini ; 1 = fini
 */
function genie_taxref_dist($t) {
	$taxons = array(); $file = _DIR_CACHE.'especes.txt';
	// spip_log("TAXREF Genie delai="._TAXREF_DELAY." iteration="._TAXREF_ITERATION, "biodiv."._LOG_INFO_IMPORTANTE);
	$especes = taxref_lister_especes($file, _TAXREF_DELAY);
	$status = taxref_lire_catalogue(_ROOT_RACINE ._TAXREF_PATTERN,$taxons,_TAXREF_ITERATION,$especes);
	// taxref_dump($taxons);
	taxref_insert($taxons);
	return $status;
}

/**
 * Liste de toutes les fiches espèces
 *
 * Construit un tableau contenant des informations sur les fiches espèces. Le
 * tableau est établi à partir d'un fichier 'cache' si celui-ci n'est pas
 * trop ancien, sinon le fichier est regénéré en requêtant la base. 
 * Le tableau est destiné à taxref_lire_catalogue.
 *
 * @used-by					genie_taxref_dist()
 * @param string $filename	Nom du fichier destinataire de la liste
 * @param int $delay		Durée de vie du fichier avant regénération
 * @return array			Tableau des espèces trouvées
 */

function taxref_lister_especes($filename,$delay) {
	// Si un fichier cache existe et n'est pas trop vieux
	// on retourne son contenu
	//spip_log("TAXREF lister especes fichier=".$filename." delai=".$delay, "biodiv."._LOG_INFO_IMPORTANTE);	
	
	if(file_exists($filename)) {
		// spip_log("TAXREF fichier existant " . $filename, "biodiv."._LOG_INFO_IMPORTANTE);
		$modif = filemtime($filename);
		if((time() - $modif) < $delay) {
			// spip_log("TAXREF fichier a jour ".$filename, "biodiv."._LOG_INFO_IMPORTANTE);
			$rows = unserialize(file_get_contents($filename));
			//spip_log("TAXREF : especes lues ".count($rows), "biodiv."._LOG_INFO_IMPORTANTE);
			return $rows;
		}
	}
	// Sinon, on va chercher le contenu dans la base
	// et on crée le fichier cache pour prochaine fois.
	// spip_log("TAXREF fichier=".$filename." inexistant ou obsolete.", "biodiv."._LOG_INFO_IMPORTANTE);
	$especes = sql_select(
				array('soustitre','id_article'),	// fields
				'spip_articles',					// from
				"composition='espece'",			// where
				'',									// group-by
				"soustitre"							// order-by
				);
	$rows = array(); $count=0;
	
	$pattern = '/ [Ss]p[\\.]?$/';
	$replace = '';
	while($row = sql_fetch($especes)) {
		// On supprime les suffixes ' Sp.' ou ' sp.' qui ne sont pas utilisés dans taxref.
		// 2022-03-08 : et on supprime les espaces en extremite de chaine
		$binomial = preg_replace($pattern,$replace,trim($row['soustitre'])); 
		//$binomial = $row['soustitre'];
		$rows[$binomial] = $row; $count++;
	}
	// spip_log("TAXREF : especes dans la base ".$count." dont ".count($rows)." distinctes.", "biodiv."._LOG_INFO_IMPORTANTE);
	file_put_contents($filename,serialize($rows));
	return $rows;
}



/**
 * Lecture d'un fragment de catalogue TAXREF
 *
 * Cette fonction part du principe que le catalogue est découpé en fragments
 * suffisamment petits pour être chacun lus en une seule itération.
 * Le contenu du tableau retourné par taxref_lister_especes est confronté
 * au contenu du catalogue 
 *
 * Note: On n'utilise pas la valeur 'ligne' (from) mais on la garde à 0 pour la
 * compatibilité avec la fonction lire_catalogue d'origine.
 *
 * @param string $catPattern	Pattern de nom de fichier fragment du catalogue TAXREF
 * @param array  &$taxons		Tableau qui recevra les taxons à insérer
 * @param int $nbLignes			Nombre de lignes à lire dans le fragment
 * @param array $especes		Espèces présentes dans la base du site
 * @return int					1 si tout le catalogue a été lu, -10 sinon
 */
 
function taxref_lire_catalogue($catPattern,&$taxons,$nbLignes,$especes) {
	$lDelimiter="\t";
	include_spip('inc/meta');
	
	// La portion du catalogue à lire démare à $from
	// La valeur est stockée dans les métas et mise à jour à chaque appel
	// de cette fonction pour permettre une lecture itérative sans timeout.
	$from = array(
		'ligne' => 0,		// reste pour compatibilité mais non utilisé
		'fragment' => 1,
		'reset' => date("Y-m-d H:i:s"),
		'group1' => '',
		'group2' => ''
		);
	if(isset($GLOBALS['meta']['taxref_debut'])) {
		$from = unserialize($GLOBALS['meta']['taxref_debut']);
	}
	
	$catalogue = sprintf($catPattern,intval($from['fragment']));
	// spip_log("TAXREF : lecture de ".$catalogue, "biodiv."._LOG_INFO_IMPORTANTE);
	$catalogue = realpath($catalogue);
	$lFp = fopen($catalogue,"r");
	if(!$lFp){
		spip_log("TAXREF : Echec ouverture de ".$catalogue, "biodiv."._LOG_INFO_IMPORTANTE);
		if(is_readable($catalogue))
			spip_log("TAXREF : ... mais le fichier ".$catalogue. " est accessible en lecture", "biodiv."._LOG_INFO_IMPORTANTE);
		return(-10);
	}
	// Lecture de la ligne d'entete
	$lHeader = fgetcsv($lFp,_CSV_LINESIZE,$lDelimiter);
	$group1 = null; $group2 = null;
	
	// On traite tout le fichier
	while($taxon=fgetcsv($lFp,_CSV_LINESIZE,$lDelimiter)) {
		if(!$group1) {
			$group1 = $taxon[_GROUP1];
			$group2 = $taxon[_GROUP2];
		}
		// On ne s'intéresse qu'aux espèces connues
		if(array_key_exists($taxon[_NOM_BINOMIAL],$especes)) {
			$lbnom = $taxon[_NOM_BINOMIAL];
			$taxons[] = array(
				// 'binomial' => $lbnom,
				'nom' => $lbnom,
				// 'cd_nom' => $taxon[_CD_NOM],
				'id_taxref' => $taxon[_CD_NOM],
				'cd_ref' => $taxon[_CD_REF],
				'regne'  => $taxon[_REGNE],
				'group1' => $taxon[_GROUP1],
				'group2' => $taxon[_GROUP2],
				'rang'   => $taxon[_RANG],
				'id_espece' => $especes[$lbnom]['id_article'],
				);
			// spip_log("TAXREF: taxon '$lbnom' cd_nom ".$taxon[_CD_NOM]." règne ".$taxon[_REGNE], "biodiv."._LOG_INFO_IMPORTANTE);
		}
	}
	fclose($lFp);
	//spip_log("TAXREF : fragment ".$from['fragment'].", ".count($taxons)." taxons trouves ", "biodiv."._LOG_INFO_IMPORTANTE);
	
	// on a tout lu : réinitiaisation
	$from['ligne']=0;
	if($from['fragment'] == _TAXREF_FRAGMENTS) {
		$from['fragment'] = 1;
		$from['reset'] = date("Y-m-d H:i:s");
		$from['group1'] = $group1;
		$from['group2'] = $group2;
		spip_log("TAXREF : Catalogue entierement lu - nouvelle itération ", "biodiv."._LOG_INFO_IMPORTANTE);
		ecrire_meta('taxref_debut',serialize($from));
		return(1);
	} else {
		spip_log("TAXREF : Fragment lu ".$catalogue, "biodiv");
		$from['fragment']++;
		$from['group1'] = $group1;
		$from['group2'] = $group2;
		ecrire_meta('taxref_debut',serialize($from));
		return(-10);
	}	
}

/**
 * Mise à jour de la table des taxons
 *
 * Met à jour la table spip_taxrefs sur la base du tableau de taxon fourni.
 * Le taxon est créé ou mis à jour suivant le cas.
 *
 * @param array $taxons		Tableau des taxons à insérer
 * @return void			
 */

function taxref_insert($taxons) {
	foreach($taxons as $entry) {
		sql_replace('spip_taxrefs',$entry);
	}
}

/**
 * Utilitaire de debug.
 * @ignore
 */
function taxref_dump($taxons) {
	file_put_contents(_DIR_CACHE . 'taxons.txt', print_r($taxons,true));
}

?>
