<?php
//$Id$
//gen openMairie le 30/05/2019 16:10

require_once "../gen/obj/election.class.php";

class election extends election_gen {

    /**
     * Contiens le paramétrage des plans au format json
     *
     * @var array
     */
    public $parametragePlan;

    /**
     * Récupére le paramétrage des plans de l'élection dans le
     * repertoire du portail web.
     * Si le fichier existe son contenu est au format json. Son contenu
     * est donc transformer en tableau avant d'être enregistré dans
     * parametragePlan.
     * Si le fichier n'existe pas c'est un tableau vide qui est enregistré.
     *
     * @return void
     */
    protected function set_parametrage_plans() {
        $parametragePLan = array();
        // Récupération du paramétrage des plans depuis le répertoire web
        // de l'élection
        $path = $this->get_election_web_path();
        if (file_exists($path.'/plans.json')) {
            $plansJson =  file_get_contents($path.'/plans.json');
            $parametragePLan = json_decode($plansJson, true);
        }
        $this->parametragePlan = $parametragePLan;
    }

    /**
     * Récupére le paramétrage des plans de l'élection dans le
     * repertoire du portail web.
     * Si le fichier existe son contenu est au format json. Son contenu
     * est donc transformer en tableau avant d'être enregistré dans
     * parametragePlan.
     * Si le fichier n'existe pas c'est un tableau vide qui est enregistré.
     *
     * @return void
     */
    public function get_parametrage_plans() {
        return $this->parametragePlan;
    }

    /**
     * Constructeur.
     *
     * @param string $id Identifiant de l'objet.
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     */
    function __construct($id, &$dnu1 = null, $dnu2 = null) {
        $this->constructeur($id);
        if ($id != ']') {
            // Récupération et stockage du paramétrage des plans de l'élection
            $this->set_parametrage_plans();
        }
    }

    /**
     *
     * @return string
     */
    function get_var_sql_forminc__sql_perimetre() {
        return "SELECT
                unite.unite,
                concat(unite.code_unite, ' ', unite.libelle)
            FROM
                ".DB_PREFIXE."unite
            WHERE
                ((unite.om_validite_debut IS NULL AND
                (unite.om_validite_fin IS NULL OR
                    unite.om_validite_fin > CURRENT_DATE)) OR
                    (unite.om_validite_debut <= CURRENT_DATE AND
                        (unite.om_validite_fin IS NULL OR
                            unite.om_validite_fin > CURRENT_DATE))) AND
                unite.perimetre = TRUE
            ORDER BY unite.libelle ASC";
    }

    /**
     * Definition des actions disponibles sur la classe
     *
     * @return void
     */
    function init_class_actions() {
        parent::init_class_actions();
        // Les élections archivées ne sont pas modifiable
        $this->class_actions[1]['condition'][0] = 'non_archivee';
        $this->class_actions[2]['condition'][0] = 'is_etape_parametrage';

        // ACTION - 04 - reset
        $this->class_actions[4] = array(
            "identifier" => "reset",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("reset election"),
                "order" => 40,
                "class" => "delete-16",
            ),
            "view" => "formulaire",
            "method" => "reset_election",
            "button" => "delete",
            "permission_suffix" => "reset",
            "condition" => 'is_etape_parametrage'
        );

        // ACTION - 05 - import_inscrits
        $this->class_actions[5] = array(
            "identifier" => "import_inscrits",
            "portlet" => array(
                "type" => "action-self",
                "libelle" => _("importer les inscrits"),
                "order" => 50,
                'class' => 'arrow-right-16',
            ),
            "view" => "view_import_inscrits",
            "permission_suffix" => "importer_inscrits",
        );

        // ACTION - 10 - extraction
        $this->class_actions[10] = array(
            "identifier" => "extraction",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("extraction en csv"),
                "order" => 100,
                "class" => "arrow-right-16",
            ),
            "view" => "formulaire",
            "method" => "export_csv_resultats",
            "button" => "diffuser",
            "permission_suffix" => "extraction_resultat",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 11 - prefecture
        $this->class_actions[11] = array(
            "identifier" => "prefecture",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("Envoi a la prefecture"),
                "order" => 110,
                "class" => "arrow-right-16",
            ),
            "view" => "formulaire",
            "method" => "prefecture",
            "button" => "diffuser",
            "permission_suffix" => "envoi_prefecture",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 15 - calcul des sieges
        $this->class_actions[15] = array(
            "identifier" => "siege",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("calcul de sieges elu"),
                "order" => 150,
                "class" => "add-16",
            ),
            "view" => "formulaire",
            "method" => "sieges_elu",
            "button" => "diffuser",
            "permission_suffix" => "calcul_sieges",
            "condition" => array(
                'pas_etape_parametrage',
                'pas_etape_saisie',
                'non_archivee'
            )
        );

        // ACTION - 19 - verification
        $this->class_actions[19] = array(
            "identifier" => "verification",
            "portlet" => array(
                "type" => "action-direct",
                "libelle" => _("verification paramétrage"),
                "order" => 190,
                "class" => "add-16",
            ),
            "view" => "formulaire",
            "method" => "verification",
            "button" => "creer",
            "permission_suffix" => "verification_parametrage",
            "condition" => array(
                'pas_etape_saisie',
                'pas_etape_finalisation',
                'non_archivee'
            )
        );

        // Action permettant de passer d'une étape à l'autre du workflow
        // Etape de paramétrage -> Etape de simulation
        $this->class_actions[30] = array(
            "identifier" => "simulation",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("aller à l'étape *simulation*"),
                "order" => 30,
                "class" => "workflow-action-go-to-next-step-simulation-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_simulation",
            "button" => "diffuser",
            "permission_suffix" => "simulation",
            "condition" => "is_etape_parametrage"
        );

        // Etape de simulation -> Etape de parametrage
        $this->class_actions[31] = array(
            "identifier" => "retour_parametrage",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("retour à l'étape *paramétrage*"),
                "order" => 31,
                "class" => "workflow-action-go-to-prev-step-parametrage-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_retour_parametrage",
            "button" => "diffuser",
            "permission_suffix" => "retour_parametrage",
            "condition" => "is_etape_simulation"
        );

        // Etape de simulation -> Etape de saisie
        $this->class_actions[32] = array(
            "identifier" => "saisie",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("aller à l'étape *saisie*"),
                "order" => 32,
                "class" => "workflow-action-go-to-next-step-saisie-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_saisie",
            "button" => "diffuser",
            "permission_suffix" => "saisie",
            "condition" => "is_etape_simulation"
        );

        // Etape de saisie -> Etape de simulation
        $this->class_actions[33] = array(
            "identifier" => "retour_simulation",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("retour à l'étape *simulation*"),
                "order" => 33,
                "class" => "workflow-action-go-to-prev-step-simulation-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_retour_simulation",
            "button" => "diffuser",
            "permission_suffix" => "retour_simulation",
            "condition" => "is_etape_saisie"
        );

        // Etape de saisie -> Etape de finalisation
        $this->class_actions[34] = array(
            "identifier" => "finalisation",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("aller à l'étape *finalisation*"),
                "order" => 34,
                "class" => "workflow-action-go-to-next-step-finalisation-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_finalisation",
            "button" => "diffuser",
            "permission_suffix" => "finalisation",
            "condition" => "is_etape_saisie"
        );

        // Etape de finalisation -> Etape de saisie
        $this->class_actions[35] = array(
            "identifier" => "retour_saisie",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("retour à l'étape *saisie*"),
                "order" => 35,
                "class" => "workflow-action-go-to-prev-step-saisie-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_retour_saisie",
            "button" => "diffuser",
            "permission_suffix" => "retour_saisie",
            "condition" => "is_etape_finalisation"
        );

        // Etape de finalisation -> Etape d'archivage
        $this->class_actions[36] = array(
            "identifier" => "archiver",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("aller à l'étape *archivage*"),
                "order" => 36,
                "class" => "workflow-action-go-to-next-step-archivage-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_archiver",
            "button" => "diffuser",
            "permission_suffix" => "archiver",
            "condition" => "is_etape_finalisation"
        );

        // Etape d'archivage -> Etape de finalisation
        $this->class_actions[37] = array(
            "identifier" => "retour_finalisation",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("retour à l'étape *finalisation*"),
                "order" => 37,
                "class" => "workflow-action-go-to-prev-step-finalisation-16",
            ),
            "view" => "formulaire",
            "method" => "trigger_desarchiver",
            "button" => "diffuser",
            "permission_suffix" => "retour_finalisation",
            "condition" => "is_archivee"
        );

        // ACTION - 41 - proclamation des résultats
        $this->class_actions[41] = array(
            "identifier" => "proclamation",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("proclamation des résultats"),
                "order" => 401,
                "class" => "pdf-16",
                ),
            "view" => "pdf_proclamation_resultat",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 42 - proclamation des résultats par bureau
        $this->class_actions[42] = array(
            "identifier" => "pdf_proclamation_bureau",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("proclamation des résultat par bureau"),
                "order" => 402,
                "class" => "pdf-16",
                ),
            "view" => "pdf_proclamation_resultat_par_bureau",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 43 - proclamation des résultat avec nombre de sièges
        $this->class_actions[43] = array(
            "identifier" => "pdf_proclamation_siege",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("proclamation des résultats répartition des sièges"),
                "order" => 403,
                "class" => "pdf-16",
                ),
            "view" => "pdf_proclamation_resultat_avec_siege",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 44 - proclamation des résultats par périmètre
        $this->class_actions[44] = array(
            "identifier" => "pdf_proclamation_perimetre",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("proclamation des résultats par périmètre"),
                "order" => 404,
                "class" => "pdf-16",
                ),
            "view" => "pdf_proclamation_resultat_par_perimetre",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );
        
        // ACTION - 45 - édition de la participation
        $this->class_actions[45] = array(
            "identifier" => "pdf_participation",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("édition de la participation"),
                "order" => 405,
                "class" => "pdf-16",
                ),
            "view" => "pdf_participation",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 46 - edition des résultats
        $this->class_actions[46] = array(
            "identifier" => "edition",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("édition des résultats"),
                "order" => 406,
                "class" => "pdf-16",
                ),
            "view" => "edition_resultat",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 47 - resultats par perimetre
        $this->class_actions[47] = array(
            "identifier" => "pdf_resultat_perimetre",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("édition des résultats par périmètre"),
                "order" => 407,
                "class" => "pdf-16",
                ),
            "view" => "pdf_resultat_perimetre",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 48 - pdf_prefecture
        $this->class_actions[48] = array(
            "identifier" => "pdf_prefecture",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("préfecture"),
                "order" => 408,
                "class" => "pdf-16",
                ),
            "view" => "pdf_prefecture",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );

        // ACTION - 49 - Résultats globaux
        $this->class_actions[49] = array(
            "identifier" => "pdf_resultats_globaux",
            "portlet" => array(
                "type"=>"action-blank",
                "libelle" => _("résultats globaux"),
                "order" => 409,
                "class" => "pdf-16",
                ),
            "view" => "pdf_resultats_globaux",
            "permission_suffix" => "edition",
            "condition" => 'pas_etape_parametrage'
        );
    }
    
    /**
     * SETTER FORM - setType.
     *
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     *
     * @return void
     */
    function setType(&$form, $maj) {
        parent::setType($form, $maj);
        // Pour les actions appelée en POST en Ajax, il est nécessaire de
        // qualifier le type de chaque champs (Si le champ n'est pas défini un
        // PHP Notice:  Undefined index dans core/om_formulaire.class.php est
        // levé). On sélectionne donc les actions de portlet de type
        // action-direct ou assimilé et les actions spécifiques avec le même
        // comportement.
        if ($this->get_action_param($maj, "portlet_type") == "action-direct"
            || $this->get_action_param($maj, "portlet_type") == "action-direct-with-confirmation") {
            //
            foreach ($this->champs as $key => $value) {
                $form->setType($value, 'hidden');
            }
            $form->setType($this->clePrimaire, "hiddenstatic");
        }

        if ($maj < 2) {
            $form->setType('tour', "select");
        }
        if ($maj==0) {
            $form->setType('envoi_initial', "hidden");
            $form->setType('workflow', "hidden");
        }

        // Définition aux différentes étapes du workflow de la liste des champs
        // pouvant être utilisé. Tous les champs du formulaire sont ensuite rendu
        // static sauf les champs de la liste
        if ($maj == 1) {
            switch ($this->getVal('workflow')) {
                case 'Paramétrage':
                    $champsUtilisable = $this->champs;
                    break;
                case 'Simulation':
                    $champsUtilisable = $this->champs;
                    break;
                case 'Saisie':
                    $champsUtilisable = array(
                        'envoi_initial',
                        'sieges',
                        'sieges_com',
                        'sieges_mep'
                    );
                    break;
                case 'Finalisation':
                    $champsUtilisable = array(
                        'envoi_initial',
                        'sieges',
                        'sieges_com',
                        'sieges_mep'
                    );
                    break;
                default:
                    $champsUtilisable = array();
                    break;
            }

            foreach ($this->champs as $key => $value) {
                if (! in_array($value, $champsUtilisable)) {
                    $form->setType($value, 'hiddenstatic');
                }
            }
        }

        $form->setType("workflow", 'hiddenstatic');
        $form->setType("votant_defaut", 'hidden');
        $form->setType("is_centaine", 'hidden');
        $form->setType("election_reference", 'hidden');

        if ($maj == 3) {
            $form->setType("workflow", 'widget__election_workflow');
        }
    }

    /**
     * SETTER FORM - setLib
     * 
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     * 
     * @return void
     */
    function setLib(&$form, $maj) {
        //libelle des champs
        parent::setLib($form, $maj);
        $form->setLib('type_election', __('type'));
        $form->setLib('date', __('date'));
        $form->setLib('perimetre', __('perimetre'));
        $form->setLib('votant_defaut', __('votant par defaut'));
        $form->setLib('heure_ouverture', __('heure d\'ouverture'));
        $form->setLib('heure_fermeture', __('heure de fermeture'));
        $form->setLib('envoi_initial', __('envoi initial'));
        $form->setLib('publication_auto', __('publier automatiquement les résultats'));
        $form->setLib('publication_erreur', __('publier les résultats en défaut'));
        $form->setLib('calcul_auto_exprime', __('calcul automatique des votes exprimés'));
        $form->setLib('garder_resultat_simulation', __('garder les résultats après la simulation'));
        $form->setLib('delegation_saisie', __('déléguer la saisie'));
        $form->setLib('delegation_participation', __('déléguer la saisie de la participation'));
        $form->setLib('validation_avant_publication', __('validation obligatoire pour la publication'));
        $form->setLib('sieges', __('Nombre de siège pour le conseil municipal'));
        $form->setLib('sieges_com', __('Nombre de sièges pour le conseil communautaire'));
        $form->setLib('sieges_mep', __('Nombre de sièges pour le conseil metropolitain'));
    }

    /**
     * SETTER FORM - setSelect.
     * 
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     *
     * @return void
     */ 
    function setSelect(&$form, $maj, &$db = null, $DEBUG = null) {
        parent::setSelect($form, $maj, $db, $DEBUG);
        // categorie donnees
        if ($maj<2) {
            $contenu=array();
            $contenu[0]=array('1',
                              '2',
                              );
            $contenu[1]=array(_('1'),
                              _('2'),
                              );
            $form->setSelect("tour", $contenu);
        }
    }

    /**
     * SETTER FORM - setLayout
     *
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     * 
     * @return void
     */
    function setLayout(&$form, $maj) {
        // Fieldset Election contiens dans l'ordre : libellé, code, tour,
        // type, date
            $form->setFieldset('libelle', 'D', _('Election'), "collapsible");
            $form->setFieldset('date', 'F', ''); // fin bloc
        // Fieldset Perimetre contiens dans l'ordre : perimètre, votant par défaut
            $form->setFieldset('perimetre', 'D', _('Perimetre'), "collapsible"); // bloc
            $form->setFieldset('votant_defaut', 'F', '');// fin bloc
        // Fieldset Participation contiens dans l'ordre : heure d'ouverture, de fermeture
            $form->setFieldset('heure_ouverture', 'D', _('Participation'), "collapsible"); // bloc
            $form->setFieldset('heure_fermeture', 'F', '');
        // Fieldset prefecture contiens dans l'ordre : envoi initial
            $form->setFieldset('envoi_initial', 'D', _('Prefecture'), "collapsible");// bloc
            $form->setFieldset('envoi_initial', 'F', '');// fin bloc
        // Fieldset Affichage contiens dans l'ordre : web
            $form->setFieldset('web', 'D', _('Affichage'), "collapsible");// bloc
            $form->setFieldset('web', 'F', '');// fin bloc
        // Fieldset Workflow contiens dans l'ordre : workflow
            $form->setFieldset('workflow', 'D', _('Workflow'), "collapsible");// bloc
            $form->setFieldset('workflow', 'F', '');// fin bloc
        // Fieldset Paramétrage contiens dans l'ordre : publication_auto, publication_erreur, calcul_auto_exprime
            $form->setFieldset('publication_auto', 'D', _('Paramétrage'), "collapsible");// bloc
            $form->setFieldset('validation_avant_publication', 'F', '');// fin bloc
        // Fieldset Répartition contiens dans l'ordre : sieges, sieges_com
            $form->setFieldset('sieges', 'D', _('Répartition'), "collapsible");// bloc
            $form->setFieldset('sieges_mep', 'F', '');// fin bloc
    }

    /**
     * SETTER FORM - set_form_default_values
     * 
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     * @param integer $validation Marqueur de validation du formulaire.
     * 
     * @return void
     */
    function set_form_default_values(&$form, $maj, $validation) {
        // recuperation des identifiants candidats et des resultats
        if ($validation==0 and $maj==0) {
            // Valeurs par défaut pour les heures d'ouverture et de fermeture
            // de l'élection. Si le paramètre n'existe pas, aucune valeur
            // par défaut n'est positionnée.
            if ($this->f->getParameter("id_default_heure_ouverture") != "") {
                $form->setVal(
                    "heure_ouverture",
                    intval($this->f->getParameter("id_default_heure_ouverture"))
                );
            }
            if ($this->f->getParameter("id_default_heure_fermeture") != "") {
                $form->setVal(
                    "heure_fermeture",
                    intval($this->f->getParameter("id_default_heure_fermeture"))
                );
            }
            // Valeurs par défaut pour les nombres de sièges à répartir pour
            // l'élection. Si le paramètre n'existe pas, aucune valeur
            // par défaut n'est positionnée.
            if ($this->f->getParameter("nb_sieges_default") != "") {
                $form->setVal(
                    "sieges",
                    intval($this->f->getParameter("nb_sieges_default"))
                );
            }
            if ($this->f->getParameter("nb_sieges_com_default") != "") {
                $form->setVal(
                    "sieges_com",
                    intval($this->f->getParameter("nb_sieges_com_default"))
                );
            }
            if ($this->f->getParameter("nb_sieges_mep_default") != "") {
                $form->setVal(
                    "sieges_mep",
                    intval($this->f->getParameter("nb_sieges_mep_default"))
                );
            }

            $form->setVal('tour', '1');
            $form->setVal('workflow', 'Paramétrage');
            $form->setVal('envoi_initial', 't');
        }
    }

    /**
     * TRIGGER - triggerajouterapres.
     *
     * @param string $id
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param array $val Tableau des valeurs brutes.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     *
     * @return boolean
     */
    function triggerajouterapres($id, &$dnu1 = null, $val = array(), $dnu2 = null) {
        // Ajout des unités et des tranches horaires de participation à l'élection
        $this->insert_election_unite($id, $val['perimetre']);
        $isCentaine = $this->f->boolean_string_to_boolean($val['is_centaine']);

        // La participation ne doit pas être crée pour les centaines
        if (! $isCentaine) {
            $this->participation($id, $val['heure_ouverture'], $val['heure_fermeture']);
        }
        $this->ajouter_plans_par_defaut($id);
    }

    /**
     * TRIGGER - triggermodifier.
     *
     * @param string $id
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param array $val Tableau des valeurs brutes.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     *
     * @return boolean
     */
    function triggermodifier($id, &$dnu1 = null, $val = array(), $dnu2 = null) {
        // Si le perimetre a été modifié, il faut mettre à jour les unités.
        // Les unités sont liées à la participation et aux résultats des candidats,
        // il faut donc supprimer les résultats des candidats et la participation,
        // puis les unités.
        // Ensuite, les unités doivent être recrées puis la participation et enfin,
        // pour chaque candidat, ses résultats.
        //
        // Si les tranches horaires ont été modifiées, alors il faut supprimer
        // la participation et la recréer
        $succes = true;
        if ($val['perimetre'] != $this->getVal('perimetre')) {
            $candidats = $this->get_candidats_election($id);
            foreach ($candidats as $candidat) {
                $succes = $candidat->supprimer_resultats_candidat();
                if (! $succes) {
                    break;
                }
            }
            $succes = $succes ? $this->supprimer_participation_election($id) : false;
            $succes = $succes ? $this->supprimer_unites_election($id) : false;
            if ($succes) {
                $this->insert_election_unite($id, $val['perimetre']);

                // La participation ne doit pas être crée pour les centaines
                if (! $this->is_centaine()) {
                    $this->participation($id, $val['heure_ouverture'], $val['heure_fermeture']);
                }
                foreach ($candidats as $candidat) {
                    $candidat->insert_election_resultat($id, $candidat->getVal('election_candidat'));
                }
                // Mise à jour du fichier contenant la structure du périmètre sur l'affichage
                // et du fichier contenant la liste des unités
                if (file_exists('../aff/res/'.$id)) {
                    $this->affichage_perimetre($id);

                    $electionUnite = $this->f->get_element_by_id('election_unite', ']');
                    $envoiListeUnite = $electionUnite->ecris_fichier_info_unite_affichage(array(
                        'election' => $val['election'],
                        'unite' => 0, // Identifiant d'unité inexistant pour ne pas que l'unité soit considéré comme envoyée
                        'envoi_aff' => false
                    ));
                    if ($envoiListeUnite) {
                        $this->addToMessage('La liste des unités a été mise à jour');
                    } else {
                        $this->addToMessage('Erreur : Liste des unités n\'a pas été mise à jour');
                    }
                }
            }
        } elseif ($val['heure_ouverture'] != $this->getVal('heure_ouverture') ||
            $val['heure_fermeture'] != $this->getVal('heure_fermeture')
        ) {
            $succes = $this->supprimer_participation_election($id);
            // La participation ne doit pas être crée pour les centaines
            if ($succes && ! $this->is_centaine()) {
                $this->participation($id, $val['heure_ouverture'], $val['heure_fermeture']);
            }
        }

        // Condition permettant d'utiliser la méthode triggermodifier de l'élection pour les
        // centaine sans utiliser la partie mettant à jour le paramétrage des centaines
        // La mise à jour des centaines se fait en copiant les valeurs de l'élection et en
        // utilisant ces valeurs comme si elle étaient issues du formulaire des centaines
        // Utiliser le triggermodifier de la centaine permet d'appeller celui de l'élection
        // qui va mettre à jour les unités et la participation si jamais le perimetre et/ou
        // les horaires ont changé.
        // Ainsi, les centaines seront toujours à jour vis à vis de l'élection
        if (! $this->is_centaine()) {
            $centaines = $this->get_centaines_election();
            foreach ($centaines as $centaine) {
                $modif = array();
                $champsElectionRef = array(
                    'code',
                    'tour',
                    'type_election',
                    'date',
                    'heure_ouverture',
                    'heure_fermeture',
                    'perimetre',
                    'envoi_initial',
                    'publication_auto',
                    'publication_erreur',
                    'calcul_auto_exprime'
                );
                foreach ($centaine->champs as $key => $champ) {
                    $modif[$champ] = $centaine->val[$key];
                    if (in_array($champ, $champsElectionRef)) {
                        $modif[$champ] = $val[$champ];
                    }
                }
                // modifie uniquement les données de la centaine liées à l'élection
                // et déclenche le trigger permettant de mettre à jour les unités et
                // la participation si besoin
                $centaine->modifier($modif);
            }
        }
    }

    /**
     * Methode clesecondaire
     * Surchage permettant de supprimer les vérifications des clés secondaires pour
     * les candidats, plans, animations, unité et la participation de l'élection.
     * Ainsi, il est possible de faire une suppression en cascade de l'élection et
     * de son paramétrage dans la méthode triggersupprimer()
     */
    function cleSecondaire($id, &$dnu1 = null, $val = array(), $dnu2 = null) {
        // La vérification des clés secondaires est fait dans le triggersupprimer
    }

    /**
     * TRIGGER - triggersupprimer.
     *
     * @param string $id
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param array $val Tableau des valeurs brutes.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     *
     * @return boolean
     */
    function triggersupprimer($id, &$dnu1 = null, $val = array(), $dnu2 = null) {
        // Supression du paramétrage de l'élection et vérification de la suppression
        $succes = $this->supprimer_participation_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "participation_election", "election", $id);
        $message = $this->correct && ! $succes ? 'Erreur lors de la suppression de la participation'
            : '';

        $succes = $this->supprimer_candidats_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "election_candidat", "election", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des candidats'
            : '';

        $succes = $this->supprimer_centaines_election();
        $this->rechercheTable($this->f->db, "election", "election_reference", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des candidats'
            : '';
        
        $succes = $this->supprimer_unites_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "election_unite", "election", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des unités'
            : '';

        $succes = $this->supprimer_animations_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "animation", "election", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des animations'
            : '';

        $succes = $this->supprimer_plans_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "plan_election", "election", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des plans'
            : '';
            
        $succes = $this->supprimer_delegation_election($this->getVal('election'));
        $this->rechercheTable($this->f->db, "delegation", "election", $id);
        $message .= $this->correct && ! $succes ? 'Erreur lors de la suppression des délégations'
            : '';

        // Suppression des répertoire liés à l'élection
        $webPath = $this->get_election_web_path();
        if (file_exists('../aff/res/'.$id)) {
            $this->f->rrmdir('../aff/res/'.$id);
        }
        if (file_exists($webPath)) {
            $this->f->rrmdir($webPath);
        }
        return $this->correct;
    }
    
    /**
     * TRIGGER - trigger_simulation.
     *
     * @return boolean
     */
    function trigger_simulation() {
        $succes = $this->verification();
        $succes &= $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Simulation')
        );

        $this->addToMessage('<b>Passage à l\'étape de simulation</b>');
        return $succes;
    }

    /**
     * TRIGGER - trigger_retour_parametrage.
     *
     * Lors du passage de l'étape de simulation à l'étape de paramétrage
     * les résultats saisis et enregistrés pour la participation et les
     * unités sont réinitialisés.
     * Les fichiers envoyés aux répertoires web et aff sont également mis
     * à jour pour que les résultats soient correctement annulé.
     * Pour finir les attribut d'envoi sont remis à faux pour indiquer
     * que les informations n'ont pas été envoyées à l'affichage.
     *
     * Les résultats de chaque candidat sont également réinitialisés
     *
     * Ces traitements sont réalisés à l'aide des méthodes annuler_resultat()
     * des classes election_unite et participation_election
     *
     * @return boolean
     */
    function trigger_retour_parametrage() {
        $annulationRes = true;
        // Reset des résultats de l'élection et de ceux de ses centaines
        if (! $this->reset_resultat_election()) {
            $this->addToMessage('Les résultats de l\'éléction n\'ont pas été réinitialisé');
            $annulationRes = false;
        }

        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Paramétrage')
        );

        $this->addToMessage('<b>Retour à l\'étape de paramétrage</b>');
        return $succes && $annulationRes;
    }
    
    /**
     * TRIGGER - trigger_saisie.
     *
     * Lors du passage de l'étape de simulation à l'étape de saisie
     * les résultats saisis et enregistrés pour la participation et les
     * unités sont réinitialisés.
     * Les fichiers envoyés aux répertoires web et aff sont également mis
     * à jour pour que les résultats soient correctement annulé.
     * Pour finir les attribut d'envoi sont remis à faux pour indiquer
     * que les informations n'ont pas été envoyées à l'affichage.
     *
     * Le nombre de siège de chaque candidat est également réinitialisé
     *
     * Le paramétrage ayant pu être modifié en simulation, il faut vérifier
     * qu'il est toujours correct
     *
     * Ces traitements sont réalisés à l'aide des méthodes annuler_resultat()
     * des classes election_unite et participation_election
     * @return boolean
     */
    function trigger_saisie() {
        $suppressionResultat = true;
        // Suppression des résultats si ils ne doivent pas être conservé
        $garderRes = $this->f->boolean_string_to_boolean($this->getVal('garder_resultat_simulation'));
        // Reset des résultats de l'élection et de ceux de ses centaines si l'option de conservation
        // n'est pas active
        if (! $garderRes) {
            if (! $this->reset_resultat_election()) {
                $this->addToMessage('Les résultats de l\'élection n\'ont pas été réinitialisé');
                $suppressionResultat = false;
            }
        }
        
        // Vérification du paramétrage
        $valide = $this->verification();

        $modifie = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Saisie')
        );

        $this->addToMessage('<b>Passage à l\'étape de saisie</b>');
        return $valide && $modifie && $suppressionResultat;
    }

    /**
     * Réinitialise les résultats de toutes les unités de l'élection.
     * Si l'élection n'est pas une centaine, sa participation et le nombre de siège attribué
     * aux candidats est également réinitialisé
     *
     * @return boolean indique si la réinitialisation a bien fonctionnée
     */
    protected function reset_resultat_election() {
        $annulationRes = true;
        $annulationPart = true;
        $annulationSiege = true;
        $annulationCentaine = true;
        $idElection = $this->getVal('election');
        $pathWeb = $this->get_election_web_path();

        // Remise à null des résultats de toute les unites
        if (! empty($idElection)) {
            $isCentaine = $this->f->boolean_string_to_boolean($this->getVal('is_centaine'));
            $votant = $isCentaine ? $this->getVal('votant_defaut') : null;
            $reinitRes = array(
                'votant' => $votant,
                'procuration' => null,
                'emargement' => null,
                'nul' => null,
                'blanc' => null,
                'exprime' => null,
                'saisie' => 'en attente',
                'envoi_aff' => false,
                'envoi_web' => false,
                'validation' => 'saisie en cours'
            );
            $modification = $this->db->autoExecute(
                DB_PREFIXE.'election_unite',
                $reinitRes,
                DB_AUTOQUERY_UPDATE,
                'election = '.$idElection
            );
            $this->addToLog(__METHOD__ . "() : db->autoExecute(" . $modification . ")", VERBOSE_MODE);
            if ($this->f->isDatabaseError($modification, true)) {
                $this->addToMessage('La réinitialisation des résultats a échoué');
                $this->db->rollback();
                $annulationRes = false;
            }

            // Reinitialisation des résultats des candidats
            $reinitRes = array(
                'resultat' => null
            );
            $modification = $this->db->autoExecute(
                DB_PREFIXE.'election_resultat',
                $reinitRes,
                DB_AUTOQUERY_UPDATE,
                'election_unite IN (
                    SELECT
                        election_unite
                    FROM '
                        .DB_PREFIXE.'election_unite
                    WHERE
                        election = '.$idElection.')'
            );
            $this->addToLog(__METHOD__ . "() : db->autoExecute(" . $modification . ")", VERBOSE_MODE);
            if ($this->f->isDatabaseError($modification, true)) {
                $this->addToMessage('La réinitialisation des résultats des candidats a échoué');
                $this->db->rollback();
                $annulationRes = false;
            }

            // Maj des résultats de toutes les unités sur le portail web et les animations
            $results = $this->get_full_results();
            $electionUnite = $this->f->get_element_by_id('election_unite', ']');
            $valElectionUnite = array(
                'election' => $idElection,
                'envoi_aff' => false,
                'envoi_web' => false,
                'saisie' => 'en attente',
                'inscrit' => 0,
                'votant' => 0,
                'blanc' => 0,
                'nul' => 0,
                'exprime' => 0
            );
            foreach ($results["resultats"] as $election_unite) {
                $valElectionUnite['election_unite'] = $election_unite['election_unite_id'];
                $valElectionUnite['unite'] = $election_unite['unite_id'];
                if (! $electionUnite->desaffichage($valElectionUnite)) {
                    $this->addToMessage('Echec de la réinitialisation des résultats sur les animations');
                }
                if (! $electionUnite->annuler_web($valElectionUnite)) {
                    $this->addToMessage('Echec de la réinitialisation des résultats sur le portail web');
                }
            }
        }
        
        // Annulation des résultats des centaines de l'élection
        $centaines = $this->get_centaines_election();
        foreach ($centaines as $centaine) {
            $annulation = $centaine->reset_resultat_election();
            if (! $annulation) {
                $annulationCentaine = $annulationCentaine && $annulation;
                $this->addToMessage(
                    'Echec de la réinitialisation des résultats de la centaine :'.
                    $centaine->getVal('libelle')
                );
            }
        }

        // La suppression de la participation et du nombre de siège ne concerne pas les centaines
        if (! $isCentaine) {
            $reinitPart = array(
                'votant' => null,
                'saisie' => false,
                'envoi_aff' => false,
                'envoi_web' => false
            );
            $modification = $this->db->autoExecute(
                DB_PREFIXE.'participation_unite',
                $reinitPart,
                DB_AUTOQUERY_UPDATE,
                'election_unite IN (
                    SELECT
                        election_unite
                    FROM '
                        .DB_PREFIXE.'election_unite
                    WHERE
                        election = '.$idElection.')'
            );
            $this->addToLog(__METHOD__ . "() : db->autoExecute(" . $modification . ")", VERBOSE_MODE);
            if ($this->f->isDatabaseError($modification, true)) {
                $this->addToMessage('La réinitialisation de la participation a échoué');
                $this->db->rollback();
                $annulationPart = false;
            }
            // Suppression du répertoire contenant la participation dans le repertoire web
            if (file_exists($pathWeb.'/participation.inc')) {
                if (! unlink($pathWeb.'/participation.inc')) {
                    $this->addToMessage('Echec de la supression des résultats sur le portail web');
                    $annulationPart = false;
                }
            }
            // Suppression du répertoire contenant la participation dans le repertoire aff
            if (file_exists('../aff/res/'.$idElection.'/bpar')) {
                if (! $this->f->rrmdir('../aff/res/'.$idElection.'/bpar')) {
                    $this->addToMessage('Echec de la supression de la participation sur les animations');
                    $annulationPart = false;
                }
            }
    
            // Reinitialisation du nombre de siège de chaque candidat
            $reinitSiege = array(
                'siege' => null,
                'siege_com' => null
            );
            $modification = $this->db->autoExecute(
                DB_PREFIXE.'election_candidat',
                $reinitSiege,
                DB_AUTOQUERY_UPDATE,
                'election = '.$idElection
            );
            $this->addToLog(__METHOD__ . "() : db->autoExecute(" . $modification . ")", VERBOSE_MODE);
            if ($this->f->isDatabaseError($modification, true)) {
                $this->addToMessage('La réinitialisation du nombre de siège a échoué');
                $this->db->rollback();
                $annulationSiege = false;
            }
            // Suppression du répertoire contenant la participation sur l'animation
            if (file_exists('../aff/res/'.$idElection.'/repartition_sieges.json')) {
                if (! unlink('../aff/res/'.$idElection.'/repartition_sieges.json')) {
                    $this->addToMessage('Echec de la supression de la répartion des sièges sur les animations');
                    $annulationSiege = false;
                }
            }
        }
        
        return $annulationRes && $annulationPart && $annulationSiege && $annulationCentaine;
    }

    /**
     * TRIGGER - trigger_retour_simulation.
     *
     * @return boolean
     */
    function trigger_retour_simulation() {
        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Simulation')
        );

        $this->addToMessage('<b>Retour à l\'étape de simulation</b>');
        return $succes;
    }

    /**
     * TRIGGER - trigger_finalisation.
     *
     * @return boolean
     */
    function trigger_finalisation() {
        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Finalisation')
        );

        $this->addToMessage('<b>Passage à l\'étape de finalisation</b>');
        return $succes;
    }

    /**
     * TRIGGER - trigger_retour_saisie.
     *
     * @return boolean
     */
    function trigger_retour_saisie() {
        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Saisie')
        );

        $this->addToMessage('<b>Retour à l\'étape de saisie</b>');
        return $succes;
    }

    /**
     * TRIGGER - trigger_archiver.
     *
     * @return boolean
     */
    function trigger_archiver() {
        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Archivage')
        );

        $this->addToMessage('<b>Election archivée</b>');
        return $succes;
    }

    /**
     * TRIGGER - trigger_desarchiver.
     *
     * @return boolean
     */
    function trigger_desarchiver() {
        $succes = $this->f->modifier_element_BD_by_id(
            'election',
            $this->getVal('election'),
            array('workflow' => 'Finalisation')
        );

        $this->addToMessage('<b>L\'élection est désarchivée</b>');
        return $succes;
    }

    /**
     * Pour chaque plan par défaut crée un lien entre ce plan et l'élection
     *
     * @param integer $electionId identifiant de l'élection
     * @return boolean indique si l'ajout à reussi
     */
    protected function ajouter_plans_par_defaut($electionId) {
        $succes = true;
        // Récupération de la liste des identifiants des plans à ajouter
        $plansId = $this->f->simple_query('plan', 'plan', 'par_defaut', true);
        // Creation d'un lien plan_election pour chaque identifiant de plan récupéré
        if (! empty($plansId)) {
            foreach ($plansId as $planId) {
                $data = array(
                    'plan_election' => null,
                    'plan' => $planId,
                    'election' => $electionId
                );
                $succes = $this->f->ajouter_element_BD('plan_election', $data);
            }
        }
        $message = $succes ? 'Plans ajoutés' : 'Les plans par défauts n\'ont pas été ajouté';
        $this->addToMessage($message);
        return $succes;
    }

    // =============
    // A C T I O N S
    // =============

    /**
     * Crée et initialise dans la base de données les résultats des unités de l'élection
     * en fonction de l'identifiant de l'élection, de son périmètre et du nombre de votant
     * par défaut.
     * Si ces paramètres ne sont pas renseigné utilise les valeurs issues de l'élection
     *
     * @param integer $electionId
     * @param integer $perimetreId
     * @param integer $nbVotant
     *
     * @return boolean true : ok, false : problème lors de la creation des unités
     */
    protected function insert_election_unite($electionId = null, $perimetreId = null) {
        // initialisation des paramètres avec les valeurs fournies. Si aucune valeurs
        // alors le paramétrage de l'élection est utilisé
        $electionId = $this->f->validateur_entier_positif($electionId) ? $electionId : $this->getval('election');
        $perimetreId = $this->f->validateur_entier_positif($perimetreId)  ? $perimetreId : $this->getval('perimetre');
        $succes = true;

        $nbUniteCrees=0;
        $perimetre = $this->f->get_element_by_id('unite', $perimetreId);
        $unites = $perimetre->get_all_contained_unites_id();

        foreach ($unites as $unite) {        // Ajout du nouveau dossier
            $election_unite = $this->f->get_element_by_id('election_unite', ']');
            $data = array(
                "election_unite" => null,
                "election" => $electionId,
                "unite" => $unite,
                "inscrit" => null,
                "votant" => null,
                "emargement" => null,
                "procuration" => null,
                "blanc" => null,
                "nul" => null,
                "exprime" => null,
                "envoi_aff" => false,
                "envoi_web" => false,
                "saisie" => 'en attente',
                "publier" => false,
                "validation" => 'saisie en cours',
                "date_derniere_modif" => microtime(true)
            );

            // Si l'ajout ne s'est pas déroulé correctement la transaction est annulée
            // Sinon la BD est mise à jour et le compteur des unités crées est incrementé
            if (! $election_unite->ajouter($data)) {
                $succes = false;
                break;
            }
            $nbUniteCrees++;
        }
        if ($succes) {
            $this->f->db->commit();
            $message = $nbUniteCrees.' unité(s) créée(s)';
        } else {
            $message = 'Erreur lors de l\'ajout des unités';
            $this->f->db->rollback();
        }

        $this->addToMessage($message);
        return $succes;
    }



    /**
     * Prépare la participation de l'élection
     * 
     * @return void
     */
    protected function participation($idElection, $heure_ouverture, $heure_fermeture) {
        $succes = $this->insert_participation_election($idElection, $heure_ouverture, $heure_fermeture);
        $succes = $succes ? $this->insert_participation_unite($idElection) : false;
        return $succes;
    }

    /**
     * Crée et initialise, dans la base de données, la participation de l'élection
     * pour chaque tranche horaire. Les tranches horaires sont récupèrées à partir
     * des horaires d'ouverture et de fermeture.
     * 
     * @return boolean $correct indique si le traitement à fonctionné correctement
     */
    protected function insert_participation_election($idElection = null, $heure_ouverture = null, $heure_fermeture = null) {
        $election = ! empty($idElection) ? $idElection : $this->getval('election');
        $heure_ouverture = ! empty($heure_ouverture) ? $heure_ouverture : $this->getval('heure_ouverture');
        $heure_fermeture = ! empty($heure_fermeture) ? $heure_fermeture : $this->getval('heure_fermeture');

        $succes = true;
        $nb_participations_crees = 0;
        
        // ordre heure ouverture
        $horaireOuv = $this->f->get_element_by_id('tranche', $heure_ouverture);
        $ordre_ouverture = $horaireOuv->getVal('ordre');
        // ordre heure fermeture
        $horaireFer = $this->f->get_element_by_id('tranche', $heure_fermeture);
        $ordre_fermeture = $horaireFer->getVal('ordre');
        // liste des tranches horaires à récupérer
        $sql = "SELECT tranche FROM ".DB_PREFIXE."tranche WHERE ";
        $sql .= " ordre >". $ordre_ouverture." and ordre <= ".$ordre_fermeture;
        $sql .= " order by ordre ";
        $res = $this->db->query($sql);
        $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
        $this->f->isDatabaseError($res);

        while ($row =& $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            // nouvelle participation election
            $participation_election = $this->f->get_element_by_id('participation_election', ']');
            // attribut de cette nouvelle participation
            $data = array(
                "participation_election" => null,
                "election" => $election,
                "tranche" => $row['tranche'],
                "date_derniere_modif" => microtime(true)
            );

            // Si le traitement ne s'est pas déroulé correctement, il est annulé
            // Sinon on passe à la tranche horaire suivante
            if (! $participation_election->ajouter($data)) {
                $succes = false;
                break;
            }
            $nb_participations_crees++;
        }

        if ($succes) {
            $this->f->db->commit();
            $message = $nb_participations_crees.' participation(s) élection créées';
        } else {
            $message = 'Erreur lors de l\'ajout des tranches horaires';
            $this->f->db->rollback();
        }

        $this->addToMessage($message);
        return $succes;
    }

    /**
     * Crée et initialise, dans la base de données, la participation pour chaque unité.
     * 
     * @return boolean indique si le traitement à fonctionné correctement
     */
    protected function insert_participation_unite($idElection = null) {
        $idElection = ! empty($idElection) ? $idElection : $this->getval('election');
        $succes = true;
        $nb_ajoute=0;

        // Creation des liens entre la participation par horaire et les unités de
        // l'élection
        $participations = $this->get_participation_election($idElection);
        foreach ($participations as $participation) {
            $unites = $this->get_unites_election($idElection);

            foreach ($unites as $unite) {
                $participation_unite = $this->f->get_element_by_id('participation_unite', ']');
                $data = array(
                    "participation_unite" => null,
                    "election_unite" => $unite->getVal('election_unite'),
                    "participation_election" => $participation->getVal('participation_election'),
                    "votant" => null,
                    "saisie" => false,
                    "envoi_aff" => false,
                    "envoi_web" => false,
                    "date_derniere_modif" => microtime(true)
                );

                // Si le traitement ne s'est pas déroulé correctement il est arreté
                if (! $participation_unite->ajouter($data)) {
                    $succes = false;
                    break 2;
                }
                $nb_ajoute++;
            }
        }

        if ($succes) {
            $this->f->db->commit();
            $message = $nb_ajoute.' participation(s) par unité créées';
        } else {
            $message = 'Erreur lors de la creation de la participation par unité';
            $this->f->db->rollback();
        }
        $this->addToMessage($message);
        return $succes;
    }

    /**
     * Ecris le fichier csv à envoyer à la préfecture et affiche le lien
     * permettant de télécharger ce fichier
     * 
     * @return void
     */
    protected function prefecture() {
        $debug = 0; // =1 pour debuguer

        // booleen indiquant si le message à afficher doit être un message d'erreur
        // ou de validation
        $class = 'valid';
        $this->addToMessage(_(" La mairie doit envoyer ses résultats par messagerie au service"));
        $this->addToMessage(_(" informatique de la Préfecture. Ce module permet de générer le fichier"));
        $this->addToMessage(_(" texte structuré type CSV (les données sont séparées par des ;) à"));
        $this->addToMessage(_(" transmettre."));
        $election=$this->getval('election');
        $sequence = 0;
        // Indicatif. [1 caractère]
        // I pour un résultat initial, R pour rectifié
        // On initialise la variable $indicatif avec la valeur "I"
        // et si elle est passée en POST alors on la récupère
        $indicatif = "I";
        if ($this->getval('envoi_initial')=='t') {
            $indicatif = "I";
        } else {
            $indicatif = "R";
        }

        // Ce libellé permet de réafficher la sélection à l'utilisateur dans le message
        $indicatif_libelle = "Transfert ";
        $indicatif_libelle .= ($indicatif == "I" ? "Initial" : "Rectifié");
        // Afficher le nombre de votant sur les feuilles d'émargement dans le
        // fichier CSV généré ???
        $gen_votant2 = false;
        /* ??? ne pas decommenter
        if (isset($_POST['gen_votant2']) && $_POST['gen_votant2'] === 'true' ) {
            $gen_votant2 = true;
            $checked = 'checked';
        }
        */
        // Année de l'élection. [4 caractères]
        $annee = substr($this->getval('date'), 0, 4);
        // N° du tour. [1 caractère]
        $tour = $this->getval('tour');
        // Type de scrutin. [2 caractères]
        // type_election prefecture = type de scrutin
        $sql= "select type_election.prefecture from ".DB_PREFIXE."type_election ";
        $sql .= " inner join ".DB_PREFIXE."election on election.type_election=type_election.type_election";
        $sql .= " where election.election = ".$election;
        $scrutin = $this->db->getOne($sql);
        $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
        $this->f->isDatabaseError($scrutin);
        if ($scrutin != "") {
            // Initialisation de la variable $export qui contient le contenu du
            // fichier à exporter.
            $export = "";
            // résultats par unités
            $infoUnites = $this->get_info_unites($election);
            // Vérification que les informations ont pu être récupérées. Si le tableau est vide,
            // c'est que ce n'est pas le cas. Par conséquent l'export en csv ne peut pas être fait
            if (empty($infoUnites)) {
                $class = 'error';
                $this->addToMessage('les résultats des unités n\'ont pas pu être récupérés');
                $this->f->layout->display_message($class, $this->msg);
                return;
            }
            //while ($row =& $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            foreach ($infoUnites as $infoUnite) {
                // seules les unités ayant un comportement de bureau de vote sont récupérées
                // pour l'affichage afin d'éviter d'afficher les résultats des périmètres
                if ($infoUnite['bureau'] == 't') {
                    $sequence++;
                    // N° de séquence
                    $export .= $sequence.";";
                    // Type de scrutin.
                    $export .= $scrutin.";";
                    // Année de lélection.
                    $export .= $annee.";";
                    //  UNIQUEMENT LES MUNICIPALES
                    if ($scrutin == "MN") {
                        // Type d'enregistrement. [1 caractère]
                        // V pour les enregistrements de type voix
                        $export .= "V;";
                    }
                    // N° du tour.
                    $export .= $tour.";";
                    // Code du département : 2 chiffres
                    $export .= str_pad($infoUnite['code']['departement'], 2, "0", STR_PAD_LEFT).";";
                    // Code de la commune : 3 chiffre
                    $export .= str_pad($infoUnite['code']['commune'], 3, "0", STR_PAD_LEFT).";";
                    // Code du unite de vote. [Vide ou 4 caractères]
                    $export .= str_pad($infoUnite['code']['code_unite'], 4, "0", STR_PAD_LEFT).";";
                    //  TOUTES LES ELECTIONS SAUF LES MUNICIPALES
                    if ($scrutin != "MN" && $scrutin != "MP") {
                        // Code du canton.
                        $export .= str_pad($infoUnite['code']['canton'], 2, "0", STR_PAD_LEFT).";";
                        // Code de la circonscription législative.
                        $export .= str_pad($infoUnite['code']['circonscription'], 2, "0", STR_PAD_LEFT).";";
                    }
                    // Indicatif
                    $export .= $indicatif.";";
                    // Nombre Inscrits. [1 à 8 caractères]
                    $export .= $infoUnite['resultat']['inscrit'].";";
                    // Nombre Abstentions. [1 à 8 caractères]
                    $export .= $infoUnite['resultat']['inscrit']-$infoUnite['resultat']['votant'].";";
                    // Nombre de Votants. [1 à 8 caractères]
                    $export .= $infoUnite['resultat']['votant'].";";
                    // Nombre de Votants d après les feuilles d émargements. [0 à 8 caractères]
                    $export .= $infoUnite['resultat']['emargement'].";";
                    // Nombre de bulletins « blancs ». [1 à 8 caractères]
                    $export .= $infoUnite['resultat']['blanc'].";";
                    // Nombre de bulletins « nuls ». [0 à 8 caractères]
                    $export .= $infoUnite['resultat']['nul'].";";
                    // Nombre d'Exprimés. [1 à 8 caractères]
                    $export .= $infoUnite['resultat']['exprime'].";";
                    $sql= "select resultat,prefecture from ".DB_PREFIXE."election_resultat ";
                    $sql .= " inner join ".DB_PREFIXE."election_candidat on election_candidat.election_candidat=election_resultat.election_candidat";
                    $sql .= " inner join ".DB_PREFIXE."election_unite on election_unite.election_unite=election_resultat.election_unite";
                    $sql .= " where election_unite.election = ".$election;
                    $sql .= " and election_unite.unite = ".$infoUnite['unite'];
                    $sql .= " order by election_candidat.ordre";
                    $res1 = $this->db->query($sql);
                    $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
                    $this->f->isDatabaseError($res1);
                    $nbcandidats=0;
                    $candidat_resultat="";
                    while ($row1 =& $res1->fetchRow(DB_FETCHMODE_ASSOC)) {
                        $nbcandidats++;
                        $candidat_voix = 0;
                        if ($row1['resultat'] != "") {
                            $candidat_voix = $row1['resultat'];
                        }
                        $candidat_resultat .= str_pad($row1['prefecture'], 3, "0", STR_PAD_LEFT).";".$candidat_voix.";";
                    }
                    // Nombre de listes ou de candidats.
                    $export .= $nbcandidats.";";
                    $export .= $candidat_resultat;
                    // Nombre de voix de la liste ou du candidat. [1 à 8 caractères]
                    $export = substr($export, 0, strlen($export)-1);
                    if ($debug==1) {
                        $export .= "<br>";
                    } else {
                        $export .= "\n";
                    }
                }
            }
            // => Enregistrement(s) pour la saisie des sièges  ******************
            // UNIQUEMENT LES MUNICIPALES
            if ($scrutin == "MN") {
                // On incrémente la séquence
                $sequence++;
                // N° de séquence
                $export .= $sequence.";";
                // Type de scrutin.
                $export .= $scrutin.";";
                // Année de l election.
                $export .= $annee.";";
                // Type d'enregistrement. [1 caractère]
                // S pour les enregistrements de type sièges
                $export .= "S;";
                // N° du tour.
                $export .= $tour.";";
                // Code du département.
                $export .= str_pad($infoUnite['code']['departement'], 2, "0", STR_PAD_LEFT).";";
                // Code de la commune
                $export .= str_pad($infoUnite['code']['commune'], 3, "0", STR_PAD_LEFT).";";
                // Nombre de listes ou de candidats. s);
                $export .= $nbcandidats.";";
                // Boucle sur les candidats
                $sql= "select prefecture, siege,siege_com from ".DB_PREFIXE."election_candidat ";
                $sql .= " where election = ".$election;
                $sql .= " order by ordre";
                $res2 = $this->db->query($sql);
                $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
                $this->f->isDatabaseError($res2);
                while ($row2 =& $res2->fetchRow(DB_FETCHMODE_ASSOC)) {
                    $export .= str_pad($row2['prefecture'], 3, "0", STR_PAD_LEFT).
                        ";".$row2['siege'].";";
                    // On laisse vide car réservé à PLM (Paris-Lyon-Marseille).
                    $export .= ";";
                    $export .= $row2['siege_com'].";";
                }
                // Retrait du dernier ;
                $export = substr($export, 0, strlen($export)-1);
                // Saut de ligne
                if ($debug==1) {
                    $export .= "<br>";
                } else {
                    $export .= "\n";
                }
            }
            if ($debug==1) {
                $this->addToMessage($export);
            }
            //Ecriture du fichier sur le disque
            $nom_fichier = "prefecture.csv";
            $metadata = array(
                "filename" => $nom_fichier,
                "size" => strlen($export),
                "mimetype" => "application/vnd.ms-excel",
            );
            $uid = $this->f->storage->create_temporary($export, $metadata);
            $this->addToMessage("<a href='".OM_ROUTE_FORM."&snippet=file&uid=".$uid."&amp;mode=temporary'><u>"._("Télécharger le fichier en cliquant ici")."</u></a> ");
        } else {
            // Message
            $msg = "L'élection en cours n'est pas compatible avec cet export.";
            $this->addToMessage($msg, "error");
            $class = 'error';
        }
        $this->f->layout->display_message($class, $this->msg);
        //$this->addToMessage($export);
    }

     /*liste electorale sieges_lm.inc
     * $siege_lm = array(0,0,0,28;
     * $siege_lc = array(0,0,0,14);
     * $code_lm = array(252,253,254,255,);
     * $noliste_lm = array(1,2,3,4);
     */


    /**
     * Ecris un fichier csv contenant les résultats par unités ainsi que le total
     * calculé sur l'ensemble de l'élection. Affiche également le lien permettant
     * de télécharger le fichier.
     * 
     * @return void
     */
    protected function export_csv_resultats() {
        $this->addToMessage(__("Extraction sur tableur type table résultat"));
        $election = $this->getval('election');
        // booleen indiquant si le message à afficher doit être un message d'erreur
        // ou de validation
        $class='valid';
        // 1ere ligne
        $csv="election;code;unite;inscrit;votant;emargement;procuration;exprime;nul;blanc;";

        // ajoute une colonne pour chaque candidat avec leur libelle comme entête
        $candidats = $this->get_info_candidats_election($election, "");
        foreach ($candidats as $candidat) {
            $csv .= $candidat['libelle'].";";
        }
        $csv .= "\n";
        // tableau contenant toutes les informations des unités de l'élection
        // ordre, libelle, resultats et si ce sont des bureaux de vote ou pas
        $infoUnites = $this->get_info_unites($election);
        // Vérification que les informations ont pu être récupérées. Si le tableau est vide,
        // c'est que ce n'est pas le cas. Par conséquent l'export en csv ne peut pas être fait
        if (empty($infoUnites)) {
            $class = 'error';
            $this->addToMessage('les résultats des unités n\'ont pas pu être récupérés');
            $this->f->layout->display_message($class, $this->msg);
            return;
        }

        // remplissage du csv :
        // une colonne pour chaque résultats (inscrit, votant, emargement, nul et blanc)
        // et une colonne pour chaque candidat
        foreach ($infoUnites as $infoUnite) {
            // seule les unités de type bureau de vote doivent apparaître dans le csv
            if ($infoUnite['bureau'] == 't') {
                $csv .= $election.";";
                //definition des paramètres des informations requises
                $csv .= $infoUnite['code']['code_unite'].";";
                $csv .= $infoUnite['libelle'].";";
                foreach ($infoUnite['resultat'] as $resultat) {
                    $csv .= $resultat.";";
                }

                // requête permettant de récupérer les résultats des candidats pour une
                // unité de l'élection
                $sql = "SELECT
                            resultat
                        FROM
                            ".DB_PREFIXE."election_resultat
                            INNER JOIN ".DB_PREFIXE."election_candidat
                                ON election_candidat.election_candidat
                                    = election_resultat.election_candidat
                            INNER JOIN ".DB_PREFIXE."election_unite
                                ON election_unite.election_unite
                                    = election_resultat.election_unite
                            WHERE
                                election_unite.election = ".$this->f->db->escapeSimple($election)."
                                AND election_unite.unite = ".$this->f->db->escapeSimple($infoUnite['unite'])."
                            ORDER BY
                                election_candidat.ordre";
                $res1 = $this->db->query($sql);
                $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
                if ($this->f->isDatabaseError($res1, true)) {
                    $class = 'error';
                    $this->addToLog(__METHOD__."database error:".$res1->getDebugInfo(), DEBUG_MODE);
                    $this->addToMessage('les résultats des candidats n\'ont pas pu être récupérés');
                    $this->f->layout->display_message($class, $this->msg);
                    return;
                }
                while ($row1 = $res1->fetchRow(DB_FETCHMODE_ASSOC)) {
                    $csv .= $row1['resultat'].";";
                }
                $csv = substr($csv, 0, strlen($csv)-1);
                $csv .= "\n";
            }
        }
        //Ecriture du fichier sur le disque
        $nom_fichier = "extraction.csv";
        $metadata = array(
            "filename" => $nom_fichier,
            "size" => strlen($csv),
            "mimetype" => "application/vnd.ms-excel",
        );
        $uid = $this->f->storage->create_temporary($csv, $metadata);
        $message = sprintf(
            "<a href='%s&snippet=file&uid=%s&amp;mode=temporary'><u>"
            ._("Télécharger le fichier en cliquant ici")
            ."</u></a> ",
            OM_ROUTE_FORM,
            $uid
        );
        $this->addToMessage($message);
        $this->f->layout->display_message($class, $this->msg);
    }

    //===============
    // action edition
    //================

    /**
     * Ecris et affiche le PDF de l'édition des résultats
     * 
     * @return void
     */
    function edition_resultat() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $this->setValFFromVal();
        $this->get_values_merge_fields();
        $result = $this->compute_pdf_output('etat', 'election', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF de la proclamation des résultats
     * 
     * @return void
     */
    function pdf_proclamation_resultat() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $result = $this->compute_pdf_output('etat', 'proclamation', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF de la proclamation des résultats par bureau
     *
     * @return void
     */
    public function pdf_proclamation_resultat_par_bureau() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $results = $this->get_full_results();
        $idUnitesAAfficher = array();
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                $idUnitesAAfficher[] = $election_unite["election_unite_id"];
            }
        }
        //Génération du PDF
        $result = $this->compute_pdf_output(
            'etat',
            'proclamation_bureau',
            $collectivite,
            implode('; ', $idUnitesAAfficher)
        );
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF de la proclamation des résultats avec le
     * nombre de siège
     *
     * @return void
     */
    public function pdf_proclamation_resultat_avec_siege() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $result = $this->compute_pdf_output('etat', 'proclamation_siege', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF de la proclamation des résultats par périmètre
     *
     * @return void
     */
    public function pdf_proclamation_resultat_par_perimetre() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Récupération des unités de l'élection
        $results = $this->get_full_results();
        $idUnitesAAfficher = array();
        foreach ($results["resultats"] as $election_unite) {
            $isPerimetre = $this->f->boolean_string_to_boolean($election_unite["unite_perimetre"]);
            if ($isPerimetre) {
                $idUnitesAAfficher[] = $election_unite["election_unite_id"];
            }
        }
        //Génération du PDF
        $result = $this->compute_pdf_output(
            'etat',
            'proclamation_perimetre',
            $collectivite,
            implode('; ', $idUnitesAAfficher)
        );
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF de la participation
     *
     * @return void
     */
    public function pdf_participation() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $this->setValFFromVal();
        $this->get_values_merge_fields();
        $result = $this->compute_pdf_output('etat', 'participation', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF des résultats par périmètres
     *
     * @return void
     */
    public function pdf_resultat_perimetre() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $result = $this->compute_pdf_output('etat', 'resultat_perimetre', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF des résultats destinés à la préfecture
     *
     * @return void
     */
    public function pdf_prefecture() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $result = $this->compute_pdf_output('etat', 'prefecture', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    /**
     * Ecris et affiche le PDF des resultats globaux
     *
     * @return void
     */
    public function pdf_resultats_globaux() {
        //Verification de l'accessibilité sur l'élément
        $this->checkAccessibility();
        //Récupération de ses paramètres
        $collectivite = $this->f->getCollectivite($_SESSION['collectivite']);
        //Génération du PDF
        $result = $this->compute_pdf_output('etat', 'resultats_globaux', $collectivite, $this->getVal(($this->clePrimaire)));
        //Affichage du PDF
        $this->expose_pdf_output(
            $result['pdf_output'],
            $result['filename']
        );
    }

    function get_inst__perimetre_election_unite() {
        $inst__election_unite = $this->f->get_inst__om_dbform(array(
            "obj" => "election_unite",
        ));
        $inst__perimetre_election_unite = $inst__election_unite->get_instance_election_unite(
            $this->getVal("perimetre"),
            $this->getVal($this->clePrimaire)
        );
        return $inst__perimetre_election_unite;
    }

    /**
     * Récupération des libellés des champs de fusion
     *
     * @return array $labels tableau associatif contenant les labels
     */
    function get_labels_merge_fields() {
        $labels = parent::get_labels_merge_fields();
        //
        $labels[$this->clePrimaire]["election.annee"] = __("Année de l'élection");
        $labels[$this->clePrimaire]["election.tour_texte"] = __("Tour de l'élection au format textuel (1 : 1er tour, 2 : 2ème tour)");
        $labels[$this->clePrimaire]["election.etat_participation"] = __("Etat de la participation par bureau par tranche horaire avec totaux et pourcentage (prévu pour le format A3 paysage, largeur fixée à 400mm)");
        $labels[$this->clePrimaire]["election.etat_resultat"] = __("Etat resultat par bureau avec totaux (avec colonne *id canton*)");
        $labels[$this->clePrimaire]["election.etat_resultat_perimetre_opt1"] = __("Etat resultat par bureau regroupe par canton avec totaux et pourcentage (avec colonne *vote sur emargement*)");
        $labels[$this->clePrimaire]["election.etat_resultat_perimetre_opt2"] = __("Etat resultat par bureau regroupe par canton avec totaux (avec colonne *vote sur emargement* et *procuration*)");
        $labels[$this->clePrimaire]["election.etat_resultat_perimetre_opt3"] = __("Etat resultat par bureau regroupe par canton avec totaux et pourcentage (avec colonne *vote sur emargement* et *procuration*)");
        $labels[$this->clePrimaire]["election.etat_resultat_globaux_opt1"] = __("Etat resultat par bureau avec total en pourcentage (avec colonne *vote sur emargement* et *procuration*)");
        $labels[$this->clePrimaire]["election.etat_resultat_globaux_opt2"] = __("Etat resultat par bureau avec total en pourcentage");
        $labels[$this->clePrimaire]["election.etat_resultat_prefecture"] = __("Etat resultat par bureau (sans le *nom de bureau*) avec totaux (avec colonne *vote sur emargement*)");
        $labels[$this->clePrimaire]["election.etat_resultat_prefecture_par_perimetre"] = __("Etat resultat par bureau (sans le *nom de bureau*) par périmètre avec totaux (avec colonne *vote sur emargement*)");
        $labels[$this->clePrimaire]["election.etat_proclamation"] = __("Tableau de proclamation des résultats définitifs");
        $labels[$this->clePrimaire]["election.etat_proclamation_sieges_cm_cc"] = __("Tableau de proclamation des résultats définitifs avec répartition des sièges");
        $labels[$this->clePrimaire]["election.etat_proclamation_sieges_cmp"] = __("Tableau de proclamation des résultats définitifs avec répartition des sièges métropolitains");
        //
        $inst_election_unite = $this->f->get_inst__om_dbform(array(
            "obj" => "election_unite",
            "idx" => 0,
        ));
        $labels_origin = $inst_election_unite->get_merge_fields("labels");
        $labels_dest = array();
        foreach ($labels_origin[__("election_unite")] as $key => $value) {
            $new_key = str_replace("election_unite.", "perimetre.resultat.", $key);
            $labels_dest[$new_key] = $value;
        }
        $labels[__("perimetre")] = $labels_dest;
        $labels_and_co = $inst_election_unite->get_merge_fields_and_co("labels", array("election"));
        foreach ($labels_and_co as $elem => $labels_origin) {
            $labels_dest = array();
            foreach ($labels_origin as $key => $value) {
                $new_key = str_replace($elem.".", "perimetre.".$elem.".", $key);
                $labels_dest[$new_key] = $value;
            }
            $labels[__("perimetre")] = array_merge(
                $labels[__("perimetre")],
                $labels_dest
            );
        }
        //
        return $labels;
    }

    /**
     * Récupération des valeurs des champs de fusion
     *
     * @return array $values tableau associatif contenant les résultats
     */
    function get_values_merge_fields() {
        $values = parent::get_values_merge_fields();
        //
        $inst_election_unite = $this->get_inst__perimetre_election_unite();
        $perimetre_election_unite_id = $inst_election_unite->getVal("election_unite");
        //
        $values["election.annee"] = substr($this->getval("date"), 0, 4);
        $values["election.tour_texte"] = $values["election.tour"] == "1" ? "1er tour" : "2ème tour" ;
        $values['election.etat_participation'] = $this->get_mf_etat_participation();
        $values['election.etat_resultat'] = $this->get_mf_etat_resultat();
        $values['election.etat_resultat_perimetre_opt1'] = $this->get_mf_etat_resultat_par_perimetre_selon_option(1);
        $values['election.etat_resultat_perimetre_opt2'] = $this->get_mf_etat_resultat_par_perimetre_selon_option(2);
        $values['election.etat_resultat_perimetre_opt3'] = $this->get_mf_etat_resultat_par_perimetre_selon_option(3);
        $values['election.etat_resultat_globaux_opt1'] = $this->get_mf_etat_resultat_globaux_option_1();
        $values['election.etat_resultat_globaux_opt2'] = $this->get_mf_etat_resultat_globaux_option_2();
        $values['election.etat_resultat_prefecture'] = $this->get_mf_etat_resultat_prefecture();
        $values['election.etat_resultat_prefecture_par_perimetre'] = $this->get_mf_etat_resultat_prefecture_par_perimetre();
        $values["election.etat_proclamation"] = $this->get_tableau_resultat_candidat($perimetre_election_unite_id);
        $values["election.etat_proclamation_sieges_cm_cc"] = $this->get_tableau_resultat_candidat($perimetre_election_unite_id, true, true);
        $values["election.etat_proclamation_sieges_cmp"] = $this->get_tableau_resultat_candidat($perimetre_election_unite_id, false, false, true);
        //
        $values_origin = $inst_election_unite->get_merge_fields("values");
        $values_dest = array();
        foreach ($values_origin as $key => $value) {
            $new_key = str_replace("election_unite.", "perimetre.resultat.", $key);
            $values_dest[$new_key] = $value;
        }
        $values = array_merge(
            $values,
            $values_dest
        );
        $values_and_co = $inst_election_unite->get_merge_fields_and_co("values", array("election"));
        $values_dest = array();
        foreach ($values_and_co as $key => $value) {
            $new_key = "perimetre.".$key;
            $values_dest[$new_key] = $value;
        }
        $values = array_merge(
            $values,
            $values_dest
        );
        return $values;
    }

    /**
     * Renvoie le code html du tableau des résultats à destination de la préfecture
     *
     * @return string code html du tableau
     */
    protected function get_mf_etat_resultat_prefecture() {
        $results = $this->get_full_results();
        // Colonne de l'entete du tableau affichant les noms des candidats
        $htmlEnteteCandidat = "";
        $totalCandidat = array();
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEnteteCandidat .= sprintf(
                '<th>%s %s</th>',
                $election_candidat["candidat_libelle"],
                $election_candidat["candidat_libelle_liste"]
            );

            // Initialisation du tableau contenant le total des candidats
            $totalCandidat[$election_candidat["election_candidat_id"]] = 0;
        }
        // Contenu du tableau avec les résultats des unités et des candidats
        // Seul les bureaux de votes sont affichés dans le tableau
        $htmlContenuTab = '';
        $htmlTotalTab = '';
        $oddEven = 'even';
        $htmlResultatTotal = '';
        $htmlResultatTotalCan = '';
        // Initialisation du tableau contenant le total des résultats
        $total = array(
            'inscrit' => 0,
            'emargement' => 0,
            'votant' => 0,
            'blanc' => 0,
            'nul' => 0,
            'exprime' => 0
        );
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                // Récupération des résultats et préparation de l'affichage dans le tableau
                $htmlResultats = '';
                $htmlResultatTotal = '';
                foreach (array('inscrit', 'emargement', 'votant', 'blanc', 'nul', 'exprime') as $field) {
                    $htmlResultats .= sprintf(
                        "<td>%s</td>",
                        (is_numeric($election_unite["election_unite_".$field]) ? $election_unite["election_unite_".$field] : 0)
                    );

                    // Calcul du total
                    $total[$field] += is_numeric($election_unite["election_unite_".$field]) ?
                        $election_unite["election_unite_".$field] :
                        0;
                    $htmlResultatTotal .= sprintf(
                        "<td>%s</td>",
                        $total[$field]
                    );
                }
                // Récupération des résultats des candidats et préparation de l'affichage
                $htmlResultatCan = '';
                $htmlResultatTotalCan = '';
                foreach ($results["candidats"] as $election_candidat) {
                    $resultat = $election_unite["candidat".$election_candidat["election_candidat_ordre"]];
                    $htmlResultatCan .= sprintf(
                        "<td>%s</td>",
                        ! empty($resultat) ? $resultat : 0
                    );

                    // Calcul du total obtenu par chaque candidat et remplissage de la ligne du total
                    $totalCandidat[$election_candidat["election_candidat_id"]] += ! empty($resultat) ? $resultat : 0;
                    $htmlResultatTotalCan .= sprintf(
                        "<td>%s</td>",
                        $totalCandidat[$election_candidat["election_candidat_id"]]
                    );
                }
        
                $oddEven = ($oddEven === 'even') ? 'odd' : 'even';
                $htmlContenuTab .= sprintf(
                    '<tr class="%1$s">
                        <td class="bureaux">%2$s</td>
                        %3$s
                        %4$s
                    </tr>',
                    $oddEven,
                    $election_unite["unite_code"],
                    $htmlResultats,
                    $htmlResultatCan
                );
            }
        }

        $htmlTotalTab = sprintf(
            '<tr class="total">
                <td class="bureaux">TOTAL</td>
                %1$s
                %2$s
            </tr>',
            $htmlResultatTotal,
            $htmlResultatTotalCan
        );

        // Construction du tableau des résultats pour la préfecture
        return sprintf(
            '<style>
            table#resultats-prefecture {
                font-size: 7px;
                text-align: right;
                width: 100%%;
            }
            table#resultats-prefecture td.bureaux {
                text-align: center;
            }
            table#resultats-prefecture tr.entete {
                background-color: #c3e0a9;
                text-align: center;
            }
            table#resultats-prefecture tr.total {
                background-color: #c4d5d7;
            }
            table#resultats-prefecture tr.odd {
                background-color: #f3f3f6;
            }
            </style>
            <table id="resultats-prefecture" border="1" cellpadding="1">
                <tr class="entete">
                    <th>NO BUREAU VOTE</span></th>
                    <th>INSCRITS</th>
                    <th>VOTANTS EMARGEMENT (1)</th>
                    <th>ENVELOPPES URNES (2)</th>
                    <th>BLANCS</th>
                    <th>NULS (3)</th>
                    <th>SUFFRAGE EXPRIMES</th>
                    %1$s
                </tr>
                %2$s
                %3$s
            </table>',
            $htmlEnteteCandidat,
            $htmlContenuTab,
            $htmlTotalTab
        );
    }

    /**
     * Renvoie le code html des tableaux des résultats, par périmètre,
     * à destination de la préfecture.
     *
     * @return string code html du tableau
     */
    protected function get_mf_etat_resultat_prefecture_par_perimetre() {
        $results = $this->get_full_results();
        $etatPrefecturePerimetre = '';
        foreach ($results["resultats"] as $election_unite) {
            $isPerimetre = $this->f->boolean_string_to_boolean($election_unite["unite_perimetre"]);
            if ($isPerimetre) {
                $etatPrefecturePerimetre .= $this->get_tableau_prefecture_par_perimetre($election_unite, $results);
            }
        }

        return $etatPrefecturePerimetre;
    }


    /**
     * Renvoie le code html du tableau des résultats à destination de la préfecture
     * pour un périmètre donné.
     *
     * @param array informations et résultats du périmètre à afficher
     * @param array résultat enregistrés pour l'élection
     * @return string code html du tableau
     */
    protected function get_tableau_prefecture_par_perimetre($perimetre, $results) {
        // Colonne de l'entete du tableau affichant les noms des candidats
        $htmlEnteteCandidat = "";
        $htmlResultatTotalCan = "";
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEnteteCandidat .= sprintf(
                '<th>%s %s</th>',
                $election_candidat["candidat_libelle"],
                $election_candidat["candidat_libelle_liste"]
            );

            // Remplissage du total des candidats en récupérant le résultat du périmètre
            $resultat = $perimetre["candidat".$election_candidat["election_candidat_ordre"]];
            $htmlResultatTotalCan .= sprintf(
                '<td>%s</td>',
                ! empty($resultat) ? $resultat : 0
            );
        }
        // Contenu du tableau avec les résultats des unités et des candidats
        // Seul les bureaux de votes sont affichés dans le tableau
        $htmlContenuTab = '';
        $htmlResultatTotal = '';
        $colonne_unite_titre = "";
        $oddEven = 'even';
        $fields = array('inscrit', 'emargement', 'votant', 'blanc', 'nul', 'exprime');
        foreach ($perimetre["descendants"] as $unite_enfant_id) {
            $election_unite = $this->get_full_results(array(
                "r" => "result_line",
                "s" => "unite_id",
                "v" => intval($unite_enfant_id),
            ));
            // Récupération des résultats et préparation de l'affichage dans le tableau
            $htmlResultats = '';
            foreach ($fields as $field) {
                $htmlResultats .= sprintf(
                    "<td>%s</td>",
                    (is_numeric($election_unite["election_unite_".$field]) ? $election_unite["election_unite_".$field] : 0)
                );
            }
            // Récupération des résultats des candidats et préparation de l'affichage
            $htmlResultatCan = '';
            foreach ($results["candidats"] as $election_candidat) {
                $resultat = $election_unite["candidat".$election_candidat["election_candidat_ordre"]];
                $htmlResultatCan .= sprintf(
                    "<td>%s</td>",
                    ! empty($resultat) ? $resultat : 0
                );
            }
            // Contenu du tableau
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                $libelle_unite = $election_unite["unite_code"];
                $colonne_unite_titre = "NO BUREAU VOTE";
            } else {
                $libelle_unite = $election_unite["unite_libelle"];
                $colonne_unite_titre = $election_unite["type_unite_libelle"];
            }
            $oddEven = ($oddEven === 'even') ? 'odd' : 'even';
            $htmlContenuTab .= sprintf(
                '<tr class="%1$s">
                    <td class="bureaux">%2$s</td>
                    %3$s
                    %4$s
                </tr>',
                $oddEven,
                $libelle_unite,
                $htmlResultats,
                $htmlResultatCan
            );
        }


        // Remplissage de la ligne du total en utilisant les résultats des périmètres
        foreach ($fields as $field) {
            $htmlResultatTotal .= sprintf(
                '<td>%s</td>',
                (is_numeric($perimetre["election_unite_".$field]) ? $perimetre["election_unite_".$field] : 0)
            );
        }

        // Construction du tableau des résultats pour la préfecture
        $table_style = sprintf(
            '<style>
            table#resultats-prefecture {
                font-size: 7px;
                text-align: right;
                width: 100%%;
            }
            table#resultats-prefecture td.bureaux {
                text-align: center;
            }
            table#resultats-prefecture tr.entete {
                background-color: #c3e0a9;
                text-align: center;
            }
            table#resultats-prefecture tr.total {
                background-color: #c4d5d7;
            }
            table#resultats-prefecture tr.odd {
                background-color: #f3f3f6;
            }
            div.breakafter {
                page-break-after: always;
            }
            </style>'
        );
        return sprintf(
            '%1$s<div id="perimetre">%6$s</div>
            <table id="resultats-prefecture" border="1" cellpadding="1">
                <tr class="entete">
                    <th>%7$s</span></th>
                    <th>INSCRITS</th>
                    <th>VOTANTS EMARGEMENT (1)</th>
                    <th>ENVELOPPES URNES (2)</th>
                    <th>BLANCS</th>
                    <th>NULS (3)</th>
                    <th>SUFFRAGE EXPRIMES</th>
                    %2$s
                </tr>
                %3$s
                <tr class="total">
                    <td class="bureaux">TOTAL</td>
                    %4$s
                    %5$s
                </tr>
            </table>
            <div class="breakafter"></div>',
            $table_style,
            $htmlEnteteCandidat,
            $htmlContenuTab,
            $htmlResultatTotal,
            $htmlResultatTotalCan,
            $perimetre["unite_libelle"],
            $colonne_unite_titre
        );
    }

    /**
     * Renvoie le code html du tableau des résultats globaux option 2
     *
     * @return string code html du tableau
     */
    protected function get_mf_etat_resultat_globaux_option_2() {
        $results = $this->get_full_results();
        // Colonne de l'entete du tableau affichant les noms des candidats
        $htmlEnteteCandidat = "";
        $totalCandidat = array();
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEnteteCandidat .= sprintf(
                '<th colspan="2" class="candidat">%s</th>',
                $election_candidat["candidat_libelle"]
            );

            // Initialisation du tableau contenant le total des candidats
            $totalCandidat[$election_candidat["election_candidat_id"]] = 0;
        }

        // Contenu du tableau avec les résultats des unités et des candidats
        // Seul les bureaux de votes sont affichés dans le tableau
        $htmlContenuTab = '';
        $oddEven = 'even';
        // Initialisation du tableau contenant le total des résultats
        $total = array(
            'inscrit' => 0,
            'abstention' => 0,
            'votant' => 0,
            'blanc' => 0,
            'nul' => 0,
            'exprime' => 0
        );
        // Initialisation du code html des résultats
        $htmlResultats = array('TotalCan', 'TotalCanPourcentage', 'Total', 'TotalPourcentage');
        foreach ($htmlResultats as $htmlResultat) {
            ${'htmlResultat'.$htmlResultat} = '';
        }
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                // Récupération des résultats et préparation de l'affichage dans le tableau
                $htmlResultats = '';
                $htmlResultatTotal = '';
                $htmlResultatTotalPourcentage = '';
                foreach (array('inscrit', 'abstention', 'votant', 'blanc', 'nul', 'exprime') as $field) {
                    $htmlResultats .= sprintf(
                        '<td>%s</td>',
                        number_format(intval($election_unite["election_unite_".$field]), 0, ",", " ")
                    );

                    // Calcul du total
                    $total[$field] += is_numeric($election_unite["election_unite_".$field]) ?
                        $election_unite["election_unite_".$field] :
                        0;
                    $htmlResultatTotal .= sprintf(
                        '<td>%s</td>',
                        number_format(intval($total[$field]), 0, ",", " ")
                    );

                    // Calcul du total en pourcentage
                    $pourcentageTotal = '0,00%';
                    if ($field == 'inscrit') {
                        $pourcentageTotal = '';
                    } elseif (($field == 'abstention' || $field == 'votant') && $total['inscrit'] != 0) {
                        $pourcentageTotal = number_format($total[$field] / $total['inscrit'] * 100, 2, ",", " ")."%";
                    } elseif (($field == 'blanc' || $field == 'nul' || $field == 'exprime') && $total['votant'] != 0) {
                        $pourcentageTotal = number_format($total[$field] / $total['votant'] * 100 , 2, ",", " ")."%";
                    }
                    $htmlResultatTotalPourcentage .= sprintf(
                        '<td>%s</td>',
                        $pourcentageTotal
                    );
                }

                // Récupération des résultats des candidats et préparation de l'affichage
                $htmlResultatCan = '';
                $htmlResultatTotalCan = '';
                $htmlResultatTotalCanPourcentage = '';
                foreach ($results["candidats"] as $election_candidat) {
                    $resultat = $election_unite["candidat".$election_candidat["election_candidat_ordre"]];
                    $resultatPourcentage = $election_unite["pc_candidat".$election_candidat["election_candidat_ordre"]];
                    $htmlResultatCan .= sprintf(
                        '<td>%s</td><td>%s</td>',
                        number_format($resultat, 0, ",", " "),
                        number_format($resultatPourcentage, 2, ",", " ")."%"
                    );

                    // Calcul du total obtenu par chaque candidat et remplissage de la ligne du total
                    $totalCandidat[$election_candidat["election_candidat_id"]] += $resultat;
                    $htmlResultatTotalCan .= sprintf(
                        '<td colspan="2">%s</td>',
                        number_format(intval($totalCandidat[$election_candidat["election_candidat_id"]]), 0, ",", " ")
                    );

                    // Calcul du total obtenu en pourcentage et remplissage de la ligne du total en pourcentage
                    $pourcentageTotal = '0,00%';
                    if ($total['exprime'] != 0) {
                        $pourcentageTotal = number_format(
                            $totalCandidat[$election_candidat["election_candidat_id"]] / $total['exprime'] * 100,
                            2,
                            ",",
                            " "
                        )."%";
                    }
                    $htmlResultatTotalCanPourcentage .= sprintf(
                        '<td colspan="2">%s</td>',
                        $pourcentageTotal
                    );
                }
                $oddEven = ($oddEven === 'even') ? 'odd' : 'even';
                $htmlContenuTab .= sprintf(
                    '<tr class="%1$s">
                        <td class="ordre">%2$s</td>
                        <td class="bureaux">%3$s</td>
                        %4$s
                        %5$s
                    </tr>',
                    $oddEven,
                    $election_unite["unite_code"],
                    $election_unite["unite_libelle"],
                    $htmlResultats,
                    $htmlResultatCan
                );
            }
        }

        $htmlTotalTab = sprintf(
            '<tr class="total">
                <td></td>
                <td>TOTAL DES VOIX</td>
                %1s
                %2s
            </tr>
            <tr class="total">
                <td></td>
                <td>TOTAL EN POURCENTAGE</td>
                %3s
                %4s
            </tr>',
            $htmlResultatTotal,
            $htmlResultatTotalCan,
            $htmlResultatTotalPourcentage,
            $htmlResultatTotalCanPourcentage
        );
        // Les largeurs de colonnes sont fixées, la base est un 
        // format a3 paysage largeur 420 - 10 de marge de chaque côté
        $width_global = 400;
        $width_code = 10;
        $width_bureaux = 50;
        $width_inscrits = 18;
        $width_voix = 0;
        if (count($results["candidats"]) != 0 ) {
            $width_voix = ($width_global - $width_code - $width_bureaux - $width_inscrits*6) / count($results["candidats"]);
        }
        // Construction du tableau des résultats globaux
        $table_style = sprintf(
            '<style>
                table#resultats-globaux-option2 {
                    font-size: 8vw;
                    text-align: right;
                    width: %1$smm;
                }
                table#resultats-globaux-option2 th.bureaux {
                    width: %3$smm;
                }
                table#resultats-globaux-option2 th.unite_chiffres {
                    width: %4$smm;
                }
                table#resultats-globaux-option2 th.ordre {
                    width: %2$smm;
                }
                table#resultats-globaux-option2 th.candidat {
                    width: %5$smm;
                }
                table#resultats-globaux-option2 td.bureaux {
                    text-align: left;
                }
                table#resultats-globaux-option2 td.ordre {
                    text-align: center;
                }
                table#resultats-globaux-option2 tr.entete,
                table#resultats-globaux-option2 tr.total{
                    background-color: #DCDCDC;
                    text-align: center;
                    font-weight: bold;
                }
                table#resultats-globaux-option2 tr.even {
                    background-color: #FF8250;
                }
            </style>',
            $width_global,
            $width_code,
            $width_bureaux,
            $width_inscrits,
            $width_voix
        );
        return sprintf(
            '%s<table id="resultats-globaux-option2" border="1" cellpadding="1">
                <tr class="entete">
                    <th class="ordre">N°</th>
                    <th class="bureaux">BUREAUX DE VOTE</th>
                    <th class="unite_chiffres">INSCRITS</th>
                    <th class="unite_chiffres">ABSTENT</th>
                    <th class="unite_chiffres">VOTANTS</th>
                    <th class="unite_chiffres">BLANCS</th>
                    <th class="unite_chiffres">NULS</th>
                    <th class="unite_chiffres">EXPRIMES</th>
                    %s
                </tr>
                %s
                %s
            </table>',
            $table_style,
            $htmlEnteteCandidat,
            $htmlContenuTab,
            $htmlTotalTab
        );
    }

    /**
     * Renvoie le code html du tableau des résultats globaux option 1
     *
     * @return string code html du tableau
     */
    protected function get_mf_etat_resultat_globaux_option_1() {
        $results = $this->get_full_results();
        // Colonne de l'entete du tableau affichant les noms des candidats
        $htmlEnteteCandidat = "";
        $totalCandidat = array();
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEnteteCandidat .= sprintf(
                '<th>%s</th>',
                $election_candidat["candidat_libelle"]
            );

            // Initialisation du tableau contenant le total des candidats
            $totalCandidat[$election_candidat["election_candidat_id"]] = 0;
        }

        // Contenu du tableau avec les résultats des unités et des candidats
        // Seul les bureaux de votes sont affichés dans le tableau
        $htmlContenuTab = '';
        $htmlTotalTab = '';
        $oddEven = 'even';
        // Initialisation du tableau contenant le total des résultats
        $total = array(
            'inscrit' => 0,
            'abstention' => 0,
            'votant' => 0,
            'emargement' => 0,
            'blanc' => 0,
            'nul' => 0,
            'exprime' => 0,
            'procuration' => 0
        );
        // Initialisation du code html des résultats
        $htmlResultats = array('TotalCan', 'TotalCanPourcentage', 'Total', 'TotalPourcentage');
        foreach ($htmlResultats as $htmlResultat) {
            ${'htmlResultat'.$htmlResultat} = '';
        }
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                // Récupération des résultats et préparation de l'affichage dans le tableau
                $htmlResultats = '';
                $htmlResultatTotal = '';
                $htmlResultatTotalPourcentage = '';
                foreach (array('inscrit', 'abstention', 'votant', 'emargement', 'blanc', 'nul', 'exprime', 'procuration') as $field) {
                    $htmlResultats .= sprintf(
                        '<td>%s</td>',
                        number_format(intval($election_unite["election_unite_".$field]), 0, ",", " ")
                    );

                    // Calcul du total
                    $total[$field] += is_numeric($election_unite["election_unite_".$field]) ?
                        $election_unite["election_unite_".$field] :
                        0;
                    $htmlResultatTotal .= sprintf(
                        '<td>%s</td>',
                        number_format(intval($total[$field]), 0, ",", " ")
                    );

                    // Calcul du total en pourcentage
                    $pourcentageTotal = '0,00%';
                    if ($field == 'inscrit') {
                        $pourcentageTotal = '';
                    } elseif (($field == 'abstention' || $field == 'votant'|| $field == 'emargement' || $field == 'procuration')
                        && $total['inscrit'] != 0) {
                        $pourcentageTotal = number_format($total[$field] / $total['inscrit'] * 100, 2, ",", " ")."%";
                    } elseif (($field == 'blanc' || $field == 'nul' || $field == 'exprime') && $total['votant'] != 0) {
                        $pourcentageTotal = number_format($total[$field] / $total['votant'] * 100 , 2, ",", " ")."%";
                    }
                    $htmlResultatTotalPourcentage .= sprintf(
                        '<td>%s</td>',
                        $pourcentageTotal
                    );
                }
                // Récupération des résultats des candidats et préparation de l'affichage
                $htmlResultatCan = '';
                $htmlResultatTotalCan = '';
                $htmlResultatTotalCanPourcentage = '';
                foreach ($results["candidats"] as $election_candidat) {
                    $resultat = intval($election_unite["candidat".$election_candidat["election_candidat_ordre"]]);
                    $htmlResultatCan .= sprintf(
                        '<td>%s</td>',
                        number_format($resultat, 0, ",", " ")
                    );

                    // Calcul du total obtenu par chaque candidat et remplissage de la ligne du total
                    $totalCandidat[$election_candidat["election_candidat_id"]] += $resultat;
                    $htmlResultatTotalCan .= sprintf(
                        '<td>%s</td>',
                        number_format(intval($totalCandidat[$election_candidat["election_candidat_id"]]), 0, ",", " ")
                    );

                    // Calcul du total obtenu en pourcentage et remplissage de la ligne du total en pourcentage
                    $pourcentageTotal = '0,00%';
                    if ($total['exprime'] != 0) {
                        $pourcentageTotal = number_format(
                            $totalCandidat[$election_candidat["election_candidat_id"]] / $total['exprime'] * 100,
                            2,
                            ",",
                            " "
                        )."%";
                    }
                    $htmlResultatTotalCanPourcentage .= sprintf(
                        "<td>%s</td>",
                        $pourcentageTotal
                    );
                }

                // Contenu du tableau
                $oddEven = ($oddEven === 'even') ? 'odd' : 'even';
                $htmlContenuTab .= sprintf(
                    '<tr class="%1$s">
                        <td class="ordre">%2$s</td>
                        <td class="bureaux">%3$s</td>
                        <td class="canton">%4$s</td>
                        %5$s
                        %6$s
                    </tr>',
                    $oddEven,
                    $election_unite["unite_code"],
                    $election_unite["unite_libelle"],
                    $election_unite["canton_code"],
                    $htmlResultats,
                    $htmlResultatCan
                );
            }
        }

        $htmlTotalTab = sprintf(
            '<tr class="total">
                <td></td>
                <td></td>
                <td></td>
                %1s
                %2s
            </tr>
            <tr class="total">
                <td></td>
                <td></td>
                <td></td>
                %3s
                %4s
            </tr>',
            $htmlResultatTotal,
            $htmlResultatTotalCan,
            $htmlResultatTotalPourcentage,
            $htmlResultatTotalCanPourcentage
        );

        // Construction du tableau des résultats globaux
        return sprintf(
            '<style>
                table#resultats-globaux-option1 {
                    font-size: 8vw;
                    text-align: right;
                    width: 100%%;
                }
                table#resultats-globaux-option1 th.bureaux {
                    width: 10%%;
                }
                table#resultats-globaux-option1 td.bureaux {
                    text-align: left;
                }
                table#resultats-globaux-option1 th.ordre {
                    width: 2%%;
                }
                table#resultats-globaux-option1 td.ordre {
                    text-align: center;
                }
                table#resultats-globaux-option1 tr.entete {
                    background-color: #DCDCDC;
                    text-align: center;
                }
                table#resultats-globaux-option1 tr.total {
                    background-color: #a0b4c8;
                }
                table#resultats-globaux-option1 tr.even {
                    background-color: #f3f3f6;
                }
            </style>
            <table id="resultats-globaux-option1" border="1" cellpadding="1">
                <tr class="entete">
                    <th class="ordre">N°</th>
                    <th class="bureaux">BUREAUX DE VOTE</th>
                    <th>CANT</th>
                    <th>INS</th>
                    <th>ABS</th>
                    <th>V/UR</th>
                    <th>V/EM</th>
                    <th>BLA</th>
                    <th>NUL</th>
                    <th>EXP</th>
                    <th>PROC</th>
                    %1$s
                </tr>
                %2$s
                %3$s
            </table>',
            $htmlEnteteCandidat,
            $htmlContenuTab,
            $htmlTotalTab
        );
    }

    /**
     * Forme le champ de fusion des résultats par périmètre selon l'option choisi
     * 1 : avec pourcentage
     * 2 : sans pourcentage et avec la colonne procuration
     * 3 : avec pourcentage, avec procuration et avec pourcentage de vote dans l'urne
     *
     * @return string champ de fusion des résultats par périmètre
     */
    protected function get_mf_etat_resultat_par_perimetre_selon_option($option = 1) {
        $conf = array(
            1 => array(
                "pourcentage" => true,
                "pourcentageUrne" => false,
                "affProc" => false,
            ),
            2 => array(
                "pourcentage" => false,
                "pourcentageUrne" => false,
                "affProc" => true,
            ),
            3 => array(
                "pourcentage" => true,
                "pourcentageUrne" => true,
                "affProc" => true,
            ),
        );
        $results = $this->get_full_results();
        // Récupération de la légende des numéros d'ordre des candidats
        $output = $this->get_liste_candidat();
        // Initialisation de l'id du périmètre (-1 pour s'assurer de ne pas récupérer une autre unité par erreur)
        $perimetre = -1;
        // Récupère un tableau de résultat pour chaque périmètre sauf pour le périmètre
        // de l'élection qui sera utilisé pour le tableau du total
        foreach ($results["resultats"] as $election_unite) {
            $isPerimetre = $this->f->boolean_string_to_boolean($election_unite["unite_perimetre"]);
            if ($isPerimetre) {
                $output .= $this->get_tableau_resultats_perimetre(
                    $election_unite["unite_id"],
                    $conf[$option]["pourcentage"],
                    $conf[$option]["pourcentageUrne"],
                    true,
                    $conf[$option]["affProc"]
                );
            }
            if ($election_unite["unite_id"] === $this->getVal('perimetre')) {
                $perimetre = $election_unite["unite_id"];
            }
        }
        // Tableau du total de l'élection
        $output .= $this->get_tableau_resultats_perimetre(
            $perimetre,
            $conf[$option]["pourcentage"],
            $conf[$option]["pourcentageUrne"],
            false,
            $conf[$option]["affProc"]
        );
        return $output;
    }

    /**
     * Renvoie un texte correspondant à la liste des candidats de l'élection
     *
     * @return array $resultat tableau de l'édition des résultats.
     */
    protected function get_liste_candidat() {
        $results = $this->get_full_results();
        $listeCandidat = '<div><b>';
        foreach ($results["candidats"] as $election_candidat) {
            $listeCandidat .= sprintf(
                '%s-%s; ',
                $election_candidat["election_candidat_ordre"],
                $election_candidat["candidat_libelle"]
            );
        }
        return $listeCandidat.'</b></div>';
    }

    /**
     * Renvoie le tableau de l'édition de la participation.
     *
     * @return array $resultat tableau de l'édition des résultats.
     */
    protected function get_mf_etat_participation() {
        $results = $this->get_full_results();
        $totals = array();
        $totalInscrit = 0;
        // Remplissage de l'entete : creation d'une colonne par tranche horaire
        $htmlEnteteColonne = '';
        foreach ($results["participations"] as $participation_election) {
            $htmlEnteteColonne .= sprintf(
                "<td class=\"tranche_voix\">%s</td>
                <td class=\"tranche_taux\">%%</td>",
                substr($participation_election["tranche_libelle"], 0, 5)
            );
            // Initialisation du tableau contenant la participation total
            $totals[$participation_election["participation_election_id"]] = 0;
        }

        // Remplissage du corps : Une ligne par unité contenant le nom de l'unité
        // et le nombre de votant enregistré par tranche hraire pour cet unité
        // Seul les bureaux de vote sont affichés et le total est calculé à partir
        // des unités affichées
        $htmlParticipation = '';
        $oddEven = 'even';
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            $htmlVotantTranche = '';
            if ($isBV) {
                foreach ($results["participations"] as $participation_election) {
                    // Remplissage du nombre de votant pour chaque tranche horaire
                    $votant = $results["participations"][$participation_election["participation_election_id"]]["unites"][$election_unite["election_unite_id"]]["votant"];
                    // Si le nombre de votant est null ou n'a pas pu être récupéré il est mis à 0
                    // pour éviter des erreurs lors du calcul du pourcentage
                    $votant = is_numeric($votant) ? $votant : 0;
                    $pourcentageVotant = ! empty($election_unite["election_unite_inscrit"]) ?
                        number_format(round($votant  * 100 / $election_unite["election_unite_inscrit"], 2), 2, ",", " ")
                        : '0,00';

                    $htmlVotantTranche .= sprintf(
                        "<td class=\"tranche_voix\">%s</td>
                        <td class=\"tranche_taux\">%s</td>",
                        number_format($votant, 0, ",", " "),
                        $pourcentageVotant
                    );

                    // Calcul du total
                    $totals[$participation_election["participation_election_id"]] += $votant;
                }
                
                $totalInscrit = ! empty($election_unite["election_unite_inscrit"]) ?
                    $totalInscrit + $election_unite["election_unite_inscrit"] : $totalInscrit + 0;
             
                // Code html de la participation par unité
                $inscrit = ! empty($election_unite["election_unite_inscrit"]) ?
                    number_format($election_unite["election_unite_inscrit"], 0, ",", " ") :
                    0;
                $htmlParticipation .= sprintf(
                    "<tr class=\"data %s\">
                        <td class=\"bureaux\">%s</td>
                        <td class=\"inscrits\">%s</td>
                        %s
                    </tr>",
                    $oddEven = $oddEven === 'odd' ? 'even' : 'odd',
                    $election_unite["unite_code"].' '.$election_unite["unite_libelle"],
                    $inscrit,
                    $htmlVotantTranche
                );
            }
        }

        // Remplissage du total : Nombre total de votant enregistré pour chaque tranche
        $htmlTotalVotant = '';
        foreach ($totals as $total) {
            $pourcentageTotal = $totalInscrit != 0 ?
                number_format(round($total * 100 / $totalInscrit, 2), 2, ",", " ")
                : '0,00';

            $htmlTotalVotant .= sprintf(
                "<td class=\"tranche_voix\">%s</td>
                <td class=\"tranche_taux\">%s</td>",
                number_format($total, 0, ",", " "),
                $pourcentageTotal
            );
        }
        $htmlTotal = sprintf(
            "<tr class=\"total\">
                <td class=\"bureaux\">Total</td>
                <td class=\"inscrits\">%s</td>
                %s
            </tr>",
            number_format($totalInscrit, 0, ",", " "),
            $htmlTotalVotant
        );

        // Les largeurs de colonnes sont fixées, la base est un 
        // format a3 paysage largeur 420 - 10 de marge de chaque côté
        $width_global = 400;
        $width_bureaux = 90;
        $width_inscrits = 20;
        $width_voix = 0;
        if (count($results["participations"]) != 0 ) {
            $width_voix = ($width_global - $width_bureaux - $width_inscrits) / (count($results["participations"]) * 2);
        }
        $width_taux = $width_voix;
        // code de l'entête du tableau des résultats
        $resultat = sprintf(
            '<style>
                table#participation {
                    border: 1px solid #434343;
                    width: %1$smm;
                }
                table#participation tr.total td,
                table#participation tr.entete td {
                    border-top: 1px solid #434343;
                    border-bottom: 1px solid #434343;
                }
                table#participation td {
                    border-right: 1px solid #434343;
                }
                table#participation tr.entete td.bureaux {
                    width: %2$smm;
                    text-align: center;
                }
                table#participation tr.data td.bureaux {
                    width: %2$smm;
                    text-align: left;
                }
                table#participation tr.entete td.inscrits,
                table#participation tr.entete td.tranche_voix,
                table#participation tr.entete td.tranche_taux {
                    text-align: center;
                }
                table#participation td.inscrits {
                    width: %3$smm;
                }
                table#participation td.tranche_voix {
                    width: %4$smm;
                }
                table#participation td.tranche_taux {
                    width: %5$smm;
                }
                table#participation tr.entete {
                    background-color: #c8c8c8;
                    font-weight: bold;
                }

                table#participation tr.total td.inscrits,
                table#participation tr.total td.tranche_taux,
                table#participation tr.total td.tranche_voix,
                table#participation tr.data td.inscrits,
                table#participation tr.data td.tranche_taux,
                table#participation tr.data td.tranche_voix {
                    text-align: right;
                }
                table#participation tr.total {
                    background-color: #a0b4c8;
                    font-weight: bold;
                }
                table#participation tr.even {
                    background-color: #e6e6e6;
                }
                table#participation tr.odd {
                    background-color: #ffffff;
                }
            </style>
            <table id="participation" cellspacing="0" cellpadding="2">
                <tr class="entete">
                    <td class="bureaux">Bureaux de vote</td>
                    <td class="inscrits">Inscrits</td>
                    %6$s
                </tr>
                %7$s
                %8$s
            </table>',
            $width_global,
            $width_bureaux,
            $width_inscrits,
            $width_voix,
            $width_taux,
            $htmlEnteteColonne,
            $htmlParticipation,
            $htmlTotal
        );
        return $resultat;
    }

    /**
     * Renvoie le tableau de l'édition des résultats par perimètre.
     * Il est possible d'afficher ou pas les pourcentages d'emargement, de votant, de blanc, de nul,
     * de vote exprimés et de voix obtenues par candidat.
     * Il est possible d'afficher ou pas les résultats par unités contenues dans le périmètre
     *
     * @param integer identifiant de l'élément unité perimetre de l'élection
     * @param boolean $pourcentage indique si les pourcentage doivent être présent ou pas
     * @param boolean $pourcentageUrne indique si le purcentage de votant doit être présent ou pas
     * @param boolean $detail indique si les résultats des unités contenues doit être affiché
     * @param boolean $procuration indique si le nombre de procuration doit être affiché
     *
     * @return array $resultat tableau de l'édition des résultats.
     */
    protected function get_tableau_resultats_perimetre(
        $perimetre_id,
        $pourcentage = false,
        $pourcentageUrne = false,
        $detail = true,
        $affProc = false
    ) {
        $perimetre = $this->get_full_results(array(
            "r" => "result_line",
            "s" => "unite_id",
            "v" => intval($perimetre_id),
        ));

        // Initialisation des résultats pour le cas où aucun résultat n'a été récupéré
        $resultats = array(
            "inscrit",
            "procuration",
            "emargement",
            "pc_emargement",
            "votant",
            "pc_votant",
            "blanc",
            "pc_blanc",
            "nul",
            "pc_nul",
            "exprime",
            "pc_exprime"
        );
        foreach ($resultats as $resultat) {
            ${$resultat} = 0;
            if (is_array($perimetre) &&
                array_key_exists("election_unite_".$resultat, $perimetre) &&
                is_numeric($perimetre["election_unite_".$resultat])
            ) {
                ${$resultat} = $perimetre["election_unite_inscrit"];
            }
        }


        // Remplissage des colonnes en affichant ou pas les colonnes dédiées aux pourcentage
        $colonnesTableau = array(
            'N°' => $detail ? '' : 'T',
            'BUREAUX' => 'TOTAL',
            'INS' => number_format($inscrit, 0, ",", " "),
            'PROC' => number_format($procuration, 0, ",", " "),
            'V/EM' => number_format($emargement, 0, ",", " "),
            '%V' => number_format($pc_emargement, 2, ",", " ").'%',
            'V/UR' => number_format($votant, 0, ",", " "),
            '%V/UR' => number_format($pc_votant, 2, ",", " ").'%',
            'BLA' => number_format($blanc, 0, ",", " "),
            '%B' => number_format($pc_blanc, 2, ",", " ").'%',
            'NUL' => number_format($nul, 0, ",", " "),
            '%N' => number_format($pc_nul, 2, ",", " ").'%',
            'EXP' => number_format($exprime, 0, ",", " "),
            '%E'=>  number_format($pc_exprime, 2, ",", " ").'%',
        );

        $colonneOptionnelle = array('PROC', '%V', '%V/UR', '%B', '%N', '%E');

        $htmlEntete = '';
        $htmlTotal = '';
        // Préparation de l'entete et de la ligne des résultats du tableaux
        foreach ($colonnesTableau as $colonne => $resultat) {
            // Affiche uniquement les colonnes voulues
            if (! in_array($colonne, $colonneOptionnelle) ||
                ($affProc && ($colonne === 'PROC')) ||
                ($pourcentageUrne && ($colonne === '%V/UR')) ||
                ($pourcentage && (
                    $colonne === '%V' ||
                    $colonne === '%B' ||
                    $colonne === '%N' ||
                    $colonne === '%E'
                ))) {
                $htmlEntete .= sprintf(
                    "<th id=\"%s\">%s</th>",
                    $colonne === "N°" ? 'ordre' : $colonne,
                    $colonne
                );
                $htmlTotal .= sprintf(
                    "<td id=\"%s\">%s</td>",
                    $colonne === "N°" ? 'ordre' : $colonne,
                    $resultat
                );
            }
        }
        $results = $this->get_full_results();
        // Ajout des colonnes dédiées aux candidats dans l'entete et le total
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEntete .= sprintf(
                "<th>%s</th>",
                $election_candidat["election_candidat_ordre"]
            );
            $htmlTotal .= sprintf(
                "<td>%s</td>",
                number_format($perimetre["candidat".$election_candidat["election_candidat_ordre"]], 0, ",", " ")
            );
            // Si l'option d'affichage des pourcentages est choisi, rajoute les colonnes
            // correspondantes et calcul le pourcentage de vote pour chaque candidat
            if ($pourcentage) {
                $htmlEntete .= sprintf(
                    "<th>%%%s</th>",
                    $election_candidat["election_candidat_ordre"]
                );
                $htmlTotal .= sprintf(
                    "<td>%s</td>",
                    number_format($perimetre["pc_candidat".$election_candidat["election_candidat_ordre"]], 2, ",", " ")
                );
            }
        }

        // Rempli les résultats de chaque unités
        $htmlContenu = '';
        if ($detail) {
            $oddEven = 'odd';
            foreach ($perimetre["descendants"] as $unite_enfant_id) {
                $election_unite = $this->get_full_results(array(
                    "r" => "result_line",
                    "s" => "unite_id",
                    "v" => intval($unite_enfant_id),
                ));
                $colonnesTableau = array(
                    'N°' => $election_unite["unite_code"],
                    'BUREAUX' => $election_unite["unite_libelle"],
                    'INS' => number_format($election_unite["election_unite_inscrit"], 0, ",", " "),
                    'PROC' => number_format($election_unite["election_unite_procuration"], 0, ",", " "),
                    'V/EM' => number_format($election_unite["election_unite_emargement"], 0, ",", " "),
                    '%V' => number_format($election_unite["election_unite_pc_emargement"], 2, ",", " ").'%',
                    'V/UR' => number_format($election_unite["election_unite_votant"], 0, ",", " "),
                    '%V/UR' => number_format($election_unite["election_unite_pc_votant"], 2, ",", " ").'%',
                    'BLA' => number_format($election_unite["election_unite_blanc"], 0, ",", " "),
                    '%B' => number_format($election_unite["election_unite_pc_blanc"], 2, ",", " ").'%',
                    'NUL' => number_format($election_unite["election_unite_nul"], 0, ",", " "),
                    '%N' => number_format($election_unite["election_unite_pc_nul"], 2, ",", " ").'%',
                    'EXP' => number_format($election_unite["election_unite_exprime"], 0, ",", " "),
                    '%E'=>  number_format($election_unite["election_unite_pc_exprime"], 2, ",", " ").'%',
                );
    
                $htmlResultat = '';
                foreach ($colonnesTableau as $colonne => $resultat) {
                    // Affiche uniquement les colonnes voulues
                    if (! in_array($colonne, $colonneOptionnelle) ||
                    ($affProc && ($colonne === 'PROC')) ||
                    ($pourcentageUrne && ($colonne === '%V/UR')) ||
                    ($pourcentage && (
                        $colonne === '%V' ||
                        $colonne === '%B' ||
                        $colonne === '%N' ||
                        $colonne === '%E'
                    ))) {
                        $htmlResultat .= sprintf(
                            "<td id=\"%s\">%s</td>",
                            //Pour éviter d'avoir un id avec un caractère spécial
                            $colonne === "N°" ? 'ordre' : $colonne,
                            $resultat
                        );
                    }
                }
    
                $htmlResultatCan = '';
                foreach ($results["candidats"] as $election_candidat) {
                    $htmlResultatCan .= sprintf(
                        "<td>%s</td>",
                        number_format($election_unite["candidat".$election_candidat["election_candidat_ordre"]], 0, ",", " ")
                    );
                    if ($pourcentage) {
                        $htmlResultatCan .= sprintf(
                            "<td>%s</td>",
                            number_format($election_unite["pc_candidat".$election_candidat["election_candidat_ordre"]], 2, ",", " ")
                        );
                    }
                }
                // Ligne du tableau contenant les résultats de l'unité et du candidat pour l'unité
                $htmlContenu .= sprintf(
                    "<tr class=\"%s\">
                        %s
                        %s
                    </tr>",
                    $oddEven = $oddEven === 'odd' ? 'even' : 'odd',
                    $htmlResultat,
                    $htmlResultatCan
                );
            }
        }
        $htmlTabResPerimetre = sprintf(
            '<style>
                table#resultats-par-perimetre {
                    font-size: 7px;
                    text-align: right;
                    width: 100%%;
                }
                table#resultats-par-perimetre th#BUREAUX {
                    width: 8%%;
                }
                table#resultats-par-perimetre td#BUREAUX {
                    text-align: left;
                }
                table#resultats-par-perimetre th#ordre,
                table#resultats-par-perimetre td#ordre {
                    width: 2%%;
                    text-align: center;
                }
                table#resultats-par-perimetre tr.entete {
                    background-color: #b4b4b4;
                    text-align: center;
                }
                table#resultats-par-perimetre tr.total {
                    background-color: #a0b4c8;
                }
                table#resultats-par-perimetre tr.even {
                    background-color: #f3f3f6;
                }
            </style>
            <b> - %s</b><br>
            <table id="resultats-par-perimetre" border="1" cellpadding="1">
                <tr class="entete">
                    %s
                </tr>
                %s
                <tr class="%s">
                    %s
                </tr>
            </table>',
            $detail ? $perimetre["unite_code"].' '.$perimetre["unite_libelle"] : 'Total général',
            $htmlEntete,
            $htmlContenu,
            $detail ? 'total' : 'total-general',
            $htmlTotal
        );
        return $htmlTabResPerimetre;
    }

    /**
     *
     */
    public function get_full_results($q = array()) {
        if (isset($this->_full_results) !== true
            || $this->_full_results === null) {
            $this->compute_full_results();
        }
        if (is_array($q) === false
            || count($q) === 0) {
            return $this->_full_results;
        }
        if (array_key_exists("r", $q) === true
            && $q["r"] === "result_line"
            && array_key_exists("s", $q) === true
            && in_array($q["s"], array("unite_id", "election_unite_id")) === true
            && array_key_exists("v", $q) === true
            && is_integer($q["v"]) === true) {
            foreach ($this->_full_results["resultats"] as $key => $value) {
                if ($value[$q["s"]] == $q["v"]) {
                    return $value;
                }
            }
        }
        return null;
    }

    /**
     *
     */
    protected function compute_full_results() {
        $this->_full_results = array(
            "resultats" => array(),
            "candidats" => array(),
            "participations" => array(),
        );
        // UNITES
        $election_unites = $this->f->get_all_results_from_db_query(sprintf(
            'SELECT
                unite.unite as unite_id,
                unite.code_unite as unite_code,
                unite.libelle as unite_libelle,
                unite.perimetre as unite_perimetre,
                canton.code as canton_code,
                canton.prefecture as canton_prefecture,
                election_unite.election_unite as election_unite_id,
                coalesce(election_unite.inscrit, 0) as election_unite_inscrit,
                case when coalesce(election_unite.inscrit, 0) > 0 then (coalesce(election_unite.inscrit,0)-coalesce(election_unite.votant,0)) else 0 end as election_unite_abstention,
                coalesce(election_unite.emargement, 0) as election_unite_emargement,
                coalesce(election_unite.procuration, 0) as election_unite_procuration,
                coalesce(election_unite.votant, 0) as election_unite_votant,
                coalesce(election_unite.blanc, 0) as election_unite_blanc,
                coalesce(election_unite.nul, 0) as election_unite_nul,
                coalesce(election_unite.exprime, 0) as election_unite_exprime,
                type_unite.bureau_vote as type_unite_bureau_vote,
                type_unite.libelle as type_unite_libelle
            FROM
                %1$selection_unite
                    LEFT JOIN %1$sunite ON election_unite.unite=unite.unite
                    LEFT JOIN %1$stype_unite ON unite.type_unite=type_unite.type_unite
                    LEFT JOIN %1$scanton ON unite.canton=canton.canton
                    LEFT JOIN %1$scirconscription ON unite.circonscription=circonscription.circonscription
                    LEFT JOIN %1$sdepartement ON unite.departement=departement.departement
                    LEFT JOIN %1$scommune ON unite.commune=commune.commune
            WHERE
                election_unite.election=%2$s
            ORDER BY
                unite.ordre ASC',
            DB_PREFIXE,
            intval($this->getVal($this->clePrimaire))
        ));
        if ($election_unites["code"] !== "OK") {
            return $this->_full_results;
        }
        $this->_full_results["resultats"] = $election_unites["result"];
        // CANDIDATS
        $election_candidats = $this->f->get_all_results_from_db_query(sprintf(
            'SELECT
                election_candidat.election_candidat as election_candidat_id,
                election_candidat.ordre as election_candidat_ordre,
                coalesce(election_candidat.siege, 0) as election_candidat_siege,
                coalesce(election_candidat.siege_com, 0) as election_candidat_siege_com,
                coalesce(election_candidat.siege_mep, 0) as election_candidat_siege_mep,
                candidat.candidat as candidat_id,
                candidat.libelle as candidat_libelle,
                candidat.libelle_liste as candidat_libelle_liste
            FROM
                %1$selection_candidat
                    LEFT JOIN %1$scandidat ON election_candidat.candidat=candidat.candidat
            WHERE
                election_candidat.election=%2$s
            ORDER BY
                election_candidat.ordre',
            DB_PREFIXE,
            intval($this->getVal($this->clePrimaire))
        ));
        if ($election_candidats["code"] !== "OK") {
            return $this->_full_results;
        }
        $this->_full_results["candidats"] = $election_candidats["result"];
        // PARTICIPATIONS
        $election_participations = $this->f->get_all_results_from_db_query(sprintf(
            'SELECT
                participation_election.participation_election as participation_election_id,
                tranche.tranche as tranche_id,
                tranche.libelle as tranche_libelle,
                participation_unite.participation_unite as participation_unite_id,
                participation_unite.election_unite as election_unite_id,
                coalesce(participation_unite.votant, 0) as votant
            FROM
                %1$sparticipation_unite
                    LEFT JOIN %1$sparticipation_election ON participation_unite.participation_election=participation_election.participation_election
                    LEFT JOIN %stranche ON participation_election.tranche=tranche.tranche
            WHERE
                participation_election.election=%2$s
            ORDER BY
                tranche.ordre
            ',
            DB_PREFIXE,
            intval($this->getVal($this->clePrimaire))
        ));
        if ($election_participations["code"] !== "OK") {
            return $this->_full_results;
        }
        foreach ($election_participations["result"] as $key => $value) {
            if (isset($this->_full_results["participations"][$value["participation_election_id"]]) === false) {
                $this->_full_results["participations"][$value["participation_election_id"]] = array(
                    "tranche_id" => $value["tranche_id"],
                    "tranche_libelle" => $value["tranche_libelle"],
                    "participation_election_id" => $value["participation_election_id"],
                    "unites" => array(),
                );
            }
            $this->_full_results["participations"][$value["participation_election_id"]]["unites"][$value["election_unite_id"]] = array(
                "election_unite_id" => $value["election_unite_id"],
                "votant" => $value["votant"],
            );
        }
        // RESULTATS
        $election_unites_ids = array();
        foreach ($this->_full_results["resultats"] as $key => $value) {
            $election_unites_ids[] = $value["election_unite_id"];
        }
        // Si la liste des unités est vide (c'est le cas en prévisualisation)
        // la liste prend la valeur -1 pour ne pas provoquer d'erreur dans les requêtes
        if (empty($election_unites_ids)) {
            $election_unites_ids[] = -1;
        }
        $election_resultats = $this->f->get_all_results_from_db_query(sprintf(
            'SELECT
                election_unite.election_unite as election_unite_id,
                election_candidat.election_candidat as election_candidat_id,
                election_candidat.ordre as election_candidat_ordre,
                election_resultat.resultat
            FROM
                %1$selection_resultat
                    LEFT JOIN %1$selection_unite ON election_resultat.election_unite=election_unite.election_unite
                    LEFT JOIN %1$sunite ON election_unite.unite=unite.unite
                    LEFT JOIN %1$selection_candidat ON election_resultat.election_candidat=election_candidat.election_candidat
            WHERE
                election_resultat.election_unite IN (%2$s)
            ORDER BY
                unite.ordre, election_candidat.ordre',
            DB_PREFIXE,
            implode(",", $election_unites_ids)
        ));
        if ($election_resultats["code"] !== "OK") {
            return $this->_full_results;
        }
        $unites_enfant = $this->f->get_all_results_from_db_query(sprintf(
            'SELECT
                election_unite.election_unite as election_unite_id,
                unite.ordre as unite_enfant_ordre,
                unite.libelle as unite_enfant_libelle,
                lien_unite.unite_enfant as unite_enfant_id
            FROM
                %1$selection_unite
                    INNER JOIN %1$slien_unite ON election_unite.unite=lien_unite.unite_parent
                    INNER JOIN %1$sunite ON lien_unite.unite_enfant=unite.unite
            WHERE
                election_unite.election_unite IN (%2$s)
            ORDER BY
                unite.ordre, election_unite.election_unite',
            DB_PREFIXE,
            implode(",", $election_unites_ids)
        ));
        if ($unites_enfant["code"] !== "OK") {
            return $this->_full_results;
        }

        foreach ($this->_full_results["resultats"] as $key => $value) {
            $this->_full_results["resultats"][$key]["election_unite_pc_abstention"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_votant"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_emargement"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_nul"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_blanc"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_exprime"] = 0.00;
            $this->_full_results["resultats"][$key]["election_unite_pc_procuration"] = 0.00;
            if ($value["election_unite_inscrit"] > 0) {
                $this->_full_results["resultats"][$key]["election_unite_abstention"] = $value["election_unite_inscrit"] - $value["election_unite_votant"];
                $this->_full_results["resultats"][$key]["election_unite_pc_abstention"] = $value["election_unite_abstention"] / $value["election_unite_inscrit"] * 100;
                $this->_full_results["resultats"][$key]["election_unite_pc_votant"] = $value["election_unite_votant"] / $value["election_unite_inscrit"] * 100;
                $this->_full_results["resultats"][$key]["election_unite_pc_emargement"] = $value["election_unite_emargement"] / $value["election_unite_inscrit"] * 100;
                $this->_full_results["resultats"][$key]["election_unite_pc_procuration"] = $value["election_unite_procuration"] / $value["election_unite_inscrit"] * 100;
            }
            if ($value["election_unite_votant"] > 0) {
                $this->_full_results["resultats"][$key]["election_unite_pc_nul"] = $value["election_unite_nul"] / $value["election_unite_votant"] * 100;
                $this->_full_results["resultats"][$key]["election_unite_pc_blanc"] = $value["election_unite_blanc"] / $value["election_unite_votant"] * 100;
                $this->_full_results["resultats"][$key]["election_unite_pc_exprime"] = $value["election_unite_exprime"] / $value["election_unite_votant"] * 100;
            }
        }
        foreach ($this->_full_results["resultats"] as $key => $value) {
            foreach ($election_resultats["result"] as $election_resultat) {
                if ($election_resultat["election_unite_id"] == $value["election_unite_id"]) {
                    $this->_full_results["resultats"][$key]["candidat".$election_resultat["election_candidat_ordre"]] = intval($election_resultat["resultat"]);
                    if (intval($value["election_unite_exprime"]) > 0) {
                        $this->_full_results["resultats"][$key]["pc_candidat".$election_resultat["election_candidat_ordre"]] = (intval($election_resultat["resultat"]) / intval($value["election_unite_exprime"]) ) * 100;
                    } else {
                        $this->_full_results["resultats"][$key]["pc_candidat".$election_resultat["election_candidat_ordre"]] = 0.00;
                    }
                }
            }
            $this->_full_results["resultats"][$key]["descendants"] = array();
            foreach ($unites_enfant["result"] as $unite_enfant) {
                if ($unite_enfant["election_unite_id"] == $value["election_unite_id"]) {
                    foreach ($this->_full_results["resultats"] as $key_enfant => $value_enfant) {
                        if ($unite_enfant["unite_enfant_id"] == $value_enfant["unite_id"]) {
                            $this->_full_results["resultats"][$key]["descendants"][] = $value_enfant["unite_id"];
                        }
                    }
                }
            }
        }
    }

    /**
     * Renvoie le tableau de l'édition des résultats.
     *
     * @return string $resultat tableau de l'édition des résultats.
     */
    protected function get_mf_etat_resultat() {
        $results = $this->get_full_results();
        // Colonne de l'entete du tableau affichant les noms des candidats
        // Préparation des cases vides correspondantes aux colonnes des candidats pour la ligne
        // du total de BV
        $htmlEnteteCandidat = "";
        $htmlCaseVideTotalBV = '';
        $totalCandidat = array();
        foreach ($results["candidats"] as $election_candidat) {
            $htmlEnteteCandidat .= sprintf(
                '<th>%s</th>',
                $election_candidat["candidat_libelle"]
            );
            $htmlCaseVideTotalBV .= "<td></td>";


            // Initialisation du tableau contenant le total des candidats
            $totalCandidat[$election_candidat["election_candidat_id"]] = 0;
        }
        // Composition de tout le contenu du tableau
        $htmlContenuTab = '';
        $htmlTotalTab = '';
        $oddEven = 'even';
        $nombreBureau = 0;
        $htmlResultatTotal = '';
        $htmlResultatTotalCan = '';
        // Initialisation du tableau contenant le total des résultats
        $total = array(
            'inscrit' => 0,
            'emargement' => 0,
            'votant' => 0,
            'blanc' => 0,
            'nul' => 0,
            'exprime' => 0
        );
        foreach ($results["resultats"] as $election_unite) {
            $isBV = $this->f->boolean_string_to_boolean($election_unite["type_unite_bureau_vote"]);
            if ($isBV) {
                // Récupération des résultats et préparation de l'affichage dans le tableau
                $htmlResultats = '';
                $htmlResultatTotal = '';
                foreach (array('inscrit', 'votant', 'blanc', 'nul', 'exprime') as $field) {
                    $htmlResultats .= sprintf(
                        '<td>%s</td>',
                        (is_numeric($election_unite["election_unite_".$field]) ? $election_unite["election_unite_".$field] : 0)
                    );

                    // Calcul du total
                    $total[$field] += is_numeric($election_unite["election_unite_".$field]) ?
                        $election_unite["election_unite_".$field] :
                        0;
                    $htmlResultatTotal .= sprintf(
                        "<td>%s</td>",
                        $total[$field]
                    );
                }
                // Récupération des résultats des candidats et préparation de l'affichage
                $htmlResultatCan = '';
                $htmlResultatTotalCan = '';
                foreach ($results["candidats"] as $election_candidat) {
                    $resultat = $election_unite["candidat".$election_candidat["election_candidat_ordre"]];
                    $htmlResultatCan .= sprintf(
                        '<td>%s</td>',
                        ! empty($resultat) ? $resultat : 0
                    );

                    // Calcul du total obtenu par chaque candidat et remplissage de la ligne du total
                    $totalCandidat[$election_candidat["election_candidat_id"]] += ! empty($resultat) ? $resultat : 0;
                    $htmlResultatTotalCan .= sprintf(
                        "<td>%s</td>",
                        $totalCandidat[$election_candidat["election_candidat_id"]]
                    );
                }
                // Contenu du tableau
                $oddEven = ($oddEven === 'even') ? 'odd' : 'even';
                $nombreBureau++;
                $htmlContenuTab .= sprintf(
                    '<tr class="%1$s">
                        %2$s
                        <td class="canton">%3$s</td>
                        <td class="bureau">%4$s</td>
                        %5$s
                    </tr>',
                    $oddEven,
                    $htmlResultats,
                    $election_unite["canton_prefecture"],
                    $election_unite["unite_code"].' '.$election_unite["unite_libelle"],
                    $htmlResultatCan
                );
            }
        }

        // Remplissage de la ligne de total de l'état
        $htmlTotalTab = sprintf(
            '<tr class="total">
                %1$s
                <td></td>
                <td></td>
                %2$s
            </tr>',
            $htmlResultatTotal,
            $htmlResultatTotalCan
        );

        // Construction du tableau des résultats
        return sprintf(
            '<style>
                table#resultats-standard, table#resultats-standard th, table#resultats-standard td {
                    border: 0.1px solid black;
                }
                table#resultats-standard tr {
                    text-align : right;
                }
                table#resultats-standard tr.entete {
                    background-color: #c3e0a9;
                    text-align: center;
                }
                table#resultats-standard tr.total {
                    background-color: #c4d5d7;
                }
                table#resultats-standard tr.even {
                    background-color: #f3f3f6;
                }
                table#resultats-standard td.canton {
                    text-align: center;
                }
                table#resultats-standard td.bureau {
                    text-align: left;
                }
            </style>
            <table id="resultats-standard">
                <tr class="entete">
                    <th>INSCRITS</th>
                    <th>VOTANTS</th>
                    <th>BLANCS</th>
                    <th>NULS</th>
                    <th>EXPRIMES</th>
                    <th>CANTON</th>
                    <th>BUREAUX DE VOTE</th>
                    %1$s
                </tr>
                %2$s
                %3$s
                <tr class="total">
                    <td>NOMBRE</td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td>%4$s</td>
                    %5$s
                </tr>
            </table>',
            $htmlEnteteCandidat,
            $htmlContenuTab,
            $htmlTotalTab,
            $nombreBureau,
            $htmlCaseVideTotalBV
        );
    }

    /**
     * Renvoie le tableau des résultats des candidats pour une unité donnée
     *
     * @param integer id de l'unité de l'élection
     * @param boolean option pour afficher les sièges ou pas
     * @param boolean option pour afficher le nom de la liste ou celui du candidat
     * @param boolean option pour afficher le nombre de siège pour le conseil métropolitain
     *
     * @return string tableau des résultats des candidats.
     */
    public function get_tableau_resultat_candidat(
        $idUniteElec,
        $afficherSiege = false,
        $afficherListe = false,
        $afficherSiegeMep = false
    ) {
        if (empty($idUniteElec)) {
            return;
        }
        $results = $this->get_full_results();
        $election_unite = $this->get_full_results(array(
            "r" => "result_line",
            "s" => "election_unite_id",
            "v" => intval($idUniteElec),
        ));
        $resultatsCandidat = '';
        foreach ($results["candidats"] as $election_candidat) {
            // Mise en forme des résultats
            $voix = number_format($election_unite["candidat".$election_candidat["election_candidat_ordre"]], 0, ",", " ");
            $pourcentage = number_format($election_unite["pc_candidat".$election_candidat["election_candidat_ordre"]], 2, ",", " ").'%';
            $siegeMun = number_format($election_candidat["election_candidat_siege"], 0, ",", " ");
            $siegeCom = number_format($election_candidat["election_candidat_siege_com"], 0, ",", " ");
            $siegeMep = number_format($election_candidat["election_candidat_siege_mep"], 0, ",", " ");
            // Remplissage de la ligne des résultats du candidat
            $resultatsCandidat .= sprintf(
                '<tr class="data">
                    <td class="candidat_liste">%d - %s</td>
                    <td class="tranche_voix">%s</td>
                    <td class="tranche_taux">%s</td>
                    %s
                    %s
                    %s
                </tr>',
                $election_candidat["election_candidat_ordre"],
                $afficherListe ? $election_candidat["candidat_libelle_liste"] : $election_candidat["candidat_libelle"],
                $voix,
                $pourcentage,
                $afficherSiege ? '<td class="siege">'.$siegeMun.'</td>' : '',
                $afficherSiege ? '<td class="siege_com">'.$siegeCom.'</td>' : '',
                $afficherSiegeMep ? '<td class="siege_mep">'.$siegeMep.'</td>' : ''
            );
        }

        // Calcul de la proportion donnée au colonne des résultats
        $nbColonneResultat = 2;
        if ($afficherSiege) {
            $nbColonneResultat += 2;
        }
        if ($afficherSiegeMep) {
            $nbColonneResultat += 1;
        }
        $widthResultat = 50 / $nbColonneResultat;
        // Tableau des résultats des candidats
        $tableauResultat = sprintf(
            '<style>
                table#resultats_candidats {
                    width: 100%%;
                    border: 1px solid #434343;
                }
                table#resultats_candidats tr.entete td {
                    border-top: 1px solid #434343;
                    border-bottom: 1px solid #434343;
                }
                table#resultats_candidats td {
                    border-right: 1px solid #434343;
                    border-bottom: 1px solid #434343;
                }
                table#resultats_candidats tr.entete td.candidat_liste {
                    width: 50%%;
                    text-align: center;
                }
                table#resultats_candidats tr.data td.candidat_liste {
                    width: 50%%;
                    text-align: left;
                }
                table#resultats_candidats tr.entete,
                table#resultats_candidats tr.entete td.tranche_voix,
                table#resultats_candidats tr.entete td.tranche_taux,
                table#resultats_candidats tr.entete td.siege,
                table#resultats_candidats tr.entete td.siege_com,
                table#resultats_candidats tr.entete td.siege_mep {
                    text-align: center;
                    font-weight: bold;
                    width: %d%%;
                }
                table#resultats_candidats tr.data td.tranche_voix,
                table#resultats_candidats tr.data td.tranche_taux,
                table#resultats_candidats tr.data td.siege,
                table#resultats_candidats tr.data td.siege_com,
                table#resultats_candidats tr.data td.siege_mep {
                    text-align: right;
                    width: %d%%;
                }
            </style>
            <table id="resultats_candidats" cellspacing="0" cellpadding="2">
                <tr class="entete">
                    <td class="candidat_liste">CANDIDATS OU LISTES</td>
                    <td class="tranche_voix">VOIX</td>
                    <td class="tranche_taux">%%</td>
                    %s
                    %s
                    %s
                </tr>
                %s
            </table>',
            $widthResultat,
            $widthResultat,
            $afficherSiege ? '<td class="siege">SIEGES CM</td>' : '',
            $afficherSiege ? '<td class="siege_com">SIEGES CC</td>' : '',
            $afficherSiegeMep ? '<td class="siege_mep">SIEGES CMP</td>' : '',
            $resultatsCandidat
        );
        return $tableauResultat;
    }

    /**
     * Permet d'obtenir des informations sur toutes les unités de l'élection sous la
     * forme d'un tableau contenant les information suivantes :
     *     unite : int -> identifiant de l'unité
     *     ordre : int -> numéro d'ordre de l'unité
     *     libelle : string -> libellé de l'unité
     *     resultat : array ->
     *                  inscrits : int -> nombre d'inscrit dans cette unité
     *                  votants : int -> nombre de votant dans cette unité
     *                  emargements : int -> nombre d'emargement dans cette unité
     *                  procurations : int -> nombre de procuration dans cette unité
     *                  exprimes : int -> nombre de vote exprime dans cette unité
     *                  nuls : int -> nombre de vote nul dans cette unité
     *                  blancs : int -> nombre de vote blanc dans cette unité
     *     bureau : string 't' ou 'f' -> indique si l'unité se comporte comme un bureau de vote
     *     code : array ->
     *                  canton : string -> code préfécture du canton
     *                  circonscription : string -> code préfécture de la circonscription
     *
     * @return array tableau des unités et de leurs résultats
     * si la requête permettant de récupérer les valeurs echoue la méthode renvoie un
     * tableau vide.
     */
    protected function get_info_unites($electionId) {
        $infoUnites = array();
        $sql = "SELECT
                    unite.unite, unite.ordre, unite.code_unite, unite.libelle, inscrit, votant,
                    emargement, procuration, exprime, nul, blanc, bureau_vote,
                    canton.prefecture as canton,
                    circonscription.prefecture as circonscription,
                    departement.prefecture as departement,
                    commune.prefecture as commune
                FROM
                    ".DB_PREFIXE."election_unite
                    INNER JOIN ".DB_PREFIXE."unite ON election_unite.unite=unite.unite
                    INNER JOIN ".DB_PREFIXE."type_unite ON unite.type_unite=type_unite.type_unite
                    LEFT JOIN ".DB_PREFIXE."canton ON unite.canton=canton.canton
                    LEFT JOIN ".DB_PREFIXE."circonscription ON unite.circonscription=circonscription.circonscription
                    LEFT JOIN ".DB_PREFIXE."departement ON unite.departement=departement.departement
                    LEFT JOIN ".DB_PREFIXE."commune ON unite.commune=commune.commune
                WHERE
                    election_unite.election = ".
                        $this->f->db->escapeSimple($electionId)."
                ORDER BY
                    unite.ordre";
        $res = $this->f->db->query($sql);
        $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
        if ($this->f->isDatabaseError($res, true)) {
            $this->addToLog(__METHOD__." problèmes sur la requête : ".$res->getDebugInfo().";", DEBUG_MODE);
            return $infoUnites;
        }
        $infoUnites = array();
        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            // Définition des champs requis
            $required = array(
                'inscrit',
                'votant',
                'emargement',
                'procuration',
                'exprime',
                'nul',
                'blanc'
            );
            // Vérification du l'existance et du type des valeurs récupérées
            // pour éviter les erreurs lors de leurs utilisations
            $resultat = array();
            foreach ($required as $parameter) {
                $resultat[$parameter] = isset($row[$parameter]) && is_numeric($row[$parameter]) ? $row[$parameter] : 0;
            }

            $code = array(
                'code_unite' => $row['code_unite'],
                'canton' => $row['canton'],
                'circonscription' => $row['circonscription'],
                'departement' => $row['departement'],
                'commune' => $row['commune']
            );

            $infoUnites[] = array(
                'unite' => $row['unite'],
                'ordre' => $row['ordre'],
                'libelle' => $row['libelle'],
                'resultat' => $resultat,
                'code' => $code,
                'bureau' => $row['bureau_vote']
            );
        }
        return $infoUnites;
    }

    /**
     * Ecris et renvoie le chemin vers le répertoire web de l'élection.
     *      Ex : ../web/001-0C/
     * Avec : 001 -> identifiant de l'élection
     *        0   -> dans la v1 correspondait au numéro du tour. Le numéro de tour pouvant changer
     *               pour éviter des erreurs en cas de modification 0 est mis à la place
     *        C   -> C pour centaine et rien pour une élection
     *
     * @return string
     */
    public function get_election_web_path() {
        $path = sprintf(
            "../web/res/%'.03d-0%s",
            $this->getVal('election'),
            $this->is_centaine() ? 'C' : ''
        );
        return $path;
    }

    /**
     * Ecris un fichier contenant la liste des périmètres et pour chaque périmètre la
     * liste des unités qu'il contiens sous cette forme :
     *   $perimetre [47] = array(
     *              'unite_parent' => 47,
     *              'unite_enfant' => array( '44', '45'
     *              ));
     * 
     * @param integer $idElection identifiant de l'élection
     *
     * @return void
     */
    public function affichage_perimetre($idElection) {
        //Instance de la classe election_unite pour pouvoir
        //utiliser la méthode de récupération des périmètres de
        //cette classe
        $election_unite = $this->f->get_inst__om_dbform(array(
            'obj' => 'election_unite',
            'idx' => ']'
        ));

        //liste des perimetres de l'élection
        $perimetresElection = $election_unite->get_election_perimetres($idElection);
        $contenu = array();
        //Récupèration, pour chaque périmètre, de la liste de ses enfants.
        //Ecriture du contenu du fichier perimetre.inc, chaque ligne correspond
        //a un périmètre.
        foreach ($perimetresElection as $perimetreElection) {
            //Récupération de l'instance de l'unité pour pouvoir accéder
            //au type du périmètre et à son libellé
            $perimetre = $this->f->get_inst__om_dbform(array(
                'obj' => 'unite',
                'idx' => $perimetreElection->getVal('unite')
            ));

            $listeEnfant = $perimetre->get_unite_enfant($perimetre->getVal('unite'));

            //contenu du fichier perimetre.inc qui contiens pour chaque périmétre
            //ses résultats et les résultats des candidats
            $contenu[$perimetre->getVal('unite')] = $listeEnfant;
        }
        $contenu = json_encode($contenu);

        $this->f->write_file("../aff/res/".$idElection."/perimetres.json", $contenu);
        $this->msg .= "../aff/res/".$idElection."/perimetres.json envoyé"."</br>";
    }

    // =======================
    // sieges elus municipales
    // =======================
    /*
    ================================================================================
    AVERTISSEMENT : CALCUL REPARTITION DE SIEGES POUR ELECTION MUNICIPALES
                    pour les communes de plus de 3 500 h.
    ATTENTION : l egalité de score est traitée si la case age moyen des listes
                est remplie (attribuer a liste ayant l'age superieur)
               (siege supplementaire et bonus attribuer à la première liste trouvée)
                -> un message signale l anomalie

    L attribution a ce moment la suit des regles suivant la date de naissance
    des co listiers (les dates de naissance ne sont pas saisies)

    Voir les explications du site de la documentation francaise et edile.fr

    La repartition se fait au 1er tour si une liste obtient plus de 50% de voix
    et un quart au moins des electeurs inscrits
    -> message si la 1ere liste n a pas la majorite absolue

    $elimination : Siège si liste < 5% des exprimés ($total)

    $bonus pour le 1ere liste = moitié arrondi à l entier superieur ($bonus)
    si il y a plus de 4 sieges à repartir (si il y a plus de 3500 habitants, je ne
    vois pas comment il y aurait moins de 4 sieges ?)

    $siege_proportionnel (total siege $siegeCM - $bonus) repartis entre toutes
    les listes à la représentation proportionnelle suivant la règle
    de la plus forte moyenne. " (art. L. 262 du code électoral)

    EXEMPLES TESTES (attention erreur sur le quotient dans l exemple sur le site
    de la documentation francaise)
    Nombre de sieges : 49 sieges
    *** Exemple Avec 2 listes ***
    Liste A : 15167 voix (50,1 % des suffrages) => 12 + 0 + 25  =37
    Liste B : 15108 voix (49,9 % des suffrages) => 11 + 1       =12
    *** Exemple Avec 5 listes : liste A (+ 50% / B et C - 5%) ***
    Liste A : 15167 voix (50,1 % des suffrages) => 13+ 0 + 0 + 25 = 38
    Liste B : 1453 voix (4,8 % des suffrages)
    Liste C : 1348 voix (4,4 % des suffrages)
    Liste D : 7825 voix (25,9 % des suffrages)  => 6 + 0 + 1      = 7
    Liste E : 4482 voix (14,8 % des suffrages)  => 3 + 1 + 0      = 4
    *** Exemple avec 3 listes
    Liste A : 11214 voix (37 % des suffrages)  => 8 + 1 + 25 = 34
    Liste B : 10902 voix (36 % des suffrages)  => 8 + 1      = 9
    Liste C : 8159 voix (27 % des suffrages)   => 6 + 0      = 6
    */

    /**
     * Récupération et affichage du nombre de siège pour le conseil municipale
     * et le conseil communautaire
     * Le calcul des sièges est fait en prenant uniquement en compte les unités
     * ayant un comportement de type bureau de vote. Seul les réultats recueillis
     * pour ce type d'unités sont utilisés
     *
     * @return void
     */
    protected function sieges_elu() {
        $succes = true;
        $election=$this->getval("election");
        // conseil municipal ------------------------------------------------------------------
        $this->addToMessage("<b>C O N S E I L   M U N I C I P A L</b><br>");
        $candidats = $this->repartition($election, "");
        if ($candidats !== false) {
            // tableau final
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                // cas avec aucune saisie de resultat (siege = '' sauf le 1er qui a le bonus) *** correction 2.0.0b4
                if ($candidats[$index_candidat]['siege']=='') {
                    $candidats[$index_candidat]['siege']=0;
                }
                //conserver les sieges du cm
                $siege_cm[$index_candidat]=$candidats[$index_candidat]['siege'];
                $this->addToMessage("-> <b>la Liste \"".$candidats[$index_candidat]['libelle_liste'].
                 " obtient ".$siege_cm[$index_candidat]." siège(s) pour ".$candidats[$index_candidat]['voix']." voix </b>");
            }
            // sauvegarde du resultat
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                $this->f->db->autoCommit(true);
                $siegeModif = array(
                    "siege" => $candidats[$index_candidat]['siege']
                );
                $res = $this->f->db->autoExecute(
                    DB_PREFIXE."election_candidat",
                    $siegeModif,
                    DB_AUTOQUERY_UPDATE,
                    "election_candidat = ".$candidats[$index_candidat]['election_candidat']
                );
                $this->f->addToLog(__METHOD__ . "() : db->autoExecute(" . $res . ")", VERBOSE_MODE);
                if ($this->f->isDatabaseError($res, true)) {
                    $this->addToMessage("Echec de l'enregistrement du nombre de sièges municipaux");
                    $succes = false;
                }
            }
        } else {
            $succes = false;
            $this->addToMessage("Le nombre de sièges n'a pas pu être calculé !");
        }


        // conseil communautaire ---------------------------------------------------------------------
        $this->addToMessage("<br><b>C O N S E I L   C O M M U N A U T A I R E</b><br>");
        $candidats=$this->repartition($election, "_com");
        // tableau final
        if ($candidats !== false) {
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                $this->addToMessage("-> <b>la Liste \"".$candidats[$index_candidat]['libelle_liste'].
                 " obtient ".$candidats[$index_candidat]['siege']." siège(s) pour ".$candidats[$index_candidat]['voix']." voix </b>");
            }
            // sauvegarde du resultat
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                $this->f->db->autoCommit(true);
             // cas avec aucune saisie de resultat (siege = '' sauf le 1er qui a le bonus)
                if ($candidats[$index_candidat]['siege']=='') {
                    $candidats[$index_candidat]['siege']=0;
                }
                $siegeComModif = array(
                    "siege_com" => $candidats[$index_candidat]['siege']
                );
                $res = $this->f->db->autoExecute(
                    DB_PREFIXE."election_candidat",
                    $siegeComModif,
                    DB_AUTOQUERY_UPDATE,
                    "election_candidat = ".$candidats[$index_candidat]['election_candidat']
                );
                $this->f->addToLog(__METHOD__ . "() : db->autoExecute(" . $res . ")", VERBOSE_MODE);
                if ($this->f->isDatabaseError($res, true)) {
                    $this->addToMessage("Echec de l'enregistrement du nombre de sièges municipaux");
                    $succes = false;
                }
            }
        } else {
            $this->addToMessage("Le nombre de sièges n'a pas pu être calculé !");
        }

        // conseil communautaire ---------------------------------------------------------------------
        $this->addToMessage("<br><b>C O N S E I L   M E T R O P O L I T A I N</b><br>");
        $candidats=$this->repartition($election, "_mep");
        // tableau final
        if ($candidats !== false) {
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                $this->addToMessage("-> <b>la Liste \"".$candidats[$index_candidat]['libelle_liste'].
                 " obtient ".$candidats[$index_candidat]['siege']." siège(s) pour ".$candidats[$index_candidat]['voix']." voix </b>");
            }
            // sauvegarde du resultat
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                $this->f->db->autoCommit(true);
             // cas avec aucune saisie de resultat (siege = '' sauf le 1er qui a le bonus)
                if ($candidats[$index_candidat]['siege']=='') {
                    $candidats[$index_candidat]['siege']=0;
                }
                $siegeMepModif = array(
                    "siege_mep" => $candidats[$index_candidat]['siege']
                );
                $res = $this->f->db->autoExecute(
                    DB_PREFIXE."election_candidat",
                    $siegeMepModif,
                    DB_AUTOQUERY_UPDATE,
                    "election_candidat = ".$candidats[$index_candidat]['election_candidat']
                );
                $this->f->addToLog(__METHOD__ . "() : db->autoExecute(" . $res . ")", VERBOSE_MODE);
                if ($this->f->isDatabaseError($res, true)) {
                    $this->addToMessage("Echec de l'enregistrement du nombre de sièges métropolitain");
                    $succes = false;
                }
            }
        } else {
            $this->addToMessage("Le nombre de sièges n'a pas pu être calculé !");
        }

        // Transmission du nombre de siège à l'affichage si le calcul et l'enregistrement
        // du nombre de siège ont réussi
        if ($succes && $this->envoi_repartition_sieges_affichage()) {
            $this->addToMessage("<br><br>../aff/res/".$election."/repartition_sieges.json envoyé");
        } else {
            $this->addToMessage("<br><br>../aff/res/".$election."/repartition_sieges.json non envoyé");
            $succes = false;
        }
        return $succes;
    }

    /**
     * Calcul de la répartition du nombre de siège :
     *
     * 50% des sièges sont attribués aux vainqueurs.
     * Sur ces 50%, Si le nombre de sièges à pourvoir est impair, on arrondit à l'entier supérieur
     * s'il y a plus de 4 sièges à pourvoir, à l'entier inférieur sinon. C'est la prime majoritaire.
     *
     * 50% sont répartis à la propportionelle :
     * Les sièges restants après l'application de la prime majoritaire sont répartis entre toutes
     * les listes ayant obtenu plus de 5% au premier tour, y compris la liste arrivée en tête, à
     * la proportionnelle, selon la règle de la plus forte moyenne.
     *
     * Dans un premier temps, on calcule le quotient électoral en divisant nombre de suffrages
     * exprimés par le nombre de sièges à pourvoir. On divise alors le nombre de voix obtenues
     * par chaque liste par ce quotient électoral : le résultat de cette opération, arrondi à
     * l'entier inférieur, équivaut au nombre de sièges obtenus par chaque liste.
     *
     * Pour les sièges restants, on divise le nombre de suffrages obtenus par chaque liste par
     * le nombre de sièges attribués à cette liste plus 1 : c'est la moyenne de chaque liste.
     * On attribue un siège à la liste ayant la plus forte moyenne.
     * En cas d'égalité, on attribue le siège à la liste ayant le plus grand nombre de voix.
     * Et on reproduit jusqu'à épuisement des sièges à pourvoir.
     *
     * @param integer $election identifiant de l'élection
     * @param string $suffixe "" : conseil municipale et "_com" : conseil communautaire
     *
     * @return array $candidats résultats des candidats avec leur nombre de voix et de siège
     */
    protected function repartition($election, $suffixe) {
        // Vérification qu'il y a des candidats avant de faire le calcul
        $candidats = $this->get_info_candidats_election($election, $suffixe);
        if (empty($candidats)) {
            $this->addToMessage('Erreur : il n\'y a pas de candidats pour cette élection');
            return false;
        }

        // Vérification que le nombre de siège à répartir est connu et non nul
        $temp = 'sieges'.$suffixe;
        if (empty($this->getVal($temp))) {
            $this->addToMessage('Erreur : le nombre de siège à répartir n\'est pas renseigné ou nul');
            return false;
        }
        $siege = $this->getVal($temp);

        // Calcul de la prime majoritaire
        $bonus = intval($siege / 2);
        if ($siege % 2 > 0 and $siege > 4) {
                $bonus = $bonus + 1;
        }
        $this->addToMessage(_("le bonus est de")." ".$bonus. " "._("sièges"));
        // reste a repartir a la proportionnelle
        $siege_proportionnel = $siege - $bonus;
        $this->addToMessage(_("le reste a repartir  est  de ")." ".$siege_proportionnel. " "._("sièges"));

        // Calcul du quotient electorale
        $total = $this->get_total_exprime_election($election);
        if (empty($total)) {
            $this->addToMessage('Erreur : il n\'y a pas de vote exprimé');
            return false;
        }
        $exprime=$total; // pour conserver
        unset($candidats['exprime']);
        // total exprimes
        $this->addToMessage(_("le suffrage exprimé est  de ")." ".$total. " "._("voix"));
        // elimination des listes (candidats) ayant obtenus moins de 5% au premier tour
        $elimination = intval($exprime/20);
        $nbElimination = 0;
        for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
            $nomListe = $candidats[$index_candidat]["libelle_liste"];
            if (is_numeric($candidats[$index_candidat]["voix"])) {
                if ($candidats[$index_candidat]["voix"] < $elimination) {
                    $this->addToMessage(_("Liste ".$nomListe." est eliminée car elle n'obtient pas 5% des suffrages exprimés"));
                    // On calcule le nombre de votes exprimés en soustrayant le nombre
                    // de vote pour la liste éliminée
                    $total = $total - $candidats[$index_candidat]["voix"];
                    // On modifie le tableau des voix par candidat/liste pour
                    // positionner 0 à la liste éliminée
                    $candidats[$index_candidat]["voix"] = 0;
                    // Compte le nombre d'élimination pour vérifier par la suite si toutes les
                    // listes n'ont pas été éliminées
                    $nbElimination++;
                    $total_recalcul = 1;
                }
            } else {
                $this->addToMessage(
                    'Erreur : aucun résultat enregistré pour la liste '
                    .$nomListe
                );
                return false;
            }
        }
        // Si jamais tous les candidats ont été éliminés c'est qu'il y a une erreur au niveau
        // des résultats enregistrés
        if ($nbElimination  === count($candidats)) {
            $this->addToMessage(
                'Erreur : Toutes les listes ont été éliminées et ont donc moins de 5% des voix'
            );
            return false;
        }

        // Calcul du quotient électoral
        $quotientelectoral = $total / $siege_proportionnel;
        // Formatage du quotient électoral tronqué à deux décimales pour affichage
        $quotientFormate = intval($quotientelectoral * 100) / 100;
        $quotientFormate = number_format($quotientFormate, 2);
        // Affichage du quotient électoral
        $this->addToMessage(_("le quotient Electoral est égal à :  ").$total._(" voix / ").
            $siege_proportionnel._(" sieges soit ")." : ".$quotientFormate);
        // On calcule et on affiche la première répartition proportionnelle
        $siege_attribuer=0;
        for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
            if ($quotientelectoral == 0) {
                $candidats[$index_candidat]['siege'] = 0;
            } else {
                $candidats[$index_candidat]['siege'] = intval($candidats[$index_candidat]["voix"] / $quotientelectoral);
            }
            $this->addToMessage(" - "._("la Liste")." \"".$candidats[$index_candidat]["libelle_liste"]."\" ".
                _(" obtient dans la première répartition ").$candidats[$index_candidat]['siege']." "._("sièges"));
            $siege_attribuer=$siege_attribuer+$candidats[$index_candidat]['siege'];
        }
        // On calcule et on affiche le nombre de sièges restants à attribuer
        /**
         * il est calcule l attribution des sieges non repartis à la premiere repartition (fonction siege)
         * Les sieges restants sont attribues a la plus forte moyenne
         * on prend chaque liste et on calcule la moyenne si on rajoute un siege supplementaire
         * nbCandidatMoyMax verifie qu il n' y a pas egalite de liste ayant la meme moyenne pour le siege supplementaire attribue
         * cas normal (nbCandidatMoyMax==1)
         * cas specifique : deux ou plusieurs listes ont la meme plus grosse moyenne (nbCandidatMoyMax > 1)
         *      dans ce cas, on prend la liste qui a le plus de voix
         * cas non traite : les 2 listes ont le meme nombre de voix : on prend le conseiller le plus age
         */
        $reste_a_attribuer = $siege_proportionnel - $siege_attribuer;
        $this->addToMessage(_("Il reste à attribuer")." ".$reste_a_attribuer." "._("siège(s)"));
        // repartition des sieges restant (suite a la premiere repartition) a la plus forte moyenne
        // On boucle tant que tous les sièges n'ont pas été affecté à la plus forte moyenne
        //$k = 0;
        while ($siege_attribuer < $siege_proportionnel) {
            for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
                // moyenne par liste avec un siege de plus attribué a chaque liste
                $moyennesCandidat[$index_candidat] = $candidats[$index_candidat]["voix"] / ($candidats[$index_candidat]["siege"] + 1);
                $nbSiegeCandidat= $candidats[$index_candidat]["siege"]+1;
                $moyenneFormate= intval($moyennesCandidat[$index_candidat]*100)/100;
                $moyenneFormate= number_format($moyenneFormate, 2);
                $this->addToMessage(" - "._("la Liste")." \"".$candidats[$index_candidat]["libelle_liste"]."\" ".
                 _("a comme moyenne si elle obtient")." ".$nbSiegeCandidat." "._("siège(s)")." : ".$moyenneFormate);
            }
            $maxmoyenne = max($moyennesCandidat);
            $nbCandidatMoyMax = 0;
            // liste renvoie la ou les listes qui ont la plus forte moyenne a egalite
            $liste = array();
            for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
                if ($moyennesCandidat[$index_candidat] == $maxmoyenne) {
                    $nbCandidatMoyMax++;
                    $liste[$nbCandidatMoyMax] = $index_candidat;
                }
            }
            if ($nbCandidatMoyMax == 1) { // il n y a qu une liste a la plus forte moyenne
                $candidats[$liste[1]]["siege"] = $candidats[$liste[1]]["siege"] + 1;
                $this->addToMessage(" -> "._("la Liste")." \"".$candidats[$liste[1]]["libelle_liste"]."\" "
                 ." "._("obtient le siège en plus, elle dispose donc de ").$candidats[$liste[1]]["siege"]." siège(s)");
                $siege_attribuer = $siege_attribuer+1;
            } else { // plusieurs listes ont la plus forte moyenne
                // le siege est attribue à la liste qui a le plus grand nombre de suffrage
                $this->addToMessage("il y a plusieurs listes qui ont la même moyenne donc cela se fait suivant le suffrage");
                $nbCandidatMoyMax=0;
                for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
                    $voixliste[$index_candidat] = $candidats[$liste[$index_candidat]]["voix"];
                }
                $voixmax = max($voixliste);
                $liste = array();
                for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
                    if ($candidats[$index_candidat]["voix"] == $voixmax) {
                        $nbCandidatMoyMax++;
                        $liste[$nbCandidatMoyMax] = $index_candidat;
                    }
                }
                if ($nbCandidatMoyMax == 1) {
                    $candidats[$liste[$index_candidat]]["siege"] = $candidats[$liste[$index_candidat]]["siege"] + 1;
                    $this->addToMessage(" -> "._("la Liste")." \"".$candidats[$liste[1]]["libelle_liste"]."\" ".
                     " obtient le siège en plus, car elle a ".
                        "le plus grand nombre de suffrage, et elle dispose donc de ".
                        $candidats[$liste[$index_candidat]]["siege"]." siège(s))");
                        $siege_attribuer = $siege_attribuer+1;
                } else {
                    $this->addToMessage("Il y a égalite de <b>moyenne et de suffrage</b> l'attribution n'a pas été faite.");
                    $this->addToMessage(" LE SIEGE DOIT ETRE ATTRIBUE AU PLUS AGEE DES CANDIDATS SUCEPTIBLE D'ETRE ELU "."
                     [openElu n'a pas cette donnée]");
                    $siege_attribuer = $siege_attribuer+1;
                }
            }
        }
        // attribution du bonus
        for ($index_candidat = 1; $index_candidat <= sizeof($candidats); $index_candidat++) {
            $voixliste[$index_candidat] = $candidats[$index_candidat]["voix"];
        }
        $voixmax = max($voixliste);
        $bonus2fois=0;
        $liste=array();
        for ($index_candidat=1; $index_candidat<=sizeof($candidats); $index_candidat++) {
            if ($candidats[$index_candidat]["voix"]== $voixmax) {
                $bonus2fois++;
                $liste[$bonus2fois]=$index_candidat;
            }
        }
        // le bonus va a la    liste qui a le plus de voix
        $this->addToMessage("Attribution du bonus");
        if ($bonus2fois == 1) {
            $candidats[$liste[1]]['siege'] =$candidats[$liste[1]]['siege']  + $bonus;
            $this->addToMessage("la Liste \"".$candidats[$liste[1]]['libelle_liste']."\" a le bonus de ".
                 $bonus." sièges et elle dispose donc de ". $candidats[$liste[1]]['siege']." siège(s)");
            // verification d'usage qur la majorite absolu pour le premier tour
            if ($candidats[$liste[1]]['voix'] < $exprime / 2) {
                $temp4 = round($exprime / 2, 0);
                $this->addToMessage("<i>la Liste \"".$candidats[$liste[1]]['libelle_liste'].
                 "\" n'a pas la majorité absolue qui est de ".$temp4." voix ");
                $this->addToMessage("Il faut aussi vérifier si la liste a plus de 25% des inscrits pour le 1er tour</i>");
            }
        } else {
            // il y a plusieurs listes avec le même nombre de voix
            // le bonus est attribue a la liste qui a la moyenne d age la plus eleve
            // On affiche le résultat et l'âge moyen de chaque candidat/liste
            for ($index_candidat =1; $index_candidat <= sizeof($candidats); $index_candidat++) {
                    $this->addToMessage(" - "._("la liste ")." \"".$candidats[$index_candidat]['libelle_liste'].
                 "\" "._("a un age moyen de ").$candidats[$index_candidat]['age_moyen']);
            }
            $nbCandidatMoyMax = 0;
            for ($index_candidat = 1; $index_candidat <= sizeof($liste); $index_candidat++) {
                $ageliste[$index_candidat] = $candidats[$index_candidat]["age_moyen"];
            }
            $agemax = max($ageliste);
            for ($index_candidat = 1; $index_candidat <=sizeof($candidats); $index_candidat++) {
                if ($candidats[$index_candidat]["age_moyen"] == $agemax) {
                    $nbCandidatMoyMax++;
                    if ($nbCandidatMoyMax == 1) {
                        $candidats[$index_candidat]['siege'] = $candidats[$index_candidat]['siege'] + $bonus;
                        $this->addToMessage("la Liste \"".$candidats[$liste[1]]['libelle_liste'].
                             " a le bonus de ".$bonus." sièges au bénéfice".
                            " de l'âge moyen le plus élevé et elle dispose donc de ".
                        $candidats[$index_candidat]['siege']." siège(s)");
                        if ($candidats[$liste[1]]['voix'] < $exprime / 2) {
                            $temp4 = round($exprime / 2, 0);
                            $this->addToMessage("la Liste \"".$candidats[$liste[1]]['libelle_liste'].
                             "\" n'a pas la majorité absolue qui est de ".$temp4." voix ");
                            $this->addToMessage("Il faut aussi vérifier si la liste a plus de 25% des inscrits pour le 1er tour ");
                        }
                    } else {
                        $this->addToMessage("Il y a égalite de <b>suffrage et d'age moyen</b>, pour la liste ".
                         $candidats[$index_candidat]['libelle_liste']." le bonus a été attribué à la 1ère liste trouvée. ".
                            "Il semble que ce cas soit juridiquement impossible à traiter [openElu].</i>");
                    }
                }
            }
        }
        return $candidats;
    }

    /**
     * Effectue une requête pour récupèrer les id de tous les candidats/listes
     * de l'élection. A partir de ces id récupère l'instance de chaque candidat.
     * Remplit un tableau contenant pour chaque liste/candidat le nombre de
     * sièges obtenus au conseil municipal et au conseil communautaire.
     * Transforme ce tableau en json et écrit un fichier repartition_sieges.json
     * dans l'animation, dans le répertoire de l'élection.
     *
     * @return boolean true : envoi effectué
     */
    protected function envoi_repartition_sieges_affichage() {
        $repartition = array();
        $election = $this->getVal('election');
        $electionCandidats = $this->get_candidats_election($this->getVal('election'));
        foreach ($electionCandidats as $candidat) {
            //Récupération du nombre de siège obtenu par le candidat/la liste pour
            //le conseil municipal et le conseil communautaire.
            $repartition['siege_lm'][$candidat->getVal('ordre')] = $candidat->getVal('siege');
            $repartition['siege_lc'][$candidat->getVal('ordre')] = $candidat->getVal('siege_com');
            $repartition['siege_lmep'][$candidat->getVal('ordre')] = $candidat->getVal('siege_mep');
        }
        $jsonRepartition = json_encode($repartition);
        $envoi = $this->f->write_file(
            '../aff/res/'.$election.'/repartition_sieges.json',
            $jsonRepartition
        );
        return $envoi;
    }

    /**
     * Récupère la liste des candidats/liste de l'élection avec leurs résultats
     * et leur âge moyen
     * 
     * @param integer $election identifiant de l'élection
     * @param string $suffixe "" : conseil municipale et "_com" : conseil communautaire
     * 
     * @return array $candidats liste des candidats avec leurs résultats
     */
    protected function get_info_candidats_election($election, $suffixe) {
        // réquête permettant de récupérer la liste des candidats à l'élection
        // avec leur numéro d'ordre leur nom, leur âge moyen et leur identfiant pour l'élection
        $sql = sprintf(
            "SELECT
                election_candidat.ordre,
                candidat.libelle,
                candidat.libelle_liste,
                age_moyen,
                age_moyen_com,
                age_moyen_mep,
                election_candidat.election_candidat
            FROM
                %selection_candidat
                INNER JOIN %scandidat
                    ON election_candidat.candidat=candidat.candidat
            WHERE
                election_candidat.election = %d
            GROUP BY
                election_candidat.ordre,
                candidat.libelle,
                candidat.libelle_liste,
                election_candidat.age_moyen,
                election_candidat.age_moyen_com,
                election_candidat.age_moyen_mep,
                election_candidat.election_candidat
            ORDER BY
                election_candidat.ordre",
            $this->f->db->escapeSimple(DB_PREFIXE),
            $this->f->db->escapeSimple(DB_PREFIXE),
            $election
        );
        $res = $this->db->query($sql);
        $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
        $this->f->isDatabaseError($res);

        // stocke les résultats de la requête dans le tableau candidats,
        // fait une requête et récupère le nombre de voie obtenue par le candidats
        // pour l'élection
        $indexCandidat = 0;
        $candidats = array();
        while ($row =& $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $indexCandidat++;
            $temp1='age_moyen'.$suffixe;
            $candidats [$indexCandidat]['libelle'] = $row ['libelle'];
            $candidats [$indexCandidat]['libelle_liste'] = $row ['libelle_liste'];
            $candidats [$indexCandidat]['ordre'] = $row ['ordre'];
            $candidats [$indexCandidat]['age_moyen'] = $row [$temp1];
            $candidats [$indexCandidat]['election_candidat'] = $row ['election_candidat'];

            // récupération du résultat du candidat pour l'élection
            // Ce résultat est calculé en faisant la somme des votes en faveur du candidat
            // pour chaque unité de saisie à condition que ces unités soit des
            // bureaux de vote
            $sql = "select sum(election_resultat.resultat)";
            $sql .= " from ".DB_PREFIXE."election_candidat ";
            $sql .= " inner join ".DB_PREFIXE."election_resultat ";
            $sql .= " on election_candidat.election_candidat=election_resultat.election_candidat ";
            $sql .= " inner join ".DB_PREFIXE."election_unite ";
            $sql .= " on election_resultat.election_unite=election_unite.election_unite ";
            $sql .= " inner join ".DB_PREFIXE."unite ";
            $sql .= " on unite.unite=election_unite.unite ";
            $sql .= " inner join ".DB_PREFIXE."type_unite ";
            $sql .= " on type_unite.type_unite=unite.type_unite ";
            $sql .= " where election_candidat.election_candidat=".$candidats[$indexCandidat]['election_candidat'];
            $sql .= " and type_unite.bureau_vote = true";

            $candidats[$indexCandidat]['voix'] = $this->db->getOne($sql);
            $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
            $this->f->isDatabaseError($candidats[$indexCandidat]['voix']);
        }
        return $candidats;
    }

    /**
     * Calcul et renvoie le nombre total de vote exprime pour une election
     * Le nombre total est calculé en faisant la somme des résultats obtenus
     * par chaque candidats dans les unités ayant un comportement de bureau
     * de vote
     * 
     * @param integer $election identifiant de l'élection
     * 
     * @return integer $voteExprime nombre de vote exprime de l'élection
     */
    protected function get_total_exprime_election($election) {
        // somme des résultats des candidats enregistrés dans les unités
        // ayant des comportements de bureau de vote
        $sql = "SELECT SUM(election_unite.exprime)";
        $sql .= " FROM ".DB_PREFIXE."election_unite ";
        $sql .= " INNER JOIN ".DB_PREFIXE."unite ";
        $sql .= " ON election_unite.unite=unite.unite ";
        $sql .= " INNER JOIN ".DB_PREFIXE."type_unite ";
        $sql .= " ON unite.type_unite=type_unite.type_unite ";
        $sql .= " where election_unite.election=".$election;
        $sql .= " and type_unite.bureau_vote = true";

        $voteExprime = $this->db->getOne($sql);
        $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
        $this->f->isDatabaseError($voteExprime);
        return $voteExprime;
    }

// ======================
// action de verification
// ======================

    /**
     * Vérifie si les unités et la participation ont été créées et si le nombre
     * d'élément présent dans la base de données correspond au nombre d'éléments
     * attendus. Affiche un message avec les résultats de cette vérification.
     * 
     * @return void
     */
    protected function verification() {
        $correct = true;
        $idElection = $this->getVal('election');
        
        //nombre d'unites
        $unites = $this->get_unites_election($idElection);
        $nbUnites = count($unites);
        $message = 'Nombre d\'unités : '.$nbUnites;
        if ($nbUnites === 0) {
            $correct = false;
            $message = 'Aucune unité enregistré';
        }
        $this->addToMessage($message);
        
        $candidats = $this->get_candidats_election($idElection);
        $nbCandidats = count($candidats);
        $message = 'nombre de candidat : '.$nbCandidats;
        if ($nbCandidats === 0) {
            $correct = false;
            $message = 'vous n\'avez pas créé de candidat (onglet candidat(s))';
        }
        $this->addToMessage($message);
        
        //nombre de resultats
        $message = '';
        $resCorrect = true;
        foreach ($candidats as $candidatEl) {
            $resultatsCandidat = $this->f->simple_query(
                'election_resultat',
                'election_resultat',
                'election_candidat',
                $candidatEl->getVal('election_candidat')
            );
            // Il doit exister un lien (résultat) entre le candidat et chaque unités
            $nbResultats = count($resultatsCandidat);
            $difference = $nbResultats - $nbUnites;

            if ($difference != 0) {
                $resCorrect = false;
                $candidat = $this->f->get_element_by_id('candidat', $candidatEl->getVal('candidat'));
                $message .= 'Erreur : le nombre de résultat enregistré pour '.
                    $candidat->getVal('libelle').
                    ' est incorrect<br>';
            }
        }
        $message = $resCorrect ? 'Les candidats sont correctement enregistrés' : $message;
        $correct = $resCorrect && $correct;
        $this->addToMessage($message);
        
        // nombre de tranche
        $tranches = $this->get_participation_election($idElection);
        $nbTranches = count($tranches);
        $message = 'Nombre de tranche horaire : '.$nbTranches;
        if ($nbTranches === 0) {
            $class = 'error';
            $message = 'Aucune tranche horaire enregistrée pour la participation';
        }
        $this->addToMessage($message);

        //nombre de participation
        $partCorrect = true;
        $message = '';
        foreach ($tranches as $tranche) {
            $partUnite = $this->f->simple_query(
                'participation_unite',
                'participation_unite',
                'participation_election',
                $tranche->getVal('participation_election')
            );

            // Il doit exister un lien entre la tranche horaire et chaque unité
            $nbParticipation = count($partUnite);
            $difference = $nbParticipation - $nbUnites;

            if ($difference != 0) {
                $partCorrect = false;
                $class = 'error';
                $horaire = $this->f->get_element_by_id('tranche', $tranche->getVal('tranche'));
                $message .= 'Erreur : la tranche de '.
                    $horaire->getVal('libelle').
                    ' n\'est pas correctement lié aux unités<br>';
            }
        }
        $message = $partCorrect ? 'La participation est correctement enregistrée' : $message;
        $correct = $correct && $partCorrect;
        $this->addToMessage($message);

        // nombre de centaines
        $centainesElection = $this->get_centaines_election();
        $message = 'Nombre de centaines : '.count($centainesElection);
        $this->addToMessage($message);

        // nombre de plans
        $planElection = $this->f->get_element_by_id('plan_election', ']');
        $plansElection = $planElection->get_liste_plans_election($idElection);
        $message = 'Nombre de plans : '.count($plansElection);
        $this->addToMessage($message);

        // nombre d'animations
        $animations = $this->get_animations_election($idElection);
        $message = 'Nombre d\'animations : '.count($animations);
        $this->addToMessage($message);

        return $correct;
    }


    // =================================================================
    // affichage des resultats (modif candidat_election n impacte pas)
    // =================================================================


    /**
     * En consultation, affiche la liste des candidats et leur résultat
     * sous le formulaire
     *
     * @return void
     */
    function afterFormSpecificContent() {
        $maj=$this->getParameter("maj");
        if ($maj==3) {
            echo "<div id=\"affichage_article tableau_bord\" class=\"formEntete ui-corner-all\">";
            switch ($this->getVal('workflow')) {
                case 'Paramétrage':
                    $this->tableau_bord_parametrage();
                    break;
                case 'Simulation':
                    $this->tableau_bord_simulation();
                    break;
                case 'Saisie':
                    $this->tableau_bord_saisie();
                    break;
                case 'Finalisation':
                    $this->tableau_bord_finalisation();
                    break;
                default:
                    $this->tableau_bord_finalisation();
            }
            echo "</div>";
        }
    }

    /**
     * Code html du sous formulaire d'affichage de la liste des candidats
     * et de leur résultats
     *
     * @param integer $election identifiant de l'élection
     *
     * @return void
     */
    protected function tableau_bord_parametrage() {
        $htmlTableau = '';
        $idElection = $this->getVal('election');
        
        // Nombre de candidat et liste des candidats
        // Fait une liste html de chaque candidat avec son libelle et son libelle long
        $candidats = $this->get_candidats_election($idElection);
        $htmlListeCandidat = array_map(function ($candidatElection) {
            $candidat = $this->f->get_element_by_id('candidat', $candidatElection->getVal('candidat'));
            $nom = $candidat->getVal('libelle');
            $cplmtNom = $candidat->getVal('libelle_liste');
            return $nom.' '.$cplmtNom.'<br>';
        }, $candidats);
        
        $htmlTableau .= sprintf(
            '<tr class="tab-data odd">
                <td class="title">Candidats</td>
                <td>%d candidat(s)</td>
                <td>%s</td>
            </tr>',
            count($candidats),
            implode('', $htmlListeCandidat)
        );

        // Phrase donnant le nombre d'unité totale + le nombre d'unité de chaque type
        $unites = $this->get_unites_election($idElection);
        $compteTypeUnite = array();
        foreach ($unites as $electionUnite) {
            $unite = $this->f->get_element_by_id('unite', $electionUnite->getVal('unite'));
            if (! array_key_exists($unite->getVal('type_unite'), $compteTypeUnite)) {
                $compteTypeUnite[$unite->getVal('type_unite')] = 0;
            }
            $compteTypeUnite[$unite->getVal('type_unite')]++;
        }

        $htmlListeType = array_map(function ($key, $value) {
            $type = $this->f->get_element_by_id('type_unite', $key);
            $libelle = $type->getVal('libelle');
            return $libelle.' : '.$value.'<br>';
        }, array_keys($compteTypeUnite), $compteTypeUnite);

        $htmlTableau .= sprintf(
            '<tr class="tab-data even">
                <td class="title">Unites</td>
                <td>%d unite(s)</td>
                <td>%s</td>
            </tr>',
            count($unites),
            implode('', $htmlListeType)
        );


        // Phrase donnant le nombre de tranche horaire et rappellant les horaires
        // d'ouverture / fermeture
        $participation = $this->get_participation_election($idElection);
        $trancheOuv = $this->f->get_element_by_id('tranche', $this->getVal('heure_ouverture'));
        $trancheFer = $this->f->get_element_by_id('tranche', $this->getVal('heure_fermeture'));
        $htmlTableau .= sprintf(
            '<tr class="tab-data odd">
                <td class="title">Participation</td>
                <td>%d tranche(s) horaire</td>
                <td>horaire : %d h - %d h</td>
            </tr>',
            count($participation),
            $trancheOuv->getVal('libelle'),
            $trancheFer->getVal('libelle')
        );

        // Nombre de centaine et nom des centaines
        $centaines = $this->get_centaines_election($idElection);
        $htmlListeCentaine = array_map(function ($centaine) {
            $libelle = $centaine->getVal('libelle');
            return $libelle.'<br>';
        }, $centaines);
        
        $htmlTableau .= sprintf(
            '<tr class="tab-data even">
                <td class="title">Centaines</td>
                <td>%d centaines</td>
                <td>%s</td>
            </tr>',
            count($centaines),
            implode('', $htmlListeCentaine)
        );

        // Nombre d'animation et libellé des animations
        $animations = $this->get_animations_election($idElection);
        $htmlListeAnim = array_map(function ($animation) {
            $libelle = $animation->getVal('libelle');
            return $libelle.'<br>';
        }, $animations);

        $htmlTableau .= sprintf(
            '<tr class="tab-data odd">
                <td class="title">Animations</td>
                <td>%d animation(s)</td>
                <td>%s</td>
            </tr>',
            count($animations),
            implode('', $htmlListeAnim)
        );

        // Nombre de plan et libelle des plans
        $planElection = $this->f->get_element_by_id('plan_election', ']');
        $plans = $planElection->get_liste_plans_election($idElection);
        $htmlListePlan = array_map(function ($planElection) {
            $plan = $this->f->get_element_by_id('plan', $planElection->getVal('plan'));
            $libelle = $plan->getVal('libelle');
            return $libelle.'<br>';
        }, $plans);

        $htmlTableau .= sprintf(
            '<tr class="tab-data even">
                <td class="title">Plans</td>
                <td>%d plan(s)</td>
                <td>%s</td>
            </tr>',
            count($plans),
            implode('', $htmlListePlan)
        );

        printf(
            '<div id="affichage_article" class="formEntete ui-corner-all">
                <fieldset class="cadre ui-corner-all ui-widget-content">
                    <legend>Paramétrage</legend>
                    <table class="tab-tab">
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title"></th>
                            <th class="title">Total</th>
                            <th class="title">Détail</th>
                        </tr>
                        %s
                    </table>
                </fieldset>
            </div>',
            $htmlTableau
        );
    }

    /**
     * Code html du sous formulaire d'affichage de la liste des candidats
     * et de leur résultats
     *
     * @param integer $election identifiant de l'élection
     *
     * @return void
     */
    protected function tableau_bord_saisie() {
        // Remplissage des lignes du tableau de bilan de la saisie des résultats
        $htmlTableauResultat = '';
        $oddEven = 'odd';
        $unites = $this->get_unites_election($this->getVal('election'));

        foreach ($unites as $uniteElec) {
            // Récupération du perimetre de l'élection qui servira a savoir si la participation
            // a été saisie
            if ($uniteElec->getVal('unite') === $this->getVal('perimetre')) {
                $idPerimetre = $uniteElec->getVal('election_unite');
            }

            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            switch ($uniteElec->getVal('saisie')) {
                case 'en attente':
                    $urlIconeSaisie = '../app/img/nonarrive.png';
                    break;
                case 'correcte':
                    $urlIconeSaisie = '../app/img/arrive.png';
                    break;
                case 'en defaut':
                    $urlIconeSaisie = '../app/img/erreur.png';
                    break;
                default:
                    $urlIconeSaisie = '../app/img/nonarrive.png';
                    break;
            }
            $urlIconeAff = $this->f->boolean_string_to_boolean($uniteElec->getVal('envoi_aff')) ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeWeb = $this->f->boolean_string_to_boolean($uniteElec->getVal('envoi_web')) ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            
                $htmlTableauResultat .= sprintf(
                    '<tr class="tab-data %s">
                        <td> %s </td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                    </tr>',
                    $oddEven,
                    $unite->getVal('code_unite').' '.$unite->getVal('libelle'),
                    $urlIconeSaisie,
                    $urlIconeAff,
                    $urlIconeWeb
                );
                // Switch entre odd et even à chaque itération
                $oddEven = $oddEven === 'odd' ? 'even' : 'odd';
        }

        // Remplissage des lignes du tableau de bilan de la saisie de la participation
        $htmlTabParticipation = '';
        $oddEven = 'odd';
        $participations = $this->get_participation_election($this->getVal('election'));

        foreach ($participations as $participation) {
            $tranche = $this->f->get_element_by_id('tranche', $participation->getVal('tranche'));

            $urlIconeSaisie = $participation->est_saisie() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeAff = $participation->est_envoye_a_affichage() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeWeb = $participation->est_envoye_au_web() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            
                $htmlTabParticipation .= sprintf(
                    '<tr class="tab-data %s">
                        <td> %s </td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                    </tr>',
                    $oddEven,
                    $tranche->getVal('libelle'),
                    $urlIconeSaisie,
                    $urlIconeAff,
                    $urlIconeWeb
                );
                // Switch entre odd et even à chaque itération
                $oddEven = $oddEven === 'odd' ? 'even' : 'odd';
        }

        printf(
            '<div id="affichage_article" class="formEntete ui-corner-all">
                <fieldset class="cadre ui-corner-all ui-widget-content">
                    <legend>Saisie</legend>
                    <table class="tab-tab">
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title">Unités</th>
                            <th class="title">Saisie</th>
                            <th class="title">Aff</th>
                            <th class="title">Web</th>
                        </tr>
                        %s
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title">Horaires</th>
                            <th class="title">Saisie</th>
                            <th class="title">Aff</th>
                            <th class="title">Web</th>
                        </tr>
                        %s
                    </table>
                </fieldset>
            </div>',
            $htmlTableauResultat,
            $htmlTabParticipation
        );
    }

    /**
     * Code html du sous formulaire d'affichage de la liste des candidats
     * et de leur résultats
     *
     * @param integer $election identifiant de l'élection
     *
     * @return void
     */
    protected function tableau_bord_finalisation() {
        $htmlTableau = '';
        $oddEven = 'odd';
        $unites = $this->get_unites_election($this->getVal('election'));

        // Préparation des colonnes dédiées aux résultats des candidats et
        // au total des voix obtenus par les candidats
        $candidats = $this->get_candidats_election($this->getVal('election'));

        foreach ($unites as $uniteElec) {
            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            if ($uniteElec->getVal('unite') === $this->getVal('perimetre')) {
                $perimetreElec = $uniteElec;
            }
            if ($uniteElec->is_bureau()) {
                $htmlResCandidats = '';
                // Préparation des résultats des colonnes dédiées aux candidats
                foreach ($candidats as $candidat) {
                    $resultat = $uniteElec->get_resultat_candidat_unite(
                        $candidat->getVal('election_candidat'),
                        $uniteElec->getVal('election_unite')
                    );
                    $htmlResCandidats .= sprintf(
                        '<td>%d</td>',
                        $resultat
                    );
                }

                $htmlTableau .= sprintf(
                    '<tr class="tab-data %s">
                        <td> %s </td>
                        <td> %d </td>
                        <td> %d </td>
                        <td> %d </td>
                        <td> %d </td>
                        <td> %d </td>
                        <td> %d </td>
                        <td> %d </td>
                        %s
                    </tr>',
                    $oddEven,
                    $unite->getVal('code_unite').' '.$unite->getVal('libelle'),
                    $uniteElec->getVal('inscrit'),
                    $uniteElec->getVal('votant'),
                    $uniteElec->getVal('emargement'),
                    $uniteElec->getVal('procuration'),
                    $uniteElec->getVal('blanc'),
                    $uniteElec->getVal('nul'),
                    $uniteElec->getVal('exprime'),
                    $htmlResCandidats
                );
                // Switch entre odd et even à chaque itération
                $oddEven = $oddEven === 'odd' ? 'even' : 'odd';
            }
        }

        
        // Préparation des titres des colonnes au nom des différents candidats et
        // du total de voix obtenus par ces candidats
        // Le total est récupéré en récupérant les résultats enregistrés sur le
        // périmètre de l'élection
        $htmlColonneCandidats = '';
        $htmlTotalCandidat = '';
        foreach ($candidats as $candidatElec) {
            // Entete des colonnes des résultats candidats
            $candidat = $this->f->get_element_by_id('candidat', $candidatElec->getVal('candidat'));
            $htmlColonneCandidats .= sprintf(
                '<th>%s</th>',
                $candidat->getVal('libelle')
            );

            // Total obtenu pour chaque candidat
            $total = $perimetreElec->get_resultat_candidat_unite(
                $candidatElec->getVal('election_candidat'),
                $perimetreElec->getVal('election_unite')
            );
            $htmlTotalCandidat .= sprintf(
                '<td>%d</td>',
                $total
            );
        }

        // Ligne du total du tableau
        $htmlTableau .= sprintf(
            '<tr class="tab-data %s">
                <td> Total </td>
                <td> %d </td>
                <td> %d </td>
                <td> %d </td>
                <td> %d </td>
                <td> %d </td>
                <td> %d </td>
                <td> %d </td>
                %s
            </tr>',
            $oddEven,
            $perimetreElec->getVal('inscrit'),
            $perimetreElec->getVal('votant'),
            $perimetreElec->getVal('emargement'),
            $perimetreElec->getVal('procuration'),
            $perimetreElec->getVal('blanc'),
            $perimetreElec->getVal('nul'),
            $perimetreElec->getVal('exprime'),
            $htmlTotalCandidat
        );

        printf(
            '<div id="affichage_article" class="formEntete ui-corner-all">
                <fieldset class="cadre ui-corner-all ui-widget-content">
                    <legend>Résultats - %s</legend>
                    <table class="tab-tab">
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title">Unités</th>
                            <th class="title">Ins</th>
                            <th class="title">Vot</th>
                            <th class="title">Em</th>
                            <th class="title">Proc</th>
                            <th class="title">Blancs</th>
                            <th class="title">Nuls</th>
                            <th class="title">Exp</th>
                            %s
                        </tr>
                        %s
                    </table>
                </fieldset>
            </div>',
            $this->getVal('libelle'),
            $htmlColonneCandidats,
            $htmlTableau
        );
    }

    /**
     * Code html du sous formulaire d'affichage de la liste des candidats
     * et de leur résultats
     *
     * 
     * @return void
     */
    protected function tableau_bord_simulation() {
        // Remplissage des lignes du tableau de bilan de la saisie des résultats
        $htmlTableauResultat = '';
        $oddEven = 'odd';
        $unites = $this->get_unites_election($this->getVal('election'));

        foreach ($unites as $uniteElec) {
            // Récupération du perimetre de l'élection qui servira a savoir si la participation
            // a été saisie
            if ($uniteElec->getVal('unite') === $this->getVal('perimetre')) {
                $idPerimetre = $uniteElec->getVal('election_unite');
            }

            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            switch ($uniteElec->getVal('saisie')) {
                case 'en attente':
                    $urlIconeSaisie = '../app/img/nonarrive.png';
                    break;
                case 'correcte':
                    $urlIconeSaisie = '../app/img/arrive.png';
                    break;
                case 'en defaut':
                    $urlIconeSaisie = '../app/img/erreur.png';
                    break;
                default:
                    $urlIconeSaisie = '../app/img/nonarrive.png';
                    break;
            }
            $urlIconeAff = $this->f->boolean_string_to_boolean($uniteElec->getVal('envoi_aff')) ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeWeb = $this->f->boolean_string_to_boolean($uniteElec->getVal('envoi_web')) ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            
                $htmlTableauResultat .= sprintf(
                    '<tr class="tab-data %s">
                        <td> %s </td>
                        <td>%d</td>
                        <td>%d</td>
                        <td>%d</td>
                        <td>%d</td>
                        <td>%d</td>
                        <td>%d</td>
                        <td>%d</td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                    </tr>',
                    $oddEven,
                    $unite->getVal('code_unite').' '.$unite->getVal('libelle'),
                    $uniteElec->getVal('inscrit'),
                    $uniteElec->getVal('emargement'),
                    $uniteElec->getVal('procuration'),
                    $uniteElec->getVal('votant'),
                    $uniteElec->getVal('blanc'),
                    $uniteElec->getVal('nul'),
                    $uniteElec->getVal('exprime'),
                    $urlIconeSaisie,
                    $urlIconeAff,
                    $urlIconeWeb
                );
                // Switch entre odd et even à chaque itération
                $oddEven = $oddEven === 'odd' ? 'even' : 'odd';
        }

        // Remplissage des lignes du tableau de bilan de la saisie de la participation
        $htmlTabParticipation = '';
        $oddEven = 'odd';
        $participations = $this->get_participation_election($this->getVal('election'));

        foreach ($participations as $participation) {
            $tranche = $this->f->get_element_by_id('tranche', $participation->getVal('tranche'));
            // Si le nombre de votant du perimetre est différent de  c'est que les votants
            // des unités qu'il contiens ont été saisie
            $votant = $participation->get_votant_par_unite_participation(
                $participation->getVal('participation_election'),
                $idPerimetre
            );

            $urlIconeSaisie = $participation->est_saisie() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeAff = $participation->est_envoye_a_affichage() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            $urlIconeWeb = $participation->est_envoye_au_web() ?
                '../app/img/arrive.png' : '../app/img/nonarrive.png';
            
                $htmlTabParticipation .= sprintf(
                    '<tr class="tab-data %s">
                        <td> %s </td>
                        <td>%d</td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                        <td><img src="%s"></td>
                    </tr>',
                    $oddEven,
                    $tranche->getVal('libelle'),
                    $votant,
                    $urlIconeSaisie,
                    $urlIconeAff,
                    $urlIconeWeb
                );
                // Switch entre odd et even à chaque itération
                $oddEven = $oddEven === 'odd' ? 'even' : 'odd';
        }

        printf(
            '<div id="affichage_article" class="formEntete ui-corner-all">
                <fieldset id="resultats" class="cadre ui-corner-all ui-widget-content">
                    <legend>Résultats</legend>
                    <table class="tab-tab">
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title">Unités</th>
                            <th class="title">Inscrit</th>
                            <th class="title">Emargement</th>
                            <th class="title">Procuration</th>
                            <th class="title">Votant</th>
                            <th class="title">Blanc</th>
                            <th class="title">Nul</th>
                            <th class="title">Exprime</th>
                            <th class="title">Saisie</th>
                            <th class="title">Aff</th>
                            <th class="title">Web</th>
                        </tr>
                        %s
                    </table>
                </fieldset>
                <fieldset id="participation" class="cadre ui-corner-all ui-widget-content">
                    <legend>Participation</legend>
                    <table class="tab-tab">
                        <tr class="ui-tabs-nav ui-accordion ui-state-default tab-title">
                            <th class="title">Horaires</th>
                            <th class="title">Votants</th>
                            <th class="title">Saisie</th>
                            <th class="title">Aff</th>
                            <th class="title">Web</th>
                        </tr>
                        %s
                    </table>
                </fieldset>
            </div>',
            $htmlTableauResultat,
            $htmlTabParticipation
        );
    }

    /**
     * Verifie si l'election est une saisie par centaine
     *
     * @return boolean true : centaine , false : election
     */
    function is_centaine() {
        return $this->f->boolean_string_to_boolean($this->getVal('is_centaine'));
    }


    /**
     * Reset l'élection en supprimant :
     *  - ses candidats et leurs résultats
     *  - ses centaines
     *  - ses animations
     *  - ses plans
     * Vide également le repertoire web de l'élection et les répertoires des
     * animations sont supprimés
     *
     * Affiche un message indiquant si le reset à réussi ou si il y a eu une
     * erreur
     *
     * @return boolean indique si la suppression à réussi
     */
    protected function reset_election() {
        $suppression = true;
        $message = 'Reset de l\'élection effectué';
        // Tant que le traitement reussi, la suppression suivante va s'effectuer.
        // En revanche si une suppression echoue alors la suppressionn du reste n'est pas faite
        // et un message d'erreur est affiché
        $succes = $this->supprimer_candidats_election($this->getVal('election'));
        $message .= ! $succes ? 'Suppression des candidats non effectuée' : '';
        $suppression = $suppression && $succes;

        $succes = $this->supprimer_centaines_election($this->getVal('election'));
        $message .= ! $succes ? 'Suppression des animations non effectuée' : '';
        $suppression = $suppression && $succes;

        $succes = $this->supprimer_animations_election($this->getVal('election'));
        $message .= ! $succes ? 'Suppression des animations non effectuée' : '';
        $suppression = $suppression && $succes;

        $succes = $this->supprimer_plans_election($this->getVal('election'));
        $message .= ! $succes ? 'Suppression des plans non effectuée' : '';
        $suppression = $suppression && $succes;

        $succes = $this->supprimer_delegation_election($this->getVal('election'));
        $message .= ! $succes ? 'Suppression des délégations non effectuée' : '';
        $suppression = $suppression && $succes;

        // Remise à zéro des résultats
        $annulationRes = true;
        $unites = $this->get_unites_election($this->getVal('election'));
        foreach ($unites as $unite) {
            // Reinitialise les résultats et supprime les fichiers de l'affichage
            $reinitRes = $unite->reset_resultat();
            $supprAff = $unite->action_desaffichage();
            $supprWeb = $unite->action_annuler_web();
            $annulationRes = $annulationRes && $reinitRes && $supprAff && $supprWeb;
        }
        $message .= ! $annulationRes ? 'Les résultats n\'ont pas été réinitialisés' : '';
        
        $annulationPart = true;
        $participations = $this->get_participation_election($this->getVal('election'));
        foreach ($participations as $participation) {
            $annulationPart = $annulationPart && $participation->annuler_resultat();
        }
        $message .= ! $annulationRes ? 'La participation n\'a pas été réinitialisée' : '';

        $this->addToMessage($message);
        return $suppression && $annulationRes && $annulationPart;
    }

    /**
     * Supprime la participation par unité de l'élection
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_participation_unite($idElection) {
        $succes = true;
        $unitesElection = $this->get_unites_election($idElection);
        // Suppression de tous les liens entre les unités et la participation
        // de l'élection
        foreach ($unitesElection as $unite) {
            $participationsUnite = $this->f->simple_query(
                'participation_unite',
                'participation_unite',
                'election_unite',
                $unite->getVal('election_unite')
            );

            foreach ($participationsUnite as $idParticipation) {
                $participationUnite = $this->f->get_inst__om_dbform(array(
                    'obj' => 'participation_unite',
                    'idx' => $idParticipation
                ));
                //Réinitialisation et remplissage du tableau de suppression
                $suppression = array();
                foreach ($participationUnite->champs as $id => $champs) {
                    $suppression[$champs] = $participationUnite->val[$id];
                }
                //Suppression
                if (! $participationUnite->supprimer($suppression)) {
                    $this->f->db->rollback();
                    $succes = false;
                    break;
                } else {
                    $this->f->db->commit();
                }
            }
        }
        return $succes;
    }

    /**
     * Supprime les centaines liées à l'élection
     *
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_centaines_election() {
        $succes = true;
        $centaines = $this->get_centaines_election();
        // Suppression de tous les liens entre les unités et la participation
        // de l'élection
        foreach ($centaines as $centaine) {
            //Réinitialisation et remplissage du tableau de suppression
            $suppression = array();
            foreach ($centaine->champs as $id => $champs) {
                $suppression[$champs] = $centaine->val[$id];
            }
            //Suppression
            if (! $centaine->supprimer($suppression)) {
                $this->f->db->rollback();
                $succes = false;
                break;
            } else {
                $this->f->db->commit();
            }
        }
        return $succes;
    }

    /**
     * Supprime la participation par tranche horaire et par unité de
     * l'élection
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_participation_election($idElection) {
        $succes = $this->supprimer_participation_unite($idElection);
        $participations = $this->get_participation_election($idElection);
        foreach ($participations as $participation) {
            $suppression = array();
            foreach ($participation->champs as $id => $champs) {
                if (isset($participation->val[$id])) {
                    $suppression[$champs] = $participation->val[$id];
                }
            }
            //validation de la suppression
            if (! $participation->supprimer($suppression)) {
                $succes = false;
                $this->f->db->rollback();
                break;
            } else {
                $this->f->db->commit();
            }
        }
        // Suppression du fichier contenant la participation sur le portail web
        $pathWeb = $this->get_election_web_path();
        if (file_exists($pathWeb.'/participation.inc')) {
            if (! unlink($pathWeb.'/participation.inc')) {
                $this->addToMessage('Echec de la supression des résultats sur les animations');
                $succes = false;
            }
        }
        // Suppression du répertoire contenant la participation sur l'animation
        if (file_exists('../aff/res/'.$idElection.'/bpar')) {
            if (! $this->f->rrmdir('../aff/res/'.$idElection.'/bpar')) {
                $this->addToMessage('Echec de la supression des résultats sur les animations');
                $succes = false;
            }
        }
        return $succes;
    }

    /**
     * Supprime tous les candidats de l'élection et leurs résultats
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_candidats_election($idElection) {
        $succes = true;
        $candidats = $this->get_candidats_election($idElection);
        foreach ($candidats as $candidat) {
            $succesSuppr = $candidat->supprimer_resultats_candidat();
            $suppression = array();
            foreach ($candidat->champs as $id => $champs) {
                $suppression[$champs] = $candidat->val[$id];
            }
            //validation de la suppression
            if (! $succesSuppr || ! $candidat->supprimer($suppression)) {
                $succes = false;
                $this->f->db->rollback();
                break;
            } else {
                $this->f->db->commit();
            }
        }
        // Suppression de la liste des candidats de l'animation
        if (file_exists('../aff/res/'.$idElection.'/candidats.json')) {
            unlink('../aff/res/'.$idElection.'/candidats.json');
        }
        return $succes;
    }

    /**
     * Supprime toutes les unités de l'élection
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_unites_election($idElection) {
        $succes = true;
        $unitesElection = $this->get_unites_election($idElection);
        foreach ($unitesElection as $electionUnite) {
            $unite = $this->f->get_element_by_id('unite', $electionUnite->getVal('unite'));
            $suppression = array();
            foreach ($electionUnite->champs as $id => $champs) {
                if (isset($electionUnite->val[$id])) {
                    $suppression[$champs] = $electionUnite->val[$id];
                }
            }
            //validation de la suppression
            if (! $electionUnite->supprimer($suppression)) {
                $succes = false;
                $this->f->db->rollback();
                break;
            } else {
                $this->f->db->commit();
            }
            // Suppression des fichiers liés aux unités sur le portail web
            // Suppression du fichier contenant la participation sur le portail web
            $pathWeb = $this->get_election_web_path();
            if (file_exists($pathWeb.'/b'.$unite->getVal('ordre').'.html')) {
                if (! unlink($pathWeb.'/b'.$unite->getVal('ordre').'.html')) {
                    $this->addToMessage('Echec de la supression des résultats sur les animations');
                    $succes = false;
                }
            }
        }
        // Suppression du répertoire contenant les résultats sur l'animation
        if (file_exists('../aff/res/'.$idElection.'/bres')) {
            if (! $this->f->rrmdir('../aff/res/'.$idElection.'/bres')) {
                $this->addToMessage('Echec de la supression des résultats sur les animations');
                $succes = false;
            }
        }
        return $succes;
    }

    /**
     * Supprime toutes les animations de l'élection.
     * Supprime également le repertoire d'affichage des animations.
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_animations_election($idElection) {
        $succes = true;
        $animations = $this->get_animations_election($idElection);
        foreach ($animations as $animation) {
            $suppression = array();
            foreach ($animation->champs as $id => $champs) {
                // Evite des erreurs liées aux champs du formulaire qui n'existe pas en base
                if (array_key_exists($id, $animation->val)) {
                    $suppression[$champs] = $animation->val[$id];
                }
            }
            //validation de la suppression
            if (! $animation->supprimer($suppression)) {
                $succes = false;
                $this->f->db->rollback();
                break;
            } else {
                // Suppression du répertoire de l'animation
                $repertoire = '../aff/res/'.$idElection.'/animation_'.$animation->getVal('animation');
                if (file_exists($repertoire)) {
                    $this->f->rrmdir($repertoire);
                }
                $this->f->db->commit();
            }
        }
        return $succes;
    }

    /**
     * Supprime tous les plans de l'élection
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_plans_election($idElection) {
        $succes = true;
        // Utilisation d'une méthode de la classe plan_election pour récupérer
        // la liste des plans
        $planElection = $this->f->get_element_by_id('plan_election', ']');
        $webPath = $this->get_election_web_path();
        $plansElection = $planElection->get_liste_plans_election($idElection);
        foreach ($plansElection as $plan) {
            $suppression = array();
            foreach ($plan->champs as $id => $champs) {
                $suppression[$champs] = $plan->val[$id];
            }
            //validation de la suppression
            if (! $plan->supprimer($suppression)) {
                $this->f->db->rollback();
                $succes = false;
                break;
            } else {
                // Suppression du répertoire du plan de l'élection
                $repertoire = $webPath.'/plan_'.$plan->getVal('plan');
                if (file_exists($repertoire)) {
                    $this->f->rrmdir($repertoire);
                }
                $this->f->db->commit();
            }
        }
        if ($succes) {
            // Suppression du fichier de paramétrage des plans
            if (file_exists($webPath.'/plans.json')) {
                unlink($webPath.'/plans.json');
            }
        }
        return $succes;
    }

    /**
     * Supprime toutes les délégations liées à l'élection
     *
     * @param integer $idElection identifiant de l'élection
     * @return boolean indique si la suppression à réussi
     */
    protected function supprimer_delegation_election($idElection) {
        $succes = true;
        // Récupération de la liste des délégations
        $idsDelegation = $this->f->simple_query('delegation', 'delegation', 'election', $idElection);
        foreach ($idsDelegation as $idDelegation) {
            $delegation = $this->f->get_element_by_id('delegation', $idDelegation);
            $suppression = array();
            foreach ($delegation->champs as $id => $champs) {
                $suppression[$champs] = $delegation->val[$id];
            }
            //validation de la suppression
            if (! $delegation->supprimer($suppression)) {
                $this->f->db->rollback();
                $succes = false;
                break;
            }
            $this->f->db->commit();
        }
        return $succes;
    }

    /**
     * Récupére toutes les animations d'une election.
     *
     * @param integer identifiant de l'élection
     * @return array liste des instances d'animation
     */
    protected function get_animations_election($idElection) {
        $idAnimations = $this->f->simple_query(
            'animation',
            'animation',
            'election',
            $idElection
        );

        $animation = array();
        foreach ($idAnimations as $idAnimation) {
            $animation[] = $this->f->get_element_by_id('animation', $idAnimation);
        }
        return $animation;
    }

    /**
     * Récupére toutes les unites d'une election
     *
     * @param integer identifiant de l'élection
     * @return array liste des unites
     */
    public function get_unites_election($idElection) {
        $unitesElection = array();
        if (! empty($idElection)) {
            $sql = sprintf(
                "SELECT
                    election_unite
                FROM
                    %selection_unite
                    INNER JOIN %sunite on election_unite.unite = unite.unite
                WHERE
                    election = %d
                ORDER BY
                    ordre ASC",
                $this->f->db->escapeSimple(DB_PREFIXE),
                $this->f->db->escapeSimple(DB_PREFIXE),
                $idElection
            );
            $res = $this->f->db->query($sql);
            $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
            if ($this->f->isDatabaseError($res, true)) {
                $this->addToLog(__METHOD__." problèmes sur la requête : ".$res->getDebugInfo().";", DEBUG_MODE);
                $this->addToMessage("Erreur : la liste des unités de l'élection n'a pas pu être récupérée");
                return;
            }
    
            while ($row =& $res->fetchRow(DB_FETCHMODE_ASSOC)) {
                $unitesElection[] = $this->f->get_element_by_id('election_unite', $row['election_unite']);
            }
        }
        return $unitesElection;
    }

    /**
     * Récupére toutes la participation par tranches horaires d'une election
     *
     * @param integer identifiant de l'élection
     * @return array liste des tranches horaires de participation
     */
    public function get_participation_election($idElection) {
        $idParticipations= $this->f->simple_query(
            'participation_election',
            'participation_election',
            'election',
            $idElection
        );

        $participations = array();
        foreach ($idParticipations as $idParticipation) {
            $participations[] = $this->f->get_element_by_id(
                'participation_election',
                $idParticipation
            );
        }
        return $participations;
    }

    /**
     * Récupére toutes les candidats d'une election
     *
     * @param integer identifiant de l'élection
     * @return array liste des candidats
     */
    public function get_candidats_election($idElection) {
        $electionCandidats = array();
        if (! empty($idElection)) {
            $sql = sprintf(
                'SELECT
                    election_candidat
                FROM
                    '.DB_PREFIXE.'election_candidat
                WHERE
                    election = %d
                ORDER BY
                    election_candidat.ordre',
                $idElection
            );
            $res = $this->f->db->query($sql);

            $this->addToLog(__METHOD__."(): db->query(\"".$sql."\");", VERBOSE_MODE);
            if ($this->f->isDatabaseError($res, true)) {
                $this->addToLog(__METHOD__." database error:".$res->getDebugInfo().";", DEBUG_MODE);
                $this->addToMessage("Erreur : la liste des candidats de l'élection n'a pas pu être récupérée");
                return true;
            }

    
            $electionCandidats = array();
            while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
                $electionCandidats[] = $this->f->get_element_by_id(
                    'election_candidat',
                    $row['election_candidat']
                );
            }
        }
        return $electionCandidats;
    }

    /**
     * Récupére la liste des instances des centaines rattachées
     * à l'élection
     *
     * @return array liste des centaines
     */
    public function get_centaines_election() {
        $idElection = $this->getVal('election');
        $centaines = array();
        if (! empty($idElection)) {
            $idCentaines= $this->f->simple_query(
                'election',
                'election',
                'election_reference',
                $idElection
            );

            foreach ($idCentaines as $idCentaine) {
                $centaine = $this->f->get_element_by_id('election', $idCentaine);
                // Vérifie que c'est bien une centaine avant de l'ajouter à la liste
                if ($centaine->is_centaine()) {
                    $centaines[] = $centaine;
                }
            }
        }
        return $centaines;
    }

    /**
     * Indique si l'étape en cours est celle du paramétrage
     *
     * @return boolean
     */
    public function is_etape_parametrage() {
        $workflow = $this->getVal('workflow');
        return $workflow === 'Paramétrage';
    }

    /**
     * Indique si l'étape en cours n'est pas celle du paramétrage
     *
     * @return boolean
     */
    public function pas_etape_parametrage() {
        return ! $this->is_etape_parametrage();
    }

    /**
     * Indique si l'étape en cours est celle de la simulation
     *
     * @return boolean
     */
    public function is_etape_simulation() {
        $workflow = $this->getVal('workflow');
        return $workflow === 'Simulation';
    }

    /**
     * Indique si l'étape en cours n'est pas celle de la simulation
     *
     * @return boolean
     */
    public function pas_etape_simulation() {
        return ! $this->is_etape_simulation();
    }

    /**
     * Indique si l'étape en cours est celle de la saisie
     *
     * @return boolean
     */
    public function is_etape_saisie() {
        $workflow = $this->getVal('workflow');
        return $workflow === 'Saisie';
    }

    /**
     * Indique si l'étape en cours n'est pas celle de la saisie
     *
     * @return boolean
     */
    public function pas_etape_saisie() {
        return ! $this->is_etape_saisie();
    }

    /**
     * Indique si l'étape en cours est celle de la finalisation
     *
     * @return boolean
     */
    public function is_etape_finalisation() {
        $workflow = $this->getVal('workflow');
        return $workflow === 'Finalisation';
    }

    /**
     * Indique si l'étape en cours n'est pas celle de finalisation
     *
     * @return boolean
     */
    public function pas_etape_finalisation() {
        return ! $this->is_etape_finalisation();
    }

    /**
     * Indique si l'élection est archivée
     *
     * @return boolean
     */
    public function is_archivee() {
        $workflow = $this->getVal('workflow');
        return $workflow === 'Archivage';
    }

    /**
     * Indique si l'élection n'est pas archivée
     *
     * @return boolean
     */
    public function non_archivee() {
        return ! $this->is_archivee();
    }

    /**
     * VIEW - view_import_inscrits.
     *
     * @return void
     */
    function view_import_inscrits() {
        $this->checkAccessibility();
        $obj = "election_unite";
        require_once "../obj/import_specific.class.php";
        $i = new import_specific();
        $i->compute_import_list();
        $i->set_params(array("election_id" => $this->getVal($this->clePrimaire)));
        $i->set_form_action_url($this->getDataSubmit());
        $i->set_form_back_link_url($this->get_back_link("formulaire"));
        if (isset($_POST["submit-csv-import"])) {
            $i->treatment_import($obj);
        }
        $i->display_import_form($obj);
    }

    function getFormTitle($ent) {
        //
        if ($this->getParameter("maj") == 5) {
            return $ent." -> ".__("import des inscrits");
        }
        //
        return parent::getFormTitle($ent);
    }
}
