<?php
/*******************************************************************************\
 * BIODIV, plugin et squelette pour SPIP - https://www.spip.net/               *
 *         dédié à la gestion d'observations naturalistes                      *
 *                                                                             *
 *         Copyright (C) 2008-2024 Renaud LAURETTE                             *
 *                                                                             *
 * BIODIV a été développé initialement pour le projet Biodiv.Balma de l'APCVEB *
 * (Association de Protection du Cadre de Vie et de l'Environnement balmanais) * 
 *     voir Biodiv.Balma : https://balma.biodiv.fr/                            *
 *     voir APCVEB : https://apcveb.fr/                                        *
 *                                                                             *
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.         *
 *  Pour plus de détails voir les fichier COPYING.txt et LICENCE-BIODIV.md     *
\*******************************************************************************/


include_spip('inc/autoriser');
include_spip('inc/actions');
include_spip('inc/editer');
include_spip('biodiv_fonctions');

/**
 * Traduction des opérateurs pour éviter les caractères spéciaux 
 * dans le JSON et dans les formulaires
 */
function _convertOp($op) {
	switch($op) {
	case 'lt': return '<';
	case 'gt': return '>';
	case 'eq': return '=';
	case 'ne': return '!=';
	default: return($op);
	}
}

/**
 * Construction de la clause WHERE
 *
 * Un $critere est un objet contenant, pour un champ du formulaire, toutes les informations
 * nécessaire à la génération de la partie de la clause where le concernant
 *
 * @param {array} $params		Les paramètres du formulaire
 * @param {array} $criteres		Tables de criteres
 * @param {array} &$desc		(retour) Tableau des critères en forme lisible par un utilisateur
 */
function clause_where($params,$criteres, &$desc,&$champs)
{
	$where = array();
	$tables = array();
	foreach($criteres as $ct) {
		$obj = $ct['objet'];
		$prefix = $ct['prefix'];
		$pattern = $ct['desc'];
		$dbfields = $ct['mapping'];
		
		foreach($ct['champs'] as $key => $value) {
			$pkey = $prefix . "_" . $key;
			$dbf = array_key_exists($key,$dbfields) ? $dbfields[$key] : $key;
			
			if(array_key_exists($pkey,$params)) {
				$champs[] = $pkey;
				$crit = trim($params[$pkey]);
					switch($value) {
					case 'EQUAL':
						if(strlen($crit) >0) {
							$where[]= "$prefix.$dbf=" . sql_quote($crit);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " = '$crit'";
							$tables[$prefix]=1;
						}
						break;
					case 'EQUAL-INT':
						if(strlen($crit) >0) {
							$where[]= "$prefix.$dbf=" . intval($crit);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " = ".intval($crit);
							$tables[$prefix]=1;
						}
						break;
					case 'SAME-MONTH':
						if(strlen($crit) >0) {
							$where[]= "$prefix.$dbf REGEXP " . sql_quote("^[0-2][0-9][0-9][0-9]-$crit");
							$desc[$obj][] = _T(sprintf($pattern,$dbf)). " mois = '$crit'";
							$tables[$prefix]=1;
						}
						break;
					case 'LIKE':
						if(strlen($crit) >0) {
							$where[]= "$prefix.$dbf LIKE " . sql_quote($crit);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " contient '$crit'";
							$tables[$prefix]=1;
						}
						break;
					case 'COMPARE':
						$kop = $pkey.'_op';
						if(array_key_exists($kop,$params) && (strlen($crit)>0)) {
							$where[] = "$prefix.$dbf "._convertOp($params[$kop])." " . sql_quote($params[$pkey]);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " "._convertOp($params[$kop])." '$params[$pkey]'";
							$tables[$prefix]=1;
						}
						break;
					case 'COMPARE-INT':
						$kop = $pkey.'_op';
						if(array_key_exists($kop,$params) && (strlen($crit)>0)) {
							$where[] = "$prefix.$dbf "._convertOp($params[$kop])." " . intval($params[$pkey]);
							$desc[$obj][] = _T(sprintf($pattern,$key)). "  "._convertOp($params[$kop])." ". intval($params[$pkey]);
							$tables[$prefix]=1;
						}
						break;
					case 'INTERVAL':
						$kfrom = $pkey.'_from';
						$kto = $pkey.'_to';
						if(array_key_exists($kfrom,$params) && (strlen($params[$kfrom])>0)) {
							$where[] = "$prefix.$dbf >= " .sql_quote($params[$kfrom]);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " >= '$params[$kfrom]'";
							$tables[$prefix]=1;
						}
						if(array_key_exists($kto,$params) && (strlen($params[$kto])>0)) {
							$where[] = "$prefix.$dbf <= " . sql_quote($params[$kto]);
							$desc[$obj][] = _T(sprintf($pattern,$key)). " <= '$params[$kto]'";
							$tables[$prefix]=1;
						}
						break;
					default:
						break;
					}
				}
			}
	}
	return array($tables,$where);
}

function verbose($clause) {
	$text = "";
	foreach($clause as $value) {
		if(strlen($text) !=0) $text .= " AND ";
		$text .= $value;
	}
	return $text;
}


/**
 * Construction de la clause FROM
 * Identification des tables et de leurs synonymes. Génération des clauses de jointure
 *
 * @param {array} $criteres		Données associées à chaque critère (voir clause_where())
 * @param {array} $tables		Liste des tables impliquées
 * @return list($p,$f,$w) 		$p : prefixe de la table observation
 *								$f : clause FROM
 *								$w : clause WHERE liée aux jointures
 */
function clause_from($criteres,$tables) {
	$from = $where = array();
	$p = "";
	// Récupération des jointures indirectes
	// uniquement quand la table est utilisée par la recherche
	foreach($criteres as $ct) {
		$pfx = $ct['prefix'];
		if(isset($tables[$pfx])) {
			foreach($ct['join'] as $join) $tables[$join]=1;
		}
	}
	// Récupération des tables et des jointures
	foreach($criteres as $ct) {
		$pfx = $ct['prefix'];		
		if($ct['table'] == 'spip_biodiv_observations') {
			$from[] = $ct['table'] . " AS `$pfx`";
			$p= $pfx;
		} else {
			if(isset($tables[$pfx])) {
				$from[] = $ct['table'] . " AS `$pfx`";
				$where = array_merge($where,$ct['where']);
			}
		}
	}
	return array($p,$from,$where);
}


//
// Fonction CVT
//

function formulaires_rechercher_observation_charger($rqid=null)
{

	// On crée le repertoire de gestion des requêtes s'il n'existe pas déjà
	$repertoire = _DIR_TMP . 'requetes';
	if(!is_dir($repertoire)) {
		if(!file_exists($repertoire)) {
			spip_log("RequeteObservations: création du répertoire '$repertoire'","biodiv."._LOG_INFO_IMPORTANTE);
			mkdir($repertoire);
		} else {
			spip_log("RequeteObservations: Un fichier '$repertoire' existe: création ipossible du répertoire.","biodiv."._LOG_INFO_IMPORTANTE);
			return false;
		}
	} else {
		spip_log("RequeteObservations: Le répertoire '$repertoire' existe bien.","biodiv."._LOG_INFO_IMPORTANTE);
	}
	
	// On nettoie le dossier des requetes
	// ayant plus de $delai heures
	$delai = 24;
	$contenu = array_diff(scandir($repertoire),array(".",".."));
	foreach($contenu as $entry) {
		$file = $repertoire . '/' . $entry;
		$stat = stat($file);
		if($stat['mtime'] < time() - ($delai*60*60)) {
			unlink($file);
		}
	}	

	// Initialisation	
	$valeurs = array();
	if(is_numeric($rqid)&&($rqid == intval($rqid))) {
		// Si le paramètre est un entier, on vérifie si c'est un docid de requete
		// dont les crédits correspondent à l'utilisateur
		$autdata = sql_fetsel(array('nom'),'spip_auteurs',"id_auteur=".intval(session_get('id_auteur')));
		$rqmeta = sql_fetsel('*', 'spip_documents', 
						array(
							"id_document=".intval($rqid),
							"credits=".sql_quote($autdata['nom']),
							"extension='requete'"
						)
					);
		
		if(isset($rqmeta['fichier'])) { // On a trouvé un document
			$file = _DIR_IMG . $rqmeta['fichier'];
			if(file_exists($file)) { // Son fichier existe
				$rqtxt = file_get_contents($file);
				$rqdata = json_decode($rqtxt, false); // objets en objets
				if(is_object($rqdata->formulaire)) { // la requête contient des données de formulaire
					spip_log("RequeteObservations: Initialisation du formulaire depuis '$file'.","biodiv."._LOG_INFO_IMPORTANTE);
					//spip_log("RequeteObservations: Formulaire : ".print_r($rqdata->formulaire,true),"biodiv."._LOG_INFO_IMPORTANTE);
				    foreach((array)($rqdata->formulaire) as $key => $value) {
						if(isset($value) && (strlen((string)$value)>0)) $valeurs[$key] = $value;
					}
				}
				if(is_object($rqdata->perimetre)) {
					$valeurs['dumpy'] = json_encode($rqdata->perimetre);
				}
			}
		}
	}
	spip_log("RequeteObservations: Initialisations : ".print_r($valeurs,true),"biodiv."._LOG_INFO_IMPORTANTE);
	return $valeurs;
}

function formulaires_rechercher_observation_verifier($rqid=null)
{
	$erreurs = array();
	spip_log("RequeteObservations:Début vérification.","biodiv."._LOG_INFO_IMPORTANTE);
	
	// champs de recherche pour une observation
	$obs_criteria = array(
		'titre'		=> 'LIKE',
		'type_obs'	=> 'EQUAL',
		'espece'	=> 'LIKE',
		'quantite'	=> 'COMPARE-INT',
		'denombrement' => 'EQUAL',		
		'date_obs'	=> 'INTERVAL',
		'mois'      => 'SAME-MONTH',
		'pjour'		=> 'EQUAL',
		'id_source' => 'EQUAL',
		'descriptif' => 'LIKE',
		'adresse'	=> 'LIKE',
		'commune'	=> 'LIKE',
		'insee'		=> 'EQUAL',
		'discret'	=> 'EQUAL'
		);
	
	// Correspondances non-canoniques entre champs de recherche et champ de la base
	$obs_mapping = array(
		'mois' => 'date_obs'
		);
	
	// table auteurs_liens
	$aut_criteria = array (
		'id_auteur'	=> 'EQUAL'
		);
	
	// table article pour les fiches espece
	$esp_criteria = array (
		'titre' => 'LIKE',
		'soustitre' => 'LIKE'
		);
	
	// table rubrique pour les groupes d'espèces
	$rub_criteria = array(
		'titre' => 'LIKE'
		);		
	
	// table taxons pour la classification INPN
	$tax_criteria = array(
		'regne' => 'LIKE',
		'group1' => 'LIKE',
		'group2' => 'LIKE'
		);	
	
	// tous les champs de recherche
	$criteres = array(
		array(
			'objet' => 'Observation',
			'table' => 'spip_biodiv_observations',
			'champs' => $obs_criteria,
			'mapping' => $obs_mapping,
			'prefix' => 'o', // prefix affecté aux champs de l'objet dans le formulaire
			'desc' => 'observation:champ_%s_label',
			'join' => array(),
			),
		array(
			'objet' => 'Observateur',
			'table' => 'spip_auteurs_liens',
			'champs' => $aut_criteria,
			'mapping' => [],
			'prefix' => 'a',
			'desc' => 'observation:champ_%s_label',
			'join' => array('o'), 	// prefixes des tables avec lesquelles doivent être faites les jointures
			'where' => array(		// clauses de jointure (combinées par AND)
						'a.id_objet = o.id_observation',
						"a.objet='observation'"
						)
			),	
		array(
			'objet' => 'Fiche esp&egrave;ce',
			'table' => 'spip_articles',
			'champs' => $esp_criteria,
			'mapping' => [],
			'prefix' => 'e',
			'desc' => 'espece:champ_%s_label',
			'join' => array('o'),
			'where' => array(
					"e.composition='espece'",
					'e.id_article = o.id_espece'
					)
			),
		array(
			'objet' => 'Groupe',
			'table' => 'spip_rubriques',
			'champs' => $rub_criteria,
			'mapping' => [],
			'prefix' => 'r',
			'desc' => 'espece:champ_%s_groupe',
			'join' => array('e'),
			'where' => array(
				"r.composition='espece'",
				'(r.id_rubrique=e.id_rubrique OR r.id_rubrique=e.id_secteur)',
				)
			),
		array(
			'objet' => 'Taxon',
			'table' => 'spip_taxrefs',
			'champs' => $tax_criteria,
			'mapping' => [],
			'prefix' => 't',
			'desc' => 'espece:champ_%s_taxon',
			'join' => array('e'),
			'where' => array(
				't.id_espece=e.id_article',
				)
			)
		);
	
	$desc = $tables = $champs = array();
	list($tables,$where1) = clause_where($_REQUEST,$criteres, $desc, $champs);
	list($opx,$from,$where2) = clause_from($criteres,$tables);
	// On pousse les constructions dans la globale pour les retrouver lors du traitement
	$_REQUEST['w_observation'] = array_merge($where1,$where2);
	$_REQUEST['f_observation'] = $from;
	$_REQUEST['p_observation'] = $opx;
	$_REQUEST['v_observation'] = $desc;
	$champs_extra = array('o_date_obs_from','o_date_obs_to','o_quantite_op');
	$_REQUEST['rq_champs'] = array_merge($champs,$champs_extra);
	
	spip_log("RequeteObservations: Vérification faite.","biodiv."._LOG_INFO_IMPORTANTE);
	return $erreurs;
}

function formulaires_rechercher_observation_traiter($rqid=null)
{	
	$resultat = array();
	
	$requete['requete'] = $_REQUEST['v_observation'];
	
	// Construction du FROM
	$opx = $_REQUEST['p_observation'];
	$from = $_REQUEST['f_observation'];
	
	// construction du SELECT
	$what = array("$opx.id_observation","$opx.lat","$opx.lng");	
	
	// Construction de la clause WHERE : critères saisis ...
	$where = $_REQUEST['w_observation'];
	// ... autorisations différées : 
	// seront à appliquer au moment de l'appel de la requête
	$where[] = "$opx.discret IN (:discretion:)";	
	
	// On construit la bounding box si l'utilisateur a défini un périmètre
	$requete['perimetre'] = json_decode(_request('dumpy'));
	if(is_object($requete['perimetre'])) {
			$polygon = $requete['perimetre']->geometry->coordinates[0];
			$minlng = 180; $maxlng=-180;
			$minlat=90; $maxlat =-90;
			foreach($polygon as $vertex) {
				$minlng = ($minlng > $vertex[0]) ? $vertex[0] : $minlng;
				$minlat = ($minlat > $vertex[1]) ? $vertex[1] : $minlat;
				$maxlng = ($maxlng < $vertex[0]) ? $vertex[0] : $maxlng;
				$maxlat = ($maxlat < $vertex[1]) ? $vertex[1] : $maxlat;
			}
			$requete['perimetre']->bbox = array($minlng, $minlat, $maxlng, $maxlat);
			// ... bounding box du polygone ...
			$where[] = "$opx.lat <= $maxlat";
			$where[] = "$opx.lat >= $minlat";
			$where[] = "$opx.lng <= $maxlng";
			$where[] = "$opx.lng >= $minlng";
	}
	
	// Aggrégation de la requête
	$requete['sql'] = array(
		'select' => $what,
		'from'=> $from,
		'where' => $where,
		'eval' => array(
			'discretion' => 'discretion_auteur'
			)
		);
	
	// NOUVEAU - On capture les champs du formulaire
	$formu = array();
	foreach($_REQUEST as $key => $val) {
		if(in_array($key,$_REQUEST['rq_champs'])) {
			$formu[$key] = $val;
		}
	}
	$requete['formulaire'] = $formu;		
		
	
	// on génère le fichier
	$repertoire = _DIR_TMP . "requetes/";
	$fichier = md5(time());
	$file = $repertoire . $fichier . ".json";
	spip_log("RequeteObservations: Génération du fichier de requête '$file'.","biodiv."._LOG_INFO_IMPORTANTE);
	
	$fp = fopen($file,"w");
	if($fp) {
		// ecrire le fichier contenant la requête
		fwrite($fp,json_encode($requete,JSON_PRETTY_PRINT));
		fclose($fp);
		spip_log("RequeteObservations: Requête '$file' sauvegardée.","biodiv."._LOG_INFO_IMPORTANTE);
		$resultat['message_ok'] = $requete->titre;
		// Rediriger vers la page 'observation_resultat' qui exécutera la requête
		$resultat['redirect'] = generer_url_public('observation_resultat',array('rq'=>$fichier),false, false);		
	} else {
		$resultat['message_erreur'] = "Echec cr&eacute;ation du fichier requ&ecirc;te :" . $requete->titre;
		spip_log("RequeteObservations: Echec de création du fichier requête '$file'.","biodiv."._LOG_INFO_IMPORTANTE);
	}

	// On retourne le message de résultat 
	return $resultat;
}

?>
