<?php
/**
 * @version $Id$
 * @author Thomas Crespin <thomas.crespin@sesamath.net>
 * @copyright Thomas Crespin 2009-2022
 * 
 * ****************************************************************************************************
 * SACoche <https://sacoche.sesamath.net> - Suivi d’Acquisitions de Compétences
 * © Thomas Crespin pour Sésamath <https://www.sesamath.net> - Tous droits réservés.
 * Logiciel placé sous la licence libre Affero GPL 3 <https://www.gnu.org/licenses/agpl-3.0.html>.
 * ****************************************************************************************************
 * 
 * Ce fichier est une partie de SACoche.
 * 
 * SACoche est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant les termes 
 * de la “GNU Affero General Public License” telle que publiée par la Free Software Foundation :
 * soit la version 3 de cette licence, soit (à votre gré) toute version ultérieure.
 * 
 * SACoche est distribué dans l’espoir qu’il vous sera utile, mais SANS AUCUNE GARANTIE :
 * sans même la garantie implicite de COMMERCIALISABILITÉ ni d’ADÉQUATION À UN OBJECTIF PARTICULIER.
 * Consultez la Licence Publique Générale GNU Affero pour plus de détails.
 * 
 * Vous devriez avoir reçu une copie de la Licence Publique Générale GNU Affero avec SACoche ;
 * si ce n’est pas le cas, consultez : <http://www.gnu.org/licenses/>.
 * 
 */

if(!defined('SACoche')) {exit('Ce fichier ne peut être appelé directement !');}
if($_SESSION['SESAMATH_ID']==ID_DEMO){Json::end( FALSE , 'Action désactivée pour la démo.' );}

$action  = Clean::post('f_action' , 'texte');
$section = Clean::post('f_section', 'texte');
$objet   = Clean::post('f_objet'  , 'texte');

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Voir le contenu d’un fichier déjà importé
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action=='voir_import') && $objet )
{
  $tab_fichier = DB_STRUCTURE_SIECLE::DB_recuperer_import_contenu($objet);
  if(empty($tab_fichier))
  {
    Json::end( FALSE , 'Fichier introuvable ! Objet transmis ("'.html($objet).'") correct ?' );
  }
  Json::end( TRUE , print_r($tab_fichier,TRUE) );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Récupération des autres valeurs transmises sinon
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if( ($action!='recolter') && ($action!='generer_export') )
{
  Json::end( FALSE , 'Erreur avec les données transmises !' );
}

$PAGE_REF   = Clean::post('f_page_ref'  , 'texte');
$periode    = Clean::post('f_periode'   , 'texte');
$classe_id  = Clean::post('f_classe'    , 'entier');
$groupe_nom = Clean::post('f_groupe_nom', 'texte');
$tab_eleve  = Clean::post('f_eleve'   , array('array',','));
$tab_eleve  = array_filter( Clean::map('entier',$tab_eleve) , 'positif' );

// Tableaux communs utiles

$tab_periode_livret = array(
  'periode21' => 'Semestre 1/2' ,
  'periode22' => 'Semestre 2/2' ,
  'periode31' => 'Trimestre 1/3',
  'periode32' => 'Trimestre 2/3',
  'periode33' => 'Trimestre 3/3',
  'periode41' => 'Bimestre 1/4' ,
  'periode42' => 'Bimestre 2/4' ,
  'periode43' => 'Bimestre 3/4' ,
  'periode44' => 'Bimestre 4/4' ,
  'periode51' => 'Période 1/5'  ,
  'periode52' => 'Période 2/5'  ,
  'periode53' => 'Période 3/5'  ,
  'periode54' => 'Période 4/5'  ,
  'periode55' => 'Période 5/5'  ,
  'cycle0'    => 'Fin de cycle' ,
);

$tab_export_type = array(
  'cycle1' => 'maternelle',
  'cp'     => 'ecole',
  'ce1'    => 'ecole',
  'ce2'    => 'ecole',
  'cycle2' => 'ecole',
  'cm1'    => 'ecole',
  'cm2'    => 'ecole',
  '6e'     => 'college',
  'cycle3' => 'college',
  '5e'     => 'college',
  '4e'     => 'college',
  '3e'     => 'college',
  'cycle4' => 'college',
);

// $xsd_schema_version_ecole   = (version_compare(TODAY_SQL,'2024-09-01','>=')) ? '7.0' : '6.0' ;
// $xsd_schema_version_college = (version_compare(TODAY_SQL,'2024-09-01','>=')) ? '8.0' : '7.0' ;
$xsd_schema_version_ecole   = '9.0' ;
$xsd_schema_version_college = '12.0' ;

$tab_xml_param = array(
  'ecole' => array(
    'schema'   => 'xsi:schemaLocation="urn:fr:edu:scolarite:lsun:bilans:import import-bilan-1d.xsd" schemaVersion="'.$xsd_schema_version_ecole.'"',
    'file_xsd' => 'livret-scolaire_1d_schema_'.$xsd_schema_version_ecole.'.xsd',
  ),
  'college' => array(
    'schema'   => 'xsi:schemaLocation="urn:fr:edu:scolarite:lsun:bilans:import import-bilan-complet.xsd" schemaVersion="'.$xsd_schema_version_college.'"',
    'file_xsd' => 'livret-scolaire_2d_schema_'.$xsd_schema_version_college.'.xsd',
  ),
);

// Vérification période

if( !isset($tab_periode_livret[$periode]) && ( ($action=='recolter') || !empty($periode) ) )
{
  Json::end( FALSE , 'Période "'.html($periode).'" inconnue !' );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Récolter les données pour les élèves d’une classes et d’une période
// ////////////////////////////////////////////////////////////////////////////////////////////////////

if($action=='recolter')
{
  $periode_nom = $tab_periode_livret[$periode];
  require(CHEMIN_DOSSIER_INCLUDE.'code_livret_recolter.php');
  // Normalement, on est stoppé avant.
  Json::end( FALSE , 'Problème de code : point d’arrêt manquant !' );
}

// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Générer le fichier d’export en fonction des critères sélectionnés
// ////////////////////////////////////////////////////////////////////////////////////////////////////

// Fermeture de session (mais pas destruction, juste écriture et libération des données pour éviter un verrouillage en écriture)
Session::write_close();

// Elèves
if( empty($tab_eleve) )
{
  Json::end( FALSE , 'Erreur avec les données transmises !' );
}
$liste_eleve_id = implode(',',$tab_eleve);

// Période
if(substr($periode,0,7)=='periode')
{
  $PAGE_PERIODICITE = 'periode';
  $JOINTURE_PERIODE = substr($periode,7);
}
else
{
  $PAGE_PERIODICITE = ($periode!=='') ? 'cycle' : '' ;
  $JOINTURE_PERIODE = '0';
}

// Au cas où...
Erreur500::prevention_et_gestion_erreurs_fatales( TRUE /*memory*/ , TRUE /*time*/ );

// Données
$DB_TAB = DB_STRUCTURE_LIVRET::DB_lister_livret_export_eleves( $liste_eleve_id , $PAGE_PERIODICITE , $JOINTURE_PERIODE );
if( empty($DB_TAB) )
{
  Json::end( FALSE , 'Aucun bilan trouvé selon les critères transmis !' );
}

// On extrait les données élèves et on mutualise les données communes
$tab_export_objet   = array();
$tab_export_eleve   = array();
$tab_export_donnees = array();
$tab_export_commun  = array(
  'periode'          => array(),
  'directeur'        => array(), // 1D
  'classe'           => array(), // 1D
  'responsable-etab' => array(), // 2D
  'discipline'       => array(), // 2D
  'enseignant'       => array(),
  'element'          => array(),
  'crcn'             => array(),
  'epi-thematique'   => array(), // 2D
  'epi'              => array(), // 2D
  'ap'               => array(), // 2D
  'parcours'         => array(),
  'viesco'           => array(), // 2D
);

foreach($DB_TAB as $key => $DB_ROW)
{
  $export_objet = $tab_export_type[$DB_ROW['livret_page_ref']];
  $tab_export_objet[ $export_objet ] = TRUE;
  $tab_export_donnees[$key] = json_decode($DB_ROW['export_contenu'], TRUE);
  $tab_export_eleve[$DB_ROW['user_id']] = $tab_export_donnees[$key]['eleve'];
  if($export_objet=='college')
  {
    $tab_export_commun['responsable-etab'] = array_merge( $tab_export_commun['responsable-etab'] , $tab_export_donnees[$key]['commun']['responsable-etab'] );
    $tab_export_commun['epi']              = array_merge( $tab_export_commun['epi']              , $tab_export_donnees[$key]['commun']['epi']              );
    $tab_export_commun['ap']               = array_merge( $tab_export_commun['ap']               , $tab_export_donnees[$key]['commun']['ap']               );
    $tab_export_commun['viesco']           = array_merge( $tab_export_commun['viesco']           , $tab_export_donnees[$key]['commun']['viesco']           );
    $tab_export_commun['discipline']       = array_merge( $tab_export_commun['discipline']       , $tab_export_donnees[$key]['commun']['discipline']       );
  }
  if($export_objet=='ecole')
  {
    $tab_export_commun['directeur']        = array_merge( $tab_export_commun['directeur']        , $tab_export_donnees[$key]['commun']['directeur']        );
    $tab_export_commun['classe']           = array_merge( $tab_export_commun['classe']           , $tab_export_donnees[$key]['commun']['classe']           );
  }
  $tab_export_commun['periode']            = array_merge( $tab_export_commun['periode']          , $tab_export_donnees[$key]['commun']['periode']          );
  $tab_export_commun['enseignant']         = array_merge( $tab_export_commun['enseignant']       , $tab_export_donnees[$key]['commun']['enseignant']       );
  $tab_export_commun['element']            = array_merge( $tab_export_commun['element']          , $tab_export_donnees[$key]['commun']['element']          );
  $tab_export_commun['crcn']               = array_merge( $tab_export_commun['crcn']             , $tab_export_donnees[$key]['commun']['crcn']             );
  $tab_export_commun['parcours']           = array_merge( $tab_export_commun['parcours']         , $tab_export_donnees[$key]['commun']['parcours']         );
  unset( $tab_export_donnees[$key]['eleve'] , $tab_export_donnees[$key]['commun'] );
}

if( isset($tab_export_objet['cycle_maternelle']) )
{
  Json::end( FALSE , 'L’application nationale ne gère pas le bilan de maternelle (qui ne fait pas partie du Livret Scolaire) !' );
}

if( count($tab_export_objet) > 1 )
{
  Json::end( FALSE , 'Il est impossible d’exporter dans un fichier unique des bilans de différents formats('.implode(' / ',array_keys($tab_export_objet)).') !' );
}

$export_objet = key($tab_export_objet);

// ////////////////////////////////////////////////////////////////////////////////////////////////////
/* 
 * Libérer de la place mémoire car les scripts de bilans sont assez gourmands.
 * Supprimer $DB_TAB ne fonctionne pas si on ne force pas auparavant la fermeture de la connexion.
 * SebR devrait peut-être envisager d’ajouter une méthode qui libère cette mémoire, si c’est possible...
 */
// ////////////////////////////////////////////////////////////////////////////////////////////////////

DB::close(SACOCHE_STRUCTURE_BD_NAME);
unset($DB_TAB);

// On attaque le XML : prologue + élément racine

$tab_xml = array();
$tab_xml[] = '<?xml version="1.0" encoding="UTF-8"?>';
$tab_xml[] = '<lsun-bilans xmlns="urn:fr:edu:scolarite:lsun:bilans:import" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '.$tab_xml_param[$export_objet]['schema'].'>';
// entête
$tab_xml[] = ' <entete>';
$tab_xml[] = '  <editeur>Sésamath</editeur>';
$tab_xml[] = '  <application>SACoche</application>';
$tab_xml[] = '  <appliVersion>'.VERSION_PROG.'</appliVersion>';
$tab_xml[] = '  <etablissement>'.html($_SESSION['WEBMESTRE_UAI']).'</etablissement>';
$tab_xml[] = ' </entete>';
$tab_xml[] = ' <donnees>';

// Attention, pour la conformité du XML, l’odre est important (j'aurais bien mis <eleves> en premier mais ce n’est pas permis).

$key_classe        = ($export_objet=='college') ? 'code-division'        : 'classe-ref' ;
$key_profs_eleve   = ($export_objet=='college') ? 'prof-princ-refs'      : 'enseignant-refs' ;
$key_chef_etabl    = ($export_objet=='college') ? 'responsable-etab-ref' : 'directeur-ref' ;
$key_appr_synthese = ($export_objet=='college') ? 'acquis-conseils'      : 'appreciation-generale' ;

// responsables-etab (2D)
if($export_objet=='college')
{
  $tab_xml[] = '  <responsables-etab>';
  foreach($tab_export_commun['responsable-etab'] as $id => $tab)
  {
    $tab_xml[] = '   <responsable-etab id="'.$id.'" libelle="'.html($tab['libelle']).'" />';
  }
  $tab_xml[] = '  </responsables-etab>';
}

// directeurs & classes (1D)
if($export_objet=='ecole')
{
  $tab_xml[] = '  <directeurs>';
  foreach($tab_export_commun['directeur'] as $id => $tab)
  {
    $tab_xml[] = '   <directeur id="'.$id.'" civilite="'.$tab['civilite'].'" nom="'.html($tab['nom']).'" prenom="'.html($tab['prenom']).'" />';
  }
  $tab_xml[] = '  </directeurs>';
  $tab_xml[] = '  <classes>';
  foreach($tab_export_commun['classe'] as $id => $tab)
  {
    $tab_xml[] = '   <classe id="'.$id.'" id-be="'.$tab['id-be'].'" libelle="'.html($tab['libelle']).'" />';
  }
  $tab_xml[] = '  </classes>';
}

// eleves
$nb_eleves = count($tab_export_eleve);
$tab_xml[] = '  <eleves>';
foreach($tab_export_eleve as $eleve_id => $tab)
{
  switch($export_objet)
  {
    case 'college' : $tab_xml[] = '   <eleve id="'.$tab['id'].'" id-be="'.$tab['id-be'].'" nom="'.html($tab['nom']).'" prenom="'.html($tab['prenom']).'" '.$key_classe.'="'.html($tab[$key_classe]).'" />'; break;
    case 'ecole'   : $tab_xml[] = '   <eleve id="'.$tab['id'].'" ine="'.$tab['ine'].'" '.$key_classe.'="'.html($tab[$key_classe]).'" nom="'.html($tab['nom']).'" prenom="'.html($tab['prenom']).'" />'; break;
  }
}
$tab_xml[] = '  </eleves>';
unset( $tab_export_eleve );

// periodes (uniquement si présence de bilans périodiques)
if(!empty($tab_export_commun['periode']))
{
  $tab_xml[] = '  <periodes>';
  foreach($tab_export_commun['periode'] as $id => $tab)
  {
    $date_debut = ($export_objet=='college') ? '' : ' date-debut="'.$tab['date-debut'].'"' ;
    $date_fin   = ($export_objet=='college') ? '' : ' date-fin="'.$tab['date-fin'].'"' ;
    $tab_xml[] = '   <periode id="'.$id.'" millesime="'.$tab['millesime'].'" indice="'.$tab['indice'].'" nb-periodes="'.$tab['nb-periodes'].'"'.$date_debut.$date_fin.' />';
  }
  $tab_xml[] = '  </periodes>';
}

// disciplines (2D & uniquement si présence de bilans périodiques)
if(!empty($tab_export_commun['discipline']))
{
  $tab_xml[] = '  <disciplines>';
  foreach($tab_export_commun['discipline'] as $id => $tab)
  {
    $id_lv = empty($tab['id-lv']) ? '' : ' id-lv="'.$tab['id-lv'].'"' ;
    $tab_xml[] = '   <discipline id="'.$id.'" code="'.$tab['code'].'" modalite-election="'.$tab['modalite-election'].'" libelle="'.html($tab['libelle']).'"'.$id_lv.' />';
  }
  $tab_xml[] = '  </disciplines>';
}

// enseignants
if(!empty($tab_export_commun['enseignant']))
{
  $tab_xml[] = '  <enseignants>';
  foreach($tab_export_commun['enseignant'] as $id => $tab)
  {
    $civilite = $tab['civilite'] ? ' civilite="'.$tab['civilite'].'"' : '' ;
    switch($export_objet)
    {
      case 'college' : $tab_xml[] = '   <enseignant id="'.$id.'" type="'.$tab['type'].'" id-sts="'.$tab['id-sts'].'"'.$civilite.' nom="'.html($tab['nom']).'" prenom="'.html($tab['prenom']).'" />'; break;
      case 'ecole'   : $tab_xml[] = '   <enseignant id="'.$id.'"'.$civilite.' nom="'.html($tab['nom']).'" prenom="'.html($tab['prenom']).'" />'; break;
    }
  }
  $tab_xml[] = '  </enseignants>';
}

// elements-programme (facultatif + uniquement si présence de bilans périodiques)
if(!empty($tab_export_commun['element']))
{
  $tab_xml[] = '  <elements-programme>';
  foreach($tab_export_commun['element'] as $id => $libelle)
  {
    $tab_xml[] = '   <element-programme id="'.$id.'" libelle="'.html($libelle).'" />';
  }
  $tab_xml[] = '  </elements-programme>';
}

// competences-numeriques-communs (appréciation sur la classe) (facultatif + uniquement si dernier bilan périodique de cm2 ou 6e)
if(!empty($tab_export_commun['crcn']))
{
  $tab_xml[] = '  <competences-numeriques-communs>';
  foreach($tab_export_commun['crcn'] as $key => $tab)
  {
    $classe_structure = ($export_objet=='college') ? ' code-structure="'.html($tab['code-structure']).'" type-structure="'.$tab['type-structure'].'"' : '' ;
    $tab_xml[] = '   <competences-numeriques-commun id="'.$key.'"'.$classe_structure.'>'.html($tab['appreciation']).'</competences-numeriques-commun>';
  }
  $tab_xml[] = '  </competences-numeriques-communs>';
}

// parcours-communs (appréciation sur la classe) (facultatif + uniquement si présence de bilans périodiques)
if(!empty($tab_export_commun['parcours']))
{
  $tab_parcours = array();
  foreach($tab_export_commun['parcours'] as $key => $tab)
  {
    $tab_parcours[$tab['periode-ref'].'¤'.$tab[$key_classe]][] = $tab;
  }
  $tab_xml[] = '  <parcours-communs>';
  foreach($tab_parcours as $key => $tab_ensemble)
  {
    list( $periode_ref , $champ_classe ) = explode('¤',$key,2);
    $tab_xml[] = '   <parcours-commun periode-ref="'.$periode_ref.'" '.$key_classe.'="'.html($champ_classe).'">';
    foreach($tab_ensemble as $tab)
    {
      $tab_xml[] = '    <parcours code="'.$tab['code'].'">'.html($tab['projet']).'</parcours>';
    }
    $tab_xml[] = '   </parcours-commun>';
  }
  $tab_xml[] = '  </parcours-communs>';
}

// vies-scolaires-communs (appréciation sur la classe) (facultatif + uniquement si présence de bilans périodiques)
// --> pas de saisie proposée dans SACoche


// epi-thematique (2D & uniquement si présence de thématiques personnalisées dans des bilans périodiques)
if(!empty($tab_export_commun['epi-thematique']))
{
  $tab_xml[] = '  <epis-thematiques>';
  foreach($tab_export_commun['epi-thematique'] as $code => $libelle)
  {
    $tab_xml[] = '   <epi-thematique code="'.$code.'" libelle="'.html($libelle).'" />';
  }
  $tab_xml[] = '  </epis-thematiques>';
}

// epis & epis-groupes (facultatif + uniquement si présence de bilans périodiques)
if(!empty($tab_export_commun['epi']))
{
  $tab_xml[] = '  <epis>';
  foreach($tab_export_commun['epi'] as $key => $tab)
  {
    $tab_xml[] = '   <epi id="'.$tab['id'].'" intitule="'.html($tab['intitule']).'" thematique="'.$tab['thematique'].'" discipline-refs="'.implode(' ',array_unique($tab['discipline-refs'])).'">';
    if($tab['description'])
    {
      $tab_xml[] = '    <description>'.html($tab['description']).'</description>';
    }
    $tab_xml[] = '   </epi>';
  }
  $tab_xml[] = '  </epis>';
    // Pas de sous-groupes gérés dans SACoche, l’EPI est défini sur une classe...
  $tab_xml[] = '  <epis-groupes>';
  foreach($tab_export_commun['epi'] as $key => $tab)
  {
    $tab_xml[] = '   <epi-groupe id="'.$key.'GR" intitule="'.html($tab['intitule']).'" epi-ref="'.$tab['id'].'">';
    // Pour le commentaire sur le groupe, on reporte la description (c’est ce que veut LSU de toutes façons).
    if($tab['description'])
    {
      $tab_xml[] = '    <commentaire>'.html($tab['description']).'</commentaire>';
    }
    $tab_xml[] = '    <enseignants-disciplines>';
    foreach($tab['discipline-refs'] as $key => $discipline_ref)
    {
      $enseignant_ref = $tab['enseignant-refs'][$key];
      $tab_xml[] = '     <enseignant-discipline discipline-ref="'.$discipline_ref.'" enseignant-ref="'.$enseignant_ref.'" />';
    }
    $tab_xml[] = '    </enseignants-disciplines>';
    $tab_xml[] = '   </epi-groupe>';
  }
  $tab_xml[] = '  </epis-groupes>';
}

// acc-persos & acc-persos-groupes (facultatif + uniquement si présence de bilans périodiques)
// AP supprimés à compter de LSU 25.5.0 diffusée aux académies le 19/01/2026 !!!
/*
if(!empty($tab_export_commun['ap']))
{
  $tab_xml[] = '  <acc-persos>';
  foreach($tab_export_commun['ap'] as $key => $tab)
  {
    $tab_xml[] = '   <acc-perso id="'.$tab['id'].'" intitule="'.html($tab['intitule']).'" discipline-refs="'.implode(' ',array_unique($tab['discipline-refs'])).'">';
    // EDIT 2019-02-02 : retiré, car sinon LSU l’affiche maintenant en double avec le commentaire sur le groupe (voir juste en dessous)...
    // if($tab['description'])
    // {
    //   $tab_xml[] = '    <description>'.html($tab['description']).'</description>';
    // }
    $tab_xml[] = '   </acc-perso>';
  }
  $tab_xml[] = '  </acc-persos>';
    // Pas de sous-groupes gérés dans SACoche, l’AP est défini sur une classe...
  $tab_xml[] = '  <acc-persos-groupes>';
  foreach($tab_export_commun['ap'] as $key => $tab)
  {
    $tab_xml[] = '   <acc-perso-groupe id="'.$key.'GR" intitule="'.html($tab['intitule']).'" acc-perso-ref="'.$tab['id'].'">';
    // Pour le commentaire sur le groupe, on reporte la description (c’est ce que veut LSU de toutes façons).
    if($tab['description'])
    {
      $tab_xml[] = '    <commentaire>'.html($tab['description']).'</commentaire>';
    }
    $tab_xml[] = '    <enseignants-disciplines>';
    foreach($tab['discipline-refs'] as $key => $discipline_ref)
    {
      $enseignant_ref = $tab['enseignant-refs'][$key];
      $tab_xml[] = '     <enseignant-discipline discipline-ref="'.$discipline_ref.'" enseignant-ref="'.$enseignant_ref.'" />';
    }
    $tab_xml[] = '    </enseignants-disciplines>';
    $tab_xml[] = '   </acc-perso-groupe>';
  }
  $tab_xml[] = '  </acc-persos-groupes>';
}
*/

unset( $tab_export_commun );

// responsables (dans une fonction car commun aux bilans périodiques et de fin de cycle)
function xml_responsables($tab_donnee_bilan_responsables)
{
  $tab_xml = array();
  $tab_xml[] = '    <responsables>';
  // A compter de LSU 18.4 on utilise un unique champ 'dénomination' et on fusionne les responsables de même adresse
  $tab_xml_responsable_adresse = array();
  foreach($tab_donnee_bilan_responsables as $resp_num => $tab)
  {
    $ligne2 = $tab['ligne2']   ? ' ligne2="'.html($tab['ligne2']).'"' : '' ;
    $ligne3 = $tab['ligne3']   ? ' ligne3="'.html($tab['ligne3']).'"' : '' ;
    $ligne4 = $tab['ligne4']   ? ' ligne4="'.html($tab['ligne4']).'"' : '' ;
    $adresse = '<adresse ligne1="'.html($tab['ligne1']).'"'.$ligne2.$ligne3.$ligne4.' code-postal="'.html($tab['code-postal']).'" commune="'.html($tab['commune']).'" />';
    $md5 = md5($adresse);
    if(!isset($tab_xml_responsable_adresse[$md5]))
    {
      $tab_xml_responsable_adresse[$md5] = array(
        'denomination' => $tab['nom'].' '.$tab['prenom'],
        'adresse'      => $adresse,
      );
    }
    else
    {
      $tab_xml_responsable_adresse[$md5]['denomination'] .= ', '.$tab['prenom'];
    }
  }
  foreach($tab_xml_responsable_adresse as $md5 => $tab)
  {
    $tab_xml[] = '     <responsable denomination="'.html($tab['denomination']).'">'; // lien-parente non géré (facultatif) ; legal peu compatible avec ce format
    $tab_xml[] = '      '.$tab['adresse'];
    $tab_xml[] = '     </responsable>';
  }
  $tab_xml[] = '    </responsables>';
  return $tab_xml;
}

// bilans périodiques ou de fin de cycle
$nb_bilans = count($tab_export_donnees);
$tab_xml_bilans = array ( 'periodique' => array() , 'cycle' => array() );
$rubrique_sans_element = FALSE ;
foreach($tab_export_donnees as $key => $tab_donnee_bilan)
{
  $tab_bilan = $tab_donnee_bilan['bilan'];
  // bilan périodique
  if($tab_bilan['type']=='periode')
  {
    $date_bilan  = ($export_objet=='college') ? 'date-conseil-classe="'.$tab_bilan['date-conseil-classe'].'"' : 'date-creation="'.$tab_bilan['date-creation'].'"' ;
    $profs_eleve_refs = !empty($tab_bilan[$key_profs_eleve]) ? ' '.$key_profs_eleve.'="'.$tab_bilan[$key_profs_eleve].'"' : '' ;
    $tab_xml_bilans['periodique'][] = '   <bilan-periodique'.$profs_eleve_refs.' eleve-ref="'.$tab_bilan['eleve-ref'].'" periode-ref="'.$tab_bilan['periode-ref'].'" '.$date_bilan.' date-scolarite="'.$tab_bilan['date-scolarite'].'" '.$key_chef_etabl.'="'.$tab_bilan[$key_chef_etabl].'">';
    $champ_position = $tab_bilan['position'];
    // liste-acquis
    $tab_xml_bilans['periodique'][] = '    <liste-acquis>';
    // 2D
    if($export_objet=='college')
    {
      foreach($tab_donnee_bilan['acquis'] as $discipline_ref => $tab)
      {
        // élément de prg travaillé obligatoire
        $element_programme_refs = !empty($tab['elements']) ? implode(' ',$tab['elements']) : 'EL0' ;
        // positionnement
        // si élève avec positionnement manquant transmettre :
        // 1 = "non noté" hors MEF 3e
        // 2 = "en attente" pour le niveau 3e (sauf dispensé)
        // 3 = "dispensé" pour le niveau 3e EPS ou LV2 uniquement
        if( is_null($tab[$champ_position]) )
        {
          $position_eleve = ' statut-evaluation-eleve="'.( ($tab_bilan['page_ref']!='3e') ? '1' : '2' ).'"';
        }
        else if($tab[$champ_position]==='DI') // sur les vieilles versions de PHP le test ( 0 == 'blablabla' ) renvoie TRUE !
        {
          $position_eleve = ' statut-evaluation-eleve="3"';
        }
        else
        {
          $position_eleve = ' '.$champ_position.'="'.$tab[$champ_position].'"';
        }
        // classe sans positionnement par paramétrage ou car positionnement élève non chiffré
        if( !$tab_bilan['moyenne_classe'] || ($champ_position=='positionnement') )
        {
          $position_classe = '';
        }
        // classe paramétrée avec positionnement mais manquant : transmettre 1 = "non noté" ou 2 = "en attente" pour le niveau 3e
        else if( is_null($tab['moyenne-structure']))
        {
          $position_classe = ' statut-evaluation-structure="'.( ($tab_bilan['page_ref']!='3e') ? '1' : '2' ).'"';
        }
        // classe paramétrée avec un positionnement qui a bien été déterminé
        else
        {
          $position_classe = ' moyenne-structure="'.$tab['moyenne-structure'].'"';
        }
        $tab_xml_bilans['periodique'][] = '     <acquis discipline-ref="'.$discipline_ref.'" enseignant-refs="'.implode(' ',$tab['profs']).'" element-programme-refs="'.$element_programme_refs.'"'.$position_eleve.$position_classe.' >';
        // appréciation obligatoire sauf si élève non noté
        if( $tab['appreciation'] || ( !is_null($tab[$champ_position]) && ($tab[$champ_position]!='DI') ) )
        {
          $tab['appreciation'] = !empty($tab['appreciation']) ? $tab['appreciation'] : '-' ;
          $tab_xml_bilans['periodique'][] = '      <appreciation>'.html($tab['appreciation']).'</appreciation>';
        }
        $rubrique_sans_element = $rubrique_sans_element || ($element_programme_refs=='EL0') ;
        $tab_xml_bilans['periodique'][] = '     </acquis>';
      }
    }
    // 1D
    if($export_objet=='ecole')
    {
      // on fait 2 passages parce que c’est compliqué sinon
      $tab_acquis_domaine = array();
      foreach($tab_donnee_bilan['acquis'] as $code => $tab)
      {
        list( $domaine , $sous_domaine ) = explode('-',$code);
        $element_programme_refs = !empty($tab['elements']) ? ' element-programme-refs="'.implode(' ',$tab['elements']).'"' : '' ;
        $positionnement = ($tab['positionnement']) ? ' positionnement="'.$tab['positionnement'].'"' : ' non-evalue="true"' ;
        if( $sous_domaine == 'RAC' )
        {
          $tab_acquis_domaine[$domaine] = array(
            'nb_sous_domaine'        => 0,
            'element_programme_refs' => $element_programme_refs,
            'positionnement'         => $positionnement,
            'appreciation'           => $tab['appreciation'],
          );
        }
        else
        {
          if(!isset($tab_acquis_domaine[$domaine]))
          {
            $tab_acquis_domaine[$domaine]['nb_sous_domaine'] = 1;
            $tab_acquis_domaine[$domaine]['nb_sous_domaine_avec_infos'] = 0;
            $tab_acquis_domaine[$domaine]['appreciation'] = $tab['appreciation'];
          }
          else
          {
            $tab_acquis_domaine[$domaine]['nb_sous_domaine'] += 1;
            $tab_acquis_domaine[$domaine]['appreciation'] .= $tab['appreciation'];
          }
          $tab_acquis_domaine[$domaine]['sous_domaine'][$sous_domaine] = array(
            'element_programme_refs' => $element_programme_refs,
            'positionnement'         => $positionnement,
          );
          if( $element_programme_refs || strpos($positionnement,'positionnement') )
          {
            $tab_acquis_domaine[$domaine]['nb_sous_domaine_avec_infos'] += 1;
          }
        }
      }
      foreach($tab_acquis_domaine as $code_domaine => $tab_info_domaine)
      {
        if($tab_info_domaine['nb_sous_domaine']==0)
        {
          $tab_xml_bilans['periodique'][] = '     <acquis code-domaine="'.$code_domaine.'-RAC"'.$tab_info_domaine['element_programme_refs'].$tab_info_domaine['positionnement'].'>';
          if($tab_info_domaine['appreciation'])
          {
            $tab_xml_bilans['periodique'][] = '      <appreciation>'.html($tab_info_domaine['appreciation']).'</appreciation>';
          }
          $tab_xml_bilans['periodique'][] = '     </acquis>';
        }
        else
        {
          $tab_xml_bilans['periodique'][] = '     <acquis code-domaine="'.$code_domaine.'-RAC">';
          if($tab_info_domaine['appreciation'])
          {
            $tab_xml_bilans['periodique'][] = '      <appreciation>'.html($tab_info_domaine['appreciation']).'</appreciation>';
          }
          if($tab_info_domaine['nb_sous_domaine'])
          {
            $tab_xml_bilans['periodique'][] = '      <sous-domaines>';
            foreach($tab_info_domaine['sous_domaine'] as $code_sous_domaine => $tab_info_sous_domaine)
            {
              // test pour éviter de passer ici en cas de domaine avec en théorie des sous-domaines mais en réalité seulement une appréciation transmise rattachée au 1er élément bien qu’il n’apparaisse pas
              // attention, il peut aussi s’agir d’un sous-domaine unique sans positionnement ni éléments de prg travaillés donc avec seulement une appréciation !
              if( $tab_info_sous_domaine['element_programme_refs'] || strpos($tab_info_sous_domaine['positionnement'],'positionnement') || ($tab_info_domaine['nb_sous_domaine']==1) || ($tab_info_domaine['nb_sous_domaine_avec_infos']==0) )
              {
                $tab_xml_bilans['periodique'][] = '       <sous-domaine code-domaine="'.$code_domaine.'-'.$code_sous_domaine.'"'.$tab_info_sous_domaine['element_programme_refs'].$tab_info_sous_domaine['positionnement'].' />';            }
            }
            $tab_xml_bilans['periodique'][] = '      </sous-domaines>';
          }
          $tab_xml_bilans['periodique'][] = '     </acquis>';
        }
      }
    }
    $tab_xml_bilans['periodique'][] = '    </liste-acquis>';
    // competences-numeriques
    if(!empty($tab_donnee_bilan['crcn']))
    {
      $tab_crcn = $tab_donnee_bilan['crcn'];
      $comp_num_commun_refs = !empty($tab_crcn['comp-num-commun-refs']) ? ' comp-num-commun-refs="'.$tab_crcn['comp-num-commun-refs'].'"' : '' ;
      $tab_xml_bilans['periodique'][] = '    <competences-numeriques'.$comp_num_commun_refs.'>';
      if(!empty($tab_crcn['appreciation']))
      {
        $tab_xml_bilans['periodique'][] = '     <appreciation-num>'.html($tab_crcn['appreciation']).'</appreciation-num>';
      }
      if(!empty($tab_crcn['niveau']))
      {
        foreach($tab_crcn['niveau'] as $code => $niveau)
        {
          // Pour l’instant obligatoire que pour la fin de cycle 3, avec un niveau compris entre 1 et 3 uniquement.
          $niveau = min($niveau,3);
          $tab_xml_bilans['periodique'][] = '     <competence-num code="'.$code.'" niveau="'.$niveau.'" />';
        }
      }
      $tab_xml_bilans['periodique'][] = '    </competences-numeriques>';
    }
    // epis-eleve (2D)
    if(!empty($tab_donnee_bilan['epi']))
    {
      $tab_xml_bilans['periodique'][] = '    <epis-eleve>';
      foreach($tab_donnee_bilan['epi'] as $id => $tab)
      {
        $tab_xml_bilans['periodique'][] = '     <epi-eleve epi-groupe-ref="'.$id.'GR">';
        if(!empty($tab['appreciation']))
        {
          $tab_xml_bilans['periodique'][] = '      <commentaire>'.html($tab['appreciation']).'</commentaire>';
        }
        $tab_xml_bilans['periodique'][] = '     </epi-eleve>';
      }
      $tab_xml_bilans['periodique'][] = '    </epis-eleve>';
    }
    // acc-persos-eleve (2D)
    // AP supprimés à compter de LSU 25.5.0 diffusée aux académies le 19/01/2026 !!!
    /*
    if(!empty($tab_donnee_bilan['ap']))
    {
      $tab_xml_bilans['periodique'][] = '    <acc-persos-eleve>';
      foreach($tab_donnee_bilan['ap'] as $id => $tab)
      {
        $tab_xml_bilans['periodique'][] = '     <acc-perso-eleve acc-perso-groupe-ref="'.$id.'GR">';
        if(!empty($tab['appreciation']))
        {
          $tab_xml_bilans['periodique'][] = '      <commentaire>'.html($tab['appreciation']).'</commentaire>';
        }
        $tab_xml_bilans['periodique'][] = '     </acc-perso-eleve>';
      }
      $tab_xml_bilans['periodique'][] = '    </acc-persos-eleve>';
    }
    */
    // parcours
    if(!empty($tab_donnee_bilan['parcours']))
    {
      $tab_xml_bilans['periodique'][] = '    <liste-parcours>';
      foreach($tab_donnee_bilan['parcours'] as $id => $tab)
      {
        $tab['appreciation'] = !empty($tab['appreciation']) ? $tab['appreciation'] : '-' ;
        $tab_xml_bilans['periodique'][] = '     <parcours code="'.$tab['code'].'">'.html($tab['appreciation']).'</parcours>';
      }
      $tab_xml_bilans['periodique'][] = '    </liste-parcours>';
    }
    // modalites-accompagnement (2D)
    if(!empty($tab_donnee_bilan['modaccomp']))
    {
      $tab_xml_bilans['periodique'][] = '    <modalites-accompagnement>';
      foreach($tab_donnee_bilan['modaccomp'] as $code => $info_complement)
      {
        if( ($code!='PPRE') || !$info_complement )
        {
          $tab_xml_bilans['periodique'][] = '     <modalite-accompagnement code="'.$code.'" />';
        }
        else
        {
          $tab_xml_bilans['periodique'][] = '     <modalite-accompagnement code="'.$code.'"><complement-ppre>'.html($info_complement).'</complement-ppre></modalite-accompagnement>';
        }
      }
      $tab_xml_bilans['periodique'][] = '    </modalites-accompagnement>';
    }
    // acquis-conseils (2D) | appreciation-generale (1D)
    $tab_synthese = $tab_donnee_bilan['synthese'];
    $tab_xml_bilans['periodique'][] = '    <'.$key_appr_synthese.'>'.html($tab_synthese['appreciation']).'</'.$key_appr_synthese.'>';
    // devoirs-faits
    if(!empty($tab_donnee_bilan['devoirsfaits']))
    {
      $tab_xml_bilans['periodique'][] = '    <devoirs-faits>Élève inscrit au dispositif.</devoirs-faits>';
    }
    // vie-scolaire (2D)
    if(!empty($tab_donnee_bilan['viesco']))
    {
      $tab_viesco = $tab_donnee_bilan['viesco'];
      $tab_xml_bilans['periodique'][] = '    <vie-scolaire nb-retards="'.$tab_viesco['nb-retards'].'" nb-abs-justifiees="'.$tab_viesco['nb-abs-justifiees'].'" nb-abs-injustifiees="'.$tab_viesco['nb-abs-injustifiees'].'">'; // nb-heures-manquees non géré
      if(!empty($tab_viesco['appreciation']))
      {
        $tab_xml_bilans['periodique'][] = '     <commentaire>'.html($tab_viesco['appreciation']).'</commentaire>';
      }
      $tab_xml_bilans['periodique'][] = '    </vie-scolaire>';
    }
    // socle
    // --> pas géré pour l’instant
    // responsables
    if(!empty($tab_donnee_bilan['responsables']))
    {
      $tab_xml_bilans['periodique'] = array_merge( $tab_xml_bilans['periodique'] , xml_responsables($tab_donnee_bilan['responsables']) );
    }
    $tab_xml_bilans['periodique'][] = '   </bilan-periodique>';
  }
  // bilan de fin de cycle
  else if($tab_bilan['type']=='cycle')
  {
    $profs_eleve_refs = !empty($tab_bilan[$key_profs_eleve]) ? ' '.$key_profs_eleve.'="'.$tab_bilan[$key_profs_eleve].'"' : '' ;
    $tab_xml_bilans['cycle'][] = '   <bilan-cycle'.$profs_eleve_refs.' eleve-ref="'.$tab_bilan['eleve-ref'].'" cycle="'.$tab_bilan['cycle'].'" millesime="'.$tab_bilan['millesime'].'" date-creation="'.$tab_bilan['date-creation'].'" '.$key_chef_etabl.'="'.$tab_bilan[$key_chef_etabl].'">';
    // domaines du socle
    $tab_xml_bilans['cycle'][] = '    <socle>';
    foreach($tab_donnee_bilan['socle'] as $id => $tab)
    {
      $tab_xml_bilans['cycle'][] = '     <domaine code="'.$tab['code'].'" positionnement="'.$tab['positionnement'].'" />';
    }
    $tab_xml_bilans['cycle'][] = '    </socle>';
    // synthèse
    $tab_synthese = $tab_donnee_bilan['synthese'];
    $tab_xml_bilans['cycle'][] = '    <synthese>'.html($tab_synthese['appreciation']).'</synthese>';
    // responsables
    if(!empty($tab_donnee_bilan['responsables']))
    {
      $tab_xml_bilans['cycle'] = array_merge( $tab_xml_bilans['cycle'] , xml_responsables($tab_donnee_bilan['responsables']) );
    }
    // langue de culture régionale (cycle 4 uniquement)
    if(!empty($tab_donnee_bilan['langcultregion']))
    {
      $tab_langcultregion = $tab_donnee_bilan['langcultregion'];
      $positionnement = ($tab_langcultregion['code']=='AUC') ? '' : ' positionnement="'.$tab_langcultregion['positionnement'].'"' ;
      $tab_xml_bilans['cycle'][] = '    <langue-culture-regionale code="'.$tab_langcultregion['code'].'"'.$positionnement.' />';
    }
    $tab_xml_bilans['cycle'][] = '   </bilan-cycle>';
  }
  unset($tab_export_donnees[$key]);
}
// assemblage
if(!empty($tab_xml_bilans['periodique']))
{
  $tab_xml[] = '  <bilans-periodiques>';
  $tab_xml[] = implode(NL,$tab_xml_bilans['periodique']);
  $tab_xml[] = '  </bilans-periodiques>';
}
if(!empty($tab_xml_bilans['cycle']))
{
  $tab_xml[] = '  <bilans-cycle>';
  $tab_xml[] = implode(NL,$tab_xml_bilans['cycle']);
  $tab_xml[] = '  </bilans-cycle>';
}

// fermeture des balises
$tab_xml[] = ' </donnees>';
$tab_xml[] = '</lsun-bilans>';

// Le xml
$export_xml = implode(NL,$tab_xml);
if($rubrique_sans_element)
{
  $export_xml = str_replace( '<elements-programme>' , '<elements-programme>'.NL.'   <element-programme id="EL0" libelle="-" />' , $export_xml );
}

// Vérification XML valide

// @see http://php.net/manual/fr/domdocument.schemavalidate.php#62032
function libxml_display_error($error)
{
  $return = '<p>';
  switch ($error->level)
  {
    case LIBXML_ERR_WARNING:
      $return .= "<b>Warning $error->code</b> : ";
      break;
    case LIBXML_ERR_ERROR:
      $return .= "<b>Error $error->code</b> : ";
      break;
    case LIBXML_ERR_FATAL:
      $return .= "<b>Fatal Error $error->code</b> : ";
      break;
  }
  $return .= trim($error->message);
  /*
  if ($error->file) {
    $return .=    " in <b>$error->file</b>";
  }
  $return .= " on line <b>$error->line</b>\n";
  */
  $return .= '</p>'.NL;
  return $return;
}

libxml_use_internal_errors(TRUE);
$dom_xml= new DOMDocument();
$dom_xml->loadXML($export_xml);
if(!$dom_xml->schemaValidate(CHEMIN_DOSSIER_XSD.$tab_xml_param[$export_objet]['file_xsd']))
{
  $errors = libxml_get_errors();
  Json::add_str( '<p><label class="erreur">Fichier XML obtenu détecté invalide ; utilisez le lien [assistance] en haut de page en précisant la classe ou l’élève concerné et le message d’erreur.</label></p>' );
  foreach ($errors as $error)
  {
    Json::add_str( libxml_display_error($error) );
  }
  libxml_clear_errors();
  // En PROD on ne donne pas ce fichier qui de toutes façons sera rejeté par LSU.
  // Hors PROD on laisse la possibilité de récupérer le fichier pour l’analyser.
  if(SERVEUR_TYPE == 'PROD')
  {
    Json::end( FALSE );
  }
}

// à compter de LSU 19.2 une vérification est effectuée sur la taille du fichier, limité à 100 Mo
if( mb_strlen($export_xml) > 100000000 )
{
  Json::end( FALSE , 'Fichier dépassant la taille limite de 100 Mo autorisée par LSU !<br>Sélectionner moins d’élèves ou moins de périodes.' );
}

// Enregistrement du fichier
$periode_nom = ($periode) ? $tab_periode_livret[$periode] : 'Toutes périodes' ;
$fichier_extension = 'xml';
$fichier_nom = 'import-lsun-'.Clean::fichier($_SESSION['WEBMESTRE_UAI'].'_'.$periode_nom.'_'.$groupe_nom).'_'.FileSystem::generer_fin_nom_fichier__date_et_alea().'.'.$fichier_extension; // LSU recommande le modèle "import-lsun-{timestamp}.xml"
FileSystem::ecrire_fichier( CHEMIN_DOSSIER_EXPORT.$fichier_nom , $export_xml );
$fichier_lien = './force_download.php?fichier='.$fichier_nom;

// Retour
$sb = ($nb_bilans>1) ? 's' : '' ;
$se = ($nb_eleves>1) ? 's' : '' ;
Json::add_str('<p><label class="valide">Fichier d’export généré : '.$nb_bilans.' bilan'.$sb.' concernant '.$nb_eleves.' élève'.$se.'.</label></p>'.NL);
Json::add_str('<p><a target="_blank" rel="noopener noreferrer" href="'.$fichier_lien.'"><span class="file file_'.$fichier_extension.'">Récupérer le fichier au format <em>'.$fichier_extension.'</em>.</span></a></p>'.NL);
Json::add_str('<p><label class="alerte">Pour des raisons de sécurité et de confidentialité, ce fichier sera effacé du serveur dans 1h.</label></p>'.NL);
Json::end( TRUE );

?>
