<?php
//$Id$
//gen openMairie le 05/06/2019 12:14

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

class participation_election extends participation_election_gen {

    /**
     * Liste des instances des unités de l'élection
     *
     * @var integer
     */
    protected $unitesPerimetre;

    /**
     * Récupère la liste des instances des unités de l'élection
     * @return array
     */
    protected function get_unites_perimetre() {
        return $this->unitesPerimetre;
    }

    /**
     * @param array $unitesPerimetre
     */
    protected function set_unites_perimetre($unitesPerimetre) {
        $this->unitesPerimetre = $unitesPerimetre;
    }

    /**
     * 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!="]") {
            // nombre d'unites a afficher depuis election_unite
            $idElection = $this->getVal("election");
            $election = $this->f->get_element_by_id('election', $idElection);
            $unites = $election->get_unites_election($idElection);
            foreach ($unites as $unite) {
                $this->champs[] = 'unite'.$unite->getVal('unite');
            }
            $this->set_unites_perimetre($unites);

            // champs servant à récupérer l'heure d'ouverture du formulaire
            $this->champs[] = 'ouverture';
        }
    }

    /**
     * Definition des actions disponibles sur la classe
     *
     * @return void
     */
    function init_class_actions() {
        parent::init_class_actions();
        // la suppression n'est pas possible
        $this->class_actions[2] = '';
        // la modification n'est possible que durant les étapes de saisie et de simulation
        $this->class_actions[1]['portlet']['libelle'] = 'Saisir la participation';
        $this->class_actions[1]['condition'][1] = 'is_saisie_or_simulation';

        // ACTION - 04 - affichage
        $this->class_actions[4] = array(
            "identifier" => "affichage",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("envoyer à l'animation"),
                "order" => 50,
                "class" => "arrow-right-16",
            ),
            "view" => "formulaire",
            "method" => "envoyer_affichage",
            "button" => "diffuser",
            "permission_suffix" => "publication_part_aff",
            'condition' => array(
                'is_finalisation_saisie_or_simulation'
            )
        );

        // ACTION - 06 - remiseazero
        $this->class_actions[5] = array(
            "identifier" => "desafficher",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("supprimer de l'animation"),
                "order" => 50,
                "class" => "delete-16",
            ),
            "view" => "formulaire",
            "method" => "supprimer_affichage",
            "permission_suffix" => "publication_part_aff",
            'condition' => array(
                'is_saisie_or_simulation',
                'est_envoye_a_affichage'
            )
        );

        // ACTION - 06 - envoyer au portail web
        $this->class_actions[6] = array(
            "identifier" => "web",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("envoyer au portail web"),
                "order" => 60,
                "class" => "arrow-right-16",
            ),
            "view" => "formulaire",
            "method" => "envoyer_web",
            "button" => "diffuser",
            "permission_suffix" => "publication_part_web",
            'condition' => array(
                'is_finalisation_saisie_or_simulation'
            )
        );

        // ACTION - 07 - supprimer du portail web
        $this->class_actions[7] = array(
            "identifier" => "depublier_web",
            "portlet" => array(
                "type" => "action-direct-with-confirmation",
                "libelle" => _("supprimer du portail web"),
                "order" => 70,
                "class" => "delete-16",
            ),
            "view" => "formulaire",
            "method" => "supprimer_web",
            "permission_suffix" => "publication_part_web",
            'condition' => array(
                'is_saisie_or_simulation',
                'est_envoye_au_web'
            )
        );

        // ACTION - 0020 - participation_perimetre
        //
        $this->class_actions[20] = array (
            "identifier" => "perimetre_enfants",
            "view" => "get_lien_perimetre_enfants",
            "permission_suffix" => "consulter",
        );
    }

    /**
     * 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");
        }

        // Type des champs dédiés à la saisie de la participation par unité
        // Les champs liés aux unités ne sont visible que pour la saisie (modification)
        // Les périmètres ne sont pas saisissable par l'utilisateur
        $unites = $this->get_unites_perimetre();
        foreach ($unites as $uniteElec) {
            $form->setType('unite'.$uniteElec->getVal('unite'), "hidden");
            if ($maj == 1) {
                $form->setType('unite'.$uniteElec->getVal('unite'), "text");
                $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
                $isPerimetre = $this->f->boolean_string_to_boolean($unite->getVal('perimetre'));
                if ($isPerimetre) {
                    $form->setType('unite'.$uniteElec->getVal('unite'), "hiddenstaticnum");
                }
            }
        }

        if ($maj==1) {
            // L'élection et la tranche horaire ne doivent pas pouvoir être modifiées
            $form->setType('election', "selecthiddenstatic");
            $form->setType('tranche', "selecthiddenstatic");
        }

        // Champs permettant de récupérer l'heure de la dernière modification et de
        // l'ouverture du formulaire. Pas besoin de les afficher car l'utilisateur
        // n'a pas à les remplir
        $form->setType('ouverture', "hidden");
        $form->setType('date_derniere_modif', "hidden");
    }

    /**
     * Methode setTaille
     */
    function setTaille(&$form, $maj) {
        parent::setTaille($form, $maj);
        $unites = $this->get_unites_perimetre();
        foreach ($unites as $uniteElec) {
            $form->setTaille('unite'.$uniteElec->getVal('unite'), 11);
        }
    }

    /**
     * Methode setMax
     */
    function setMax(&$form, $maj) {
        parent::setMax($form, $maj);
        $unites = $this->get_unites_perimetre();
        foreach ($unites as $uniteElec) {
            $form->setMax('unite'.$uniteElec->getVal('unite'), 11);
        }
    }

    /**
     * SETTER FORM - setOnchange.
     *
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     *
     * @return void
     */
    function setOnchange(&$form, $maj) {
        parent::setOnchange($form, $maj);
        $election = $this->getVal('election');
        if ($maj==1) {
            $unites = $this->get_unites_perimetre();
            foreach ($unites as $uniteElec) {
                $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
                $isPerimetre = $this->f->boolean_string_to_boolean($unite->getVal('perimetre'));
                if (! $isPerimetre) {
                    $form->setOnchange(
                        'unite'.$uniteElec->getVal('unite'),
                        "VerifNum(this);
                            update_participation_perimetre('$election')"
                    );
                }
            }
        }
    }

    /**
     * SETTER FORM - setLib.
     *
     * @param formulaire $form Instance formulaire.
     * @param integer $maj Identifant numérique de l'action.
     *
     * @return void
     */
    function setLib(&$form, $maj) {
        parent::setLib($form, $maj);
        $form->setLib('participation_election', 'id');
        $form->setLib('ouverture', 'ouverture');

        // Libellé des unités de la forme : code - libellé unité
        $unites = $this->get_unites_perimetre();
        foreach ($unites as $uniteElec) {
            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            $libelle = $unite->getVal('libelle');
            $code = $unite->getVal('code_unite');
            $form->setLib('unite'.$uniteElec->getVal('unite'), $code.' '.$libelle);
        }
    }

    /**
     * SETTER FORM - set_form_default_values
     *
     * Récupération de la participation enregistré pour chaque unité et atribution
     * de ces valeurs au champ correspondant.
     *
     * @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) {
        if (($validation==0 && $maj==1) || ($maj != 1)) {
            $unites = $this->get_unites_perimetre();
            foreach ($unites as $unite) {
                $form->setVal('unite'.$unite->getVal('unite'), null, $validation);
                $votant = $this->get_votant_par_unite_participation(
                    $this->getVal('participation_election'),
                    $unite->getVal('election_unite')
                );
                // Vérifie le type de votant avant d'en attribuer la valeur aux champs
                if (is_numeric($votant)) {
                    $form->setVal('unite'.$unite->getVal('unite'), $votant, $validation);
                }
            }
        }

        // Récupère le temps à l'ouverture du formulaire d'ajout, de modification et de suppression
        if ($validation == 0) {
            $form->setVal('ouverture', microtime(true));
        }
    }

    /**
     * Vérifie la validité des valeurs en mode CREATE & UPDATE.
     *
     * - Vérifie qu'il n'y a pas de conflit lors de la saisie'
     * 
     * @param array $val Tableau des valeurs brutes.
     * @param null &$dnu1 @deprecated  Ne pas utiliser.
     * @param null $dnu2 @deprecated  Ne pas utiliser.
     *
     * @return void
     */
    function verifier($val = array(), &$dnu1 = null, $dnu2 = null) {
        parent::verifier($val, $dnu1, $dnu2);
        // En cas de conflit de saisie empêche la modification
        if (array_key_exists('ouverture', $val)) {
            if ($this->f->conflit_saisie($this, $val['ouverture'])) {
                $this->addToMessage('Attention: votre modification est en conflit avec la modification précédente!');
                $this->correct = false;
                return;
            }
            // Vérifie qu'il n'y pas de conflit avec la saisie de la participation par unité
            $participationsUnite = $this->get_participations_unite();
            foreach ($participationsUnite as $participationUnite) {
                if ($participationUnite->f->conflit_saisie($participationUnite, $val['ouverture'])) {
                    $this->addToMessage('Attention: votre modification est en conflit avec la modification précédente!');
                    $this->correct = false;
                    return;
                }
            }
        }
    }

    /**
     * TRIGGER - triggermodifier.
     *
     * - Vérifie qu'il n'y a pas de conflit de saisie
     * - Met à jour la participation enregistrée pour chaque unité
     *
     * @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) {
        $succes = $this->maj_participation_unite($val);

        // Si la participation a été saisie et que la mise à jour à réussi
        // l'attribut de validation est mis à jour ainsi que la date de modification
        if ($succes) {
            // Mise à jour de la date de dernière modification
            $this->valF['date_derniere_modif'] = microtime(true);
            // Envoie à l'affichage et au portail web si la saisie est validée
            // cad si la participation a bien été enregistré pour chaque unité
            if ($this->est_saisie()) {
                $this->envoyer_affichage();
                $this->envoyer_web();
            }
        }
    }

    /**
     * Met à jour les nombres de votant dans la table participation_unite
     * en utilisant les nombres de votant enregistrés dans le formulaire.
     *
     * @param array $val tableau contenant les valeurs des champs du formulaire
     * @return boolean
     */
    protected function maj_participation_unite($val) {
        $message = 'Participations enregistrées :';
        $succes = true;
        $unites = $this->get_unites_perimetre();

        foreach ($unites as $uniteElec) {
            // Récupération du nombre de votant
            $votant = $val['unite'.$uniteElec->getVal('unite')];

            // Maj du nombre de votant dans la BD si un nombre a été saisie
            $modification = array(
                "votant" => is_numeric($votant) ? $votant : null,
                "saisie" => is_numeric($votant) ? true : false,
                "date_derniere_modif" => microtime(true)
            );

            $res = $this->f->db->autoExecute(
                DB_PREFIXE."participation_unite",
                $modification,
                DB_AUTOQUERY_UPDATE,
                "election_unite = '".$uniteElec->getVal('election_unite')."'
                AND participation_election = ".$this->getVal('participation_election')
            );
            $this->f->addToLog(__METHOD__ . "() : db->autoExecute(" . $res . ")", VERBOSE_MODE);
            if ($this->f->isDatabaseError($res, true)) {
                $message .= "<br>Erreur : la mise à jour des résultats a échouée";
                $succes = false;
            }

            // Message d'information pour l'utilisateur présentant le nombre de votant
            // enregistré par unité
            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            $message .= "<br>".$unite->getVal('libelle')." : ".$votant;
        }

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

    /**
     * Indique si l'étape en cours est celle de la saisie ou
     * de la simulation
     *
     * @return boolean
     */
    protected function is_saisie_or_simulation() {
        $election = $this->f->get_element_by_id('election', $this->getVal('election'));
        return $election->is_etape_saisie() || $election->is_etape_simulation() ;
    }

    /**
     * Indique si l'étape en cours est celle de la saisie, de la finalisation ou
     * de la simulation
     *
     * @return boolean
     */
    protected function is_finalisation_saisie_or_simulation() {
        $election = $this->f->get_element_by_id('election', $this->getVal('election'));
        return $election->is_etape_saisie() ||
            $election->is_etape_finalisation() ||
            $election->is_etape_simulation();
    }

    /**
     * Indique si la participation enregistrée pour la tranche horaire a été evoyé à l'affichage
     *
     * @return boolean
     */
    public function est_envoye_a_affichage() {
        $participations = $this->get_participations_unite();
        $envoiAff = true;
        foreach ($participations as $participation) {
            $envoiPart = $this->f->boolean_string_to_boolean($participation->getVal('envoi_aff'));
            $envoiAff = $envoiPart && $envoiAff;
        }
        return $envoiAff;
    }

    /**
     * Indique si la participation enregistrée pour la tranche horaire a été evoyé au
     * portail web
     *
     * @return boolean
     */
    public function est_envoye_au_web() {
        $participations = $this->get_participations_unite();
        $envoiWeb = true;
        foreach ($participations as $participation) {
            $envoiPart = $this->f->boolean_string_to_boolean($participation->getVal('envoi_web'));
            $envoiWeb = $envoiPart && $envoiWeb;
        }
        return $envoiWeb;
    }

    /**
     * Indique si la participation enregistrée est saisie
     *
     * @return boolean
     */
    public function est_saisie() {
        $participations = $this->get_participations_unite();
        $saisie = true;
        foreach ($participations as $participation) {
            $saisiPart = $this->f->boolean_string_to_boolean($participation->getVal('saisie'));
            $saisie = $saisiPart && $saisie;
        }
        return $saisie;
    }

    // ====================
    // methodes d'affichage
    // ====================

    /**
     * Ecris les fichiers contenant la participation par unité dans le
     * répertoire de l'affichage.
     * ex : resultat de l'unité 3 de l'élection 1 -> aff/res/1/bpar/b3.json
     *
     * @return boolean
     */
    protected function affichage() {
        // Creation du repertoire de l'élection. Permet d'éviter les problèmes d'écriture
        // de fichier pour l'envoi à l'affichage. En effet, si plusieurs repertoire doivent
        // être crées le fichier n'arrive pas à s'écrire. C'est par exemple le cas pour la
        // participation qui est dans le repertoire bpar du repertoire de l'élection.
        if (! file_exists('../aff/res/'.$this->getVal('election'))) {
            if (! mkdir('../aff/res/'.$this->getVal('election'))) {
                $this->addToMessage('repertoire aff de l\'élection non crée');
            }
        }
        // Récupération du périmètre
        $election = $this->f->get_element_by_id('election', $this->getVal('election'));
        $election->affichage_perimetre($this->getVal('election'));

        // Récupération de la liste des unités et des tranches horaires liées à l'élection
        $participationsElection = $election->get_participation_election($this->getVal('election'));
        $unitesElection = $election->get_unites_election($this->getVal('election'));
        $succes = false;

        // Ecriture et envoie au répertoire web de la participation pour chaque unité.
        // Pour chaque unité seule la partcipation des tranches envoyées est récupérée
        foreach ($unitesElection as $uniteElection) {
            $unite = $this->f->get_element_by_id('unite', $uniteElection->getVal('unite'));
            $contenu = array(
                'election' => $election->getVal('libelle'),
                'unite_libelle' => $unite->getVal('code_unite').' '.$unite->getVal('libelle'),
                'inscrit' => $uniteElection->getVal('inscrit'),
                'votant' => $uniteElection->getVal('votant'),
                'exprime' => $uniteElection->getVal('exprime'),
                'blanc' => $uniteElection->getVal('blanc'),
                'nul' => $uniteElection->getVal('nul'),
                'participations' => array()
            );

            foreach ($participationsElection as $participationElection) {
                if ($participationElection->est_envoye_a_affichage()) {
                    $tranche = $this->f->get_element_by_id('tranche', $participationElection->getVal('tranche'));
                    $votant = $this->get_votant_par_unite_participation(
                        $participationElection->getVal('participation_election'),
                        $uniteElection->getVal('election_unite')
                    );

                    $tx = ! empty($uniteElection->getVal('inscrit')) && is_numeric($votant)?
                        number_format(round($votant * 100 / $uniteElection->getVal('inscrit'), 2), 2)."%"
                        : '0%';
                    $contenu['participations'][$tranche->getVal('libelle')] = array(
                        'heure' => $tranche->getVal('libelle'),
                        'votant' => $votant,
                        'tx' => $tx
                    );
                }
            }

            $contenu = json_encode($contenu);
            $emplacementFichier = "../aff/res/"
                .$this->getVal('election')
                ."/bpar/b".$uniteElection->getVal('unite')
                .".json";
            $succes = $this->f->write_file($emplacementFichier, $contenu);
            if (! $succes) {
                break;
            }
        }

        $message = $succes ? 'La participation a été mise à jour sur les animations'
            : 'Erreur lors de l\'envoi à l\'affichage';
        $this->addToMessage($message);
        return $succes;
    }

    /**
     * Ecris le fichier contenant les informations relatives à la participation
     * dans le répertoire web (participation.inc).
     *
     * @return boolean
     */
    protected function web() {
        // Récupération du nombre d'inscrit de l'élection qui correspond au nombre d'inscrit
        // du périmètre de l'élection
        $electionUnite = $this->f->get_element_by_id('election_unite', ']');
        $election = $this->f->get_element_by_id('election', $this->getVal('election'));
        $path = $election->get_election_web_path();
        $perimetreElection = $electionUnite->get_instance_election_unite(
            $election->getVal('perimetre'),
            $election->getVal('election')
        );
        $inscrits = $perimetreElection->getVal('inscrit');
        
        // Récupération du contenu du fichier participation.inc
        $participationsElection = $election->get_participation_election($this->getVal('election'));
        $indexParticipation = 0;
        $tableauParticipation = '';
        foreach ($participationsElection as $partElection) {
            // Si la participation a été envoyé alors les résultats sont récupérés et ajoutés
            // au fichier web de la participation
            if ($partElection->est_envoye_au_web()) {
                $tranche = $this->f->get_element_by_id('tranche', $partElection->getVal('tranche'));
                $votants = $partElection->get_participation_total_tranche();
                $votants = is_numeric($votants) ? $votants : 0;
                $tx = ! empty($inscrits) ?
                    number_format(round($votants * 100 / $inscrits, 2), 2)."%"
                    : '0%';
    
                $tableauParticipation .= sprintf(
                    '$participations[%d] = array(
                        "heure" => "%s",
                        "votants" => "%d",
                        "tx" => "%s");
                    ',
                    $indexParticipation++,
                    $tranche->getVal('libelle'),
                    $votants,
                    $tx
                );
            }
        }

        // Ecriture du fichier participation.inc
        $contenu = sprintf(
            '<?php
                %s
            ?>',
            $tableauParticipation
        );

        $succes = $this->f->write_file($path."/participation.inc", $contenu);

        $message = $succes ? 'La participation a été mise à jour sur la page web' :
            'Erreur : la participation n\'a pas été envoyée';
        $this->addToMessage($message);
        return $succes;
    }

    // ==================================
    // methode de recuperation de donnees
    // ==================================

    /**
     * Récupère tous les id de la table participation_unite lié à la participation_election
     * à l'aide d'une requête.
     * Pour chaque id recupère l'instance correspondante et la stocke dans un tableau.
     * Renvoie le tableau contenant toutes les instances de la participation par unité
     *
     * @return array
     */
    protected function get_participations_unite() {
        $participations = array();

        $sql = sprintf(
            'SELECT
                participation_unite AS id
            FROM
                '.DB_PREFIXE.'participation_unite
            WHERE
                participation_election = %d',
            $this->getVal('participation_election')
        );
        $res = $this->f->db->query($sql);
        // En cas de problème sur la requête un tableau vide est renvoyé
        if ($this->f->is_database_error(
            $sql,
            $res,
            'La participation de l\'unité n\'a pas été récupérée'
        )) {
            return $participations;
        }
        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $participation = $this->f->get_element_by_id('participation_unite', $row['id']);
            $participations[$row['id']] = $participation;
        }
        return $participations;
    }

    /**
     * Récupère un identifiant de l'élection, vérifie que son
     * type correspond bien au type attendu et que cette identifiant
     * existe dans la base de données.
     *
     * @param string $election identifiant de l'élection
     *
     * @return boolean
     */
    protected function validateur_election($election) {
        //Vérification du type et de la taille
        if (! $this->f->validateur_entier_positif($election)) {
            return false;
        }
        // Requête pour chercher l'identifiant dans la base de données
        //si rien n'est trouvé, c'est qu'il n'existe pas et donc qu'il n'est
        //pas valide
        $sql = sprintf(
            'SELECT
                election
            FROM
                '.DB_PREFIXE.'election
            WHERE
                election = %d',
            $election
        );
        $res = $this->f->db->getOne($sql);
        if (empty($res)) {
            return false;
        }
        return true;
    }

    /**
     * Compose et encode un tableau json contenant les liens entre les unités de l'élection.
     * Ex :
     *      $unite[indice_unite_parent] = array (indice_enfant_1, ..., indice_enfant_n)
     *
     * /!\ : cette méthode ne prend pas de paramètre mais nécessite la récupération de
     * l'identifiant de l'élection. Cet identifiant est récupéré depuis l'URL
     *
     * @return void
     */
    public function get_lien_perimetre_enfants() {
        // Il est nécessaire de désactiver les logs car si les modes VERBOSE, EXTRA VERBOSE, etc
        // sont activé, les logs sont affichés en HTML ce qui casse le format json attendu en sortie
        $this->f->disableLog();

        //Pour pouvoir récupérer la liste des unités, il faut l'identifiant de l'élection
        //qui se trouve dans le get
        $idElection = "";
        if (! empty($this->f->get_submitted_get_value('election'))) {
            $idElection = $this->f->get_submitted_get_value('election');
        }

        // Si on ne récupère pas l'unité parente alors on ne peut pas connaitre
        // les unités de l'élection, on renvoi donc un tableau vide
        if (empty($idElection) || ! $this->validateur_election($idElection)) {
            // code retour 404 car la valeur de l'election est invalide donc on ne peut
            // trouver aucune objet avec
            header('HTTP/1.1 404 Not Found');
            return array();
        }

        //Récupère la liste des unites et indique si ce sont des périmètres ou pas
        $election = $this->f->get_element_by_id('election', $idElection);
        $unites = $election->get_unites_election($idElection);

        // Tri des unités par hierarchie. Le périmètre ayant la hiérarchie la plus importante
        // est le périmètre de l'élection.
        // /!\ Ce tri est nécessaire et ne doit pas être retiré. Si le tri n'est pas effectué
        //     il est possible que la valeur soit calculé pour un périmètre avant que les
        //     périmètres qu'il contiens ne soient mis à jour. Cette valeur étant calculé à
        //     partir des unités qu'il contiens le résultat sera donc erroné
        $unitesHierarchise = array();
        foreach ($unites as $uniteElec) {
            $unite = $this->f->get_element_by_id('unite', $uniteElec->getVal('unite'));
            $typeUnite = $this->f->get_element_by_id('type_unite', $unite->getVal('type_unite'));
            $unitesHierarchise[] = array(
                'hierarchie' => $typeUnite->getVal('hierarchie'),
                'unite' => $uniteElec->getVal('unite')
            );
        }

        sort($unitesHierarchise);

        //Pour chaque unité de la liste, on récupère la liste de ses enfants et on
        //l'ajoute dans un tableau composé de cette manière :
        //  $unite[indice_unite_parent] = array (indice_enfant_1, ..., indice_enfant_n)
        //Cela permet d'obtenir la structure de l'élection
        //et donc de connaître les dépendances entre les champs de saisie des unités.
        $indiceUniteLiee = array();
        $index = 0;
        $sql = 'SELECT
                    unite_enfant
                FROM
                    '.DB_PREFIXE.'lien_unite
                WHERE
                    unite_parent = %d';

        foreach ($unitesHierarchise as $uniteHierarchise) {
            $indiceUniteLiee[$index] = array(
                'parent' => $uniteHierarchise['unite'],
                'enfants' => array()
            );
            $unite = $this->f->get_element_by_id('unite', $uniteHierarchise['unite']);
            $isPerimetre = $this->f->boolean_string_to_boolean($unite->getVal('perimetre'));

            if ($isPerimetre) {
                $query = sprintf($sql, $uniteHierarchise['unite']);
                $res = $this->f->db->query($query);
                if ($this->f->is_database_error($query, $res, 'Erreur lors de la récupération des unités')) {
                    return array();
                }
                while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
                    $indiceUniteLiee[$index]['enfants'][] = $row['unite_enfant'];
                }
            }
            $index++;
        }

        //Encode cette liste
        echo json_encode($indiceUniteLiee);
    }

    // ===============================================================================
    // actions d'envoi et de suppression des fichiers de l'affichage et du portail web 
    // ===============================================================================

    /**
     * Met à jour les attributs d'envoi à la page web et la participation
     * dans le repertoire web
     */
    protected function envoyer_web() {
        // Maj des attributs d'envoi à la page web
        $participations = $this->get_participations_unite();
        $succes = true;
        foreach ($participations as $participation) {
            $modif = $this->f->modifier_element_BD_by_id(
                'participation_unite',
                $participation->getVal('participation_unite'),
                array('envoi_web' => true)
            );
            $succes = $succes && $modif;
        }
        if (! $succes) {
            $this->addToMessage('L\'attribut d\'envoi au portail web n\'a pas été mis à jour');
        }

        // Met à jour le fichier contenant la participation dans le répertoire web
        $succes = $succes && $this->web();
        return $succes;
    }

    /**
     * Met à jour les attributs d'envoi à l'affichage et la participation
     * dans le repertoire aff
     */
    protected function envoyer_affichage() {
        // Maj des attributs d'envoi à la page web
        $participations = $this->get_participations_unite();
        $succes = true;
        foreach ($participations as $participation) {
            $modif = $this->f->modifier_element_BD_by_id(
                'participation_unite',
                $participation->getVal('participation_unite'),
                array('envoi_aff' => true)
            );
            $succes = $succes && $modif;
        }
        if (! $succes) {
            $this->addToMessage('Les attributs d\'envoi à l\'affichage n\'ont pas été mis à jour');
        }

        // Met à jour le fichier contenant la participation dans le répertoire web
        $succes = $succes && $this->affichage();
        return $succes;
    }

    /**
     * Met à jour les attributs d'envoi à la page web et la participation
     * dans le repertoire web
     */
    protected function supprimer_web() {
        // Maj des attributs d'envoi à la page web
        $participations = $this->get_participations_unite();
        $succes = true;
        foreach ($participations as $participation) {
            $modif = $this->f->modifier_element_BD_by_id(
                'participation_unite',
                $participation->getVal('participation_unite'),
                array('envoi_web' => false)
            );
            $succes = $succes && $modif;
        }
        if (! $succes) {
            $this->addToMessage('L\'attribut d\'envoi au portail web n\'a pas été mis à jour');
        }

        // Met à jour le fichier contenant la participation dans le répertoire web
        $succes = $succes && $this->web();
        return $succes;
    }

    /**
     * Met à jour les attributs d'envoi à l'affichage et la participation
     * dans le repertoire aff
     */
    protected function supprimer_affichage() {
        // Maj des attributs d'envoi à la page web
        $participations = $this->get_participations_unite();
        $succes = true;
        foreach ($participations as $participation) {
            $modif = $this->f->modifier_element_BD_by_id(
                'participation_unite',
                $participation->getVal('participation_unite'),
                array('envoi_aff' => false)
            );
            $succes = $succes && $modif;
        }
        if (! $succes) {
            $this->addToMessage('Les attributs d\'envoi à l\'affichage n\'ont pas été mis à jour');
        }

        // Met à jour le fichier contenant la participation dans le répertoire web
        $succes = $succes && $this->affichage();
        return $succes;
    }

    /**
     * Réinitialise les resultats de la participation. Supprime les résultats sur
     * l'animation et la page web. Remet les indicateurs d'envoi à l'affichage et à la page web
     * à faux.
     *
     * @return void
     */
    public function annuler_resultat() {
        $participation_election = $this->getval('participation_election');
        // Reinitialisation du nomre de votant
        $modif = array(
            'votant' => null,
            'envoi_aff' => false,
            'envoi_web' => false,
            'saisie' => false
        );
        $modification = $this->f->db->autoExecute(
            DB_PREFIXE."participation_unite",
            $modif,
            DB_AUTOQUERY_UPDATE,
            "participation_unite.participation_election = ".$participation_election
        );
        $this->addToLog(__METHOD__ . "() : db->autoExecute(" . $modification . ")", VERBOSE_MODE);
        if ($this->f->isDatabaseError($modification, true)) {
            $this->addToMessage('Erreur lors de la modification');
            $this->db->rollback();
            return false;
        }
        $this->db->commit();
        
        // Mise à jour des informations envoyées à l'affichage et au portail web
        $this->supprimer_affichage();
        $this->supprimer_web();
        return true;
    }

    /**
     * Récupére et renvoie le nombre de votant enregistré pour l'horaire
     * (participation_election) et l'unité (election_unite) voulu
     *
     * @param integer $idPartElection identifiant du lien participation_election
     * @param integer $idElectionUnite identifiant de l'unité de l'élection
     * @return integer nombre de votant
     */
    public function get_votant_par_unite_participation($idPartElection, $idElectionUnite) {
        $sql = sprintf(
            'SELECT
                votant
            FROM
                %sparticipation_unite
            WHERE
                participation_election = %d AND
                election_unite = %d',
            DB_PREFIXE,
            $this->f->db->escapeSimple($idPartElection),
            $this->f->db->escapeSimple($idElectionUnite)
        );
        $votant = $this->f->db->getOne($sql);
        if ($this->f->is_database_error($sql, $votant, 'Erreur de récupération du nombre de votant')) {
            $votant = 0;
        }
        return $votant;
    }

    /**
     * Requête sql permettant de récupérer le nombre total de votants enregistrés
     * pour une tranche horaire
     *
     * @return integer
     */
    public function get_participation_total_tranche() {
        $sql = sprintf(
            'SELECT
                votant
            FROM
                %sresultat_participation_tranche
            WHERE
                participation_election = %d',
            DB_PREFIXE,
            $this->getVal('participation_election')
        );
        $votants = $this->db->getOne($sql);

        $this->addToLog(__METHOD__."(): db->getOne(\"".$sql."\");", VERBOSE_MODE);
        if ($this->f->isDatabaseError($votants, true)) {
            $this->addToLog(__METHOD__." database error:".$votants->getDebugInfo().";", DEBUG_MODE);
            $this->addToMessage('Erreur : le nombre de votant n\'a pas pu être récupéré');
            $votants = 0;
        }

        return $votants;
    }
}
