import {
    creerTravee,
    creerOuverture,
    supprimerToutesOuvertures,
    traitementCreationTravee,
    traitementCreationOuverture,
    selectionnerSolivage,
    recreerTrappes
} from "./objects.js"

import {
    createText,
    glassMaterial,
    doorMaterial,
    garageDoorMaterial,
    pignonMaterial,
    wallMaterial,
    roofMaterial,
    MPL_Material,
    MPE_Material,
    MF1d_Material,
    MF1g_Material,
    MF2_Material,
    MPEF_Material,
    MPF_Material,
    MPI_Material,
    MPG1_Material,
    MPG2_Material,
    PEXT_Material,
    CH1T_Material,
    COLOR_ARRAY,
    blankMaterial,
    groundMaterial
}
from "./materials.js"

import {
    URL_JSREPORT,
    info,
    alerte,
    log,
    retirerObjetModifiable,
    extraireFace,
    extraireNomTravee,
    verifierContraintes,
    showMainIncrustations,
    hideMainIncrustations,
    showPignonIncrustations,
    hidePignonIncrustations,
    incrusterCotes,
    animate,
    recalculerConstructions,
    calculerTaillePoliceOptimale,
    redimensionnerIncrustations,
    changerCouleurTextes,
    verifierControlesMetier,
    modifierIncrustation,
    restaurerPrefsUtilisateur,
    importProjet,
    exportProjet,
    initObjetsSysteme,
    updateClipboard
} from "./main.js"

import {
    camera,
    cameraOrtho,
    canvas,
    renderer,
    aspectRatio,
    initPositionCamera
} from "./environment.js"


export function unSelect() {

    /* On masque le menu déroulant...
       on déselectionne tous les objets à l'IHM,
       puis on vide les variables  */

    $("#div-menu-contextuel").hide();

    if (objetSelectionne) {
        var objetTouche = scene.getObjectByName(objetSelectionne);

        for (var i = 0; i < facesSelectionnees.length; i++) {

            // Suivant l'objet touché...
            if (!objetTouche.parent.name.includes('>')) { // Un mur            
                objetTouche.geometry.faces[facesSelectionnees[i]].color.set(COLOR_ARRAY['blanc']);
            } else {

                if (objetTouche.name.includes('Portique'))
                    objetTouche.geometry.faces[facesSelectionnees[i]].color.set(COLOR_ARRAY['blanc']);

                if (objetTouche.name.includes('Vitre'))
                    objetTouche.material = glassMaterial;

                if (objetTouche.name.includes('Porte')) {
                    if (objetTouche.name.includes('PG'))
                        objetTouche.material = garageDoorMaterial;
                    else
                        objetTouche.material = doorMaterial;
                }

                if (objetTouche.name.includes('PINT')) { // Pignon intérieur
                    for (var k = 0; k < 12; k++) {
                        objetTouche.geometry.faces[k].color.set(COLOR_ARRAY['blanc']);
                    }
                }
            }
        }
        objetTouche.geometry.elementsNeedUpdate = true;
    }
    facesSelectionnees.length = 0;
    objetSelectionne = '';

    if ($("#message-info").prop("class") == "normal")
        info(null);
}



export function changePointOfView(direction, modePDF = false) {

    // On calcule les contours de notre construction
    var left = tableauTravees['Travee 1'].positionX - (LARGEUR_TRAVEE / 2);
    var right = tableauTravees['Travee ' + nbTravees].positionX + (LARGEUR_TRAVEE / 2);
    var front = LONGUEUR_TRAVEE / 2;
    var back = -(LONGUEUR_TRAVEE / 2);

    var decalageGauche = tableauTravees['Travee 1'].decalage * (LONGUEUR_TRAVEE / 2);
    var decalageDroite = tableauTravees['Travee ' + nbTravees].decalage * (LONGUEUR_TRAVEE / 2);
    if (decalageGauche < 0 || decalageDroite < 0)
        back = -LONGUEUR_TRAVEE;

    if (decalageGauche > 0 || decalageDroite > 0)
        front = LONGUEUR_TRAVEE;

    if (modePDF)
        activeCamera = cameraOrtho;
    else
        activeCamera = camera;

    switch (direction) {
        case 'dessus':
            camera.position.set(0, 200, 0);
            camera.lookAt(scene.position);
            info("Vue de dessus");
            break;
        case 'avant':
            if (modePDF) {
                cameraOrtho.position.set(0, HAUTEUR_TRAVEE / 2, 80);
                cameraOrtho.lookAt(0, HAUTEUR_TRAVEE / 2, 0);

                cameraOrtho.left = tableauTravees['Travee 1'].positionX - 40;
                cameraOrtho.right = tableauTravees['Travee ' + nbTravees].positionX + 40;
                cameraOrtho.top = (((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.bottom = -(((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.far = 100;
                cameraOrtho.near = -100;
            } else {
                camera.position.set(0, 10, 100 + front + (Math.log(nbTravees) * 50));
                camera.lookAt(scene.position);
            }
            info("Vue avant");
            break;
        case 'arriere':
            if (modePDF) {
                cameraOrtho.position.set(0, HAUTEUR_TRAVEE / 2, -80);
                cameraOrtho.lookAt(0, HAUTEUR_TRAVEE / 2, 0);

                cameraOrtho.left = tableauTravees['Travee 1'].positionX - 40;
                cameraOrtho.right = tableauTravees['Travee ' + nbTravees].positionX + 40;
                cameraOrtho.top = (((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.bottom = -(((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.far = 100;
                cameraOrtho.near = -100;
            } else {
                camera.position.set(0, 10, -100 + back - (Math.log(nbTravees) * 50));
                camera.lookAt(scene.position);
            }
            info("Vue arrière");
            break;
        case 'gauche':
            if (modePDF) {
                var posLeft = tableauTravees['Travee 1'].positionX - (LARGEUR_TRAVEE / 2);
                cameraOrtho.position.set(posLeft - 50, HAUTEUR_TRAVEE / 2, (decalageGauche + decalageDroite) / 2);
                cameraOrtho.lookAt(0, HAUTEUR_TRAVEE / 2, (decalageGauche + decalageDroite) / 2);

                cameraOrtho.left = -80;
                cameraOrtho.right = 80;
                cameraOrtho.top = (((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.bottom = -(((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.near = tableauTravees['Travee 1'].positionX - 20;
                cameraOrtho.far = tableauTravees['Travee ' + nbTravees].positionX + 20;

                if (DEBUG) {
                    log("cameraOrtho.left=" + cameraOrtho.left + " / cameraOrtho.right=" + cameraOrtho.right + " / cameraOrtho.near=" + cameraOrtho.near + " / cameraOrtho.far=" + cameraOrtho.far);
                    log("decalageGauche=" + decalageGauche + " / decalageDroite=" + decalageDroite);
                    log("cameraPosition=");
                    log(cameraOrtho.position);
                }
            } else {
                var z;
                if (decalageDroite == 0)
                    z = -decalageGauche * 2;
                else
                    z = decalageDroite * 2;

                camera.position.set(-100 + left, 10, z);
                camera.lookAt(scene.position);
            }
            info("Vue de gauche");
            break;
        case 'droite':
            if (modePDF) {
                var posRight = tableauTravees['Travee ' + nbTravees].positionX + (LARGEUR_TRAVEE / 2);
                cameraOrtho.position.set(posRight + 50, HAUTEUR_TRAVEE / 2, (decalageGauche + decalageDroite) / 2);
                cameraOrtho.lookAt(0, HAUTEUR_TRAVEE / 2, (decalageGauche + decalageDroite) / 2);

                cameraOrtho.left = -80;
                cameraOrtho.right = 80;
                cameraOrtho.top = (((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.bottom = -(((cameraOrtho.right - cameraOrtho.left) / aspectRatio) / 2) + 10;
                cameraOrtho.near = tableauTravees['Travee 1'].positionX - 20;
                cameraOrtho.far = tableauTravees['Travee ' + nbTravees].positionX + 20;

                if (DEBUG) {
                    log("cameraOrtho.left=" + cameraOrtho.left + " / cameraOrtho.right=" + cameraOrtho.right + " / cameraOrtho.near=" + cameraOrtho.near + " / cameraOrtho.far=" + cameraOrtho.far);
                    log("decalageGauche=" + decalageGauche + " / decalageDroite=" + decalageDroite);
                    log("cameraPosition=");
                    log(cameraOrtho.position);
                }
            } else {
                var z;
                if (decalageGauche == 0)
                    z = -decalageDroite * 2;
                else
                    z = decalageGauche * 2;

                camera.position.set(100 + right, 10, z);
                camera.lookAt(0, 0, -z);
            }
            info("Vue de droite");
            break;
        case 'perspective':
            camera.position.set(60, 40, 160);
            camera.lookAt(scene.position);
            break;
    }
}


export function afficherVueAerienne(modePDF = false) {

    $("div:contains('Close Controls')").click();
    $("#changement-vue div#aerien").addClass('actif');

    // On masque toit et plancher (si pas déjà masqués)
    if ($("span:contains('afficherToit')").parent().find("input[type='checkbox']").prop('checked'))
        $("span:contains('afficherToit')").click();

    if ($("span:contains('afficherPlancher')").parent().find("input[type='checkbox']").prop('checked'))
        $("span:contains('afficherPlancher')").click();

    initPositionCamera(cameraOrtho);

    // On calcule les limites gauche, droite, haut et bas de la construction, afin de cadrer au mieux.
    var MARGE_GAUCHE = 20;
    var MARGE_DROITE = 50;
    var MARGE_HAUT = 5;
    var MARGE_BAS = 30;

    var gauche = tableauTravees['Travee 1'].positionX - (LARGEUR_TRAVEE / 2) - MARGE_GAUCHE; // Bord gauche de la construction + marge
    var droite = tableauTravees['Travee ' + nbTravees].positionX + (LARGEUR_TRAVEE / 2) + MARGE_DROITE; // Bord droit + marge
    var largeur = droite - gauche;
    var hauteurCalculee = largeur / aspectRatio;

    var maxTop = 0;
    var minBottom = 0;
    for (var laTravee in tableauTravees) {
        if (tableauTravees[laTravee].positionZ < maxTop)
            maxTop = tableauTravees[laTravee].positionZ;

        if (tableauTravees[laTravee].positionZ >= minBottom)
            minBottom = tableauTravees[laTravee].positionZ;
    }
    maxTop -= (LONGUEUR_TRAVEE / 2);
    maxTop = -maxTop + MARGE_HAUT;
    minBottom += (LONGUEUR_TRAVEE / 2);
    minBottom = -minBottom - MARGE_BAS;
    var hauteur = maxTop - minBottom;
    var largeurCalculee = hauteur * aspectRatio;

    if (hauteurCalculee >= hauteur) {
        cameraOrtho.left = gauche;
        cameraOrtho.right = droite;
        cameraOrtho.top = maxTop + ((hauteurCalculee - hauteur) / 2);
        cameraOrtho.bottom = minBottom - ((hauteurCalculee - hauteur) / 2);
    } else {
        cameraOrtho.top = maxTop;
        cameraOrtho.bottom = minBottom;
        cameraOrtho.left = gauche - ((largeurCalculee - largeur) / 2);
        cameraOrtho.right = droite + ((largeurCalculee - largeur) / 2);
    }


    if (DEBUG) {
        log("left/right : " + gauche + " / " + droite + " soit une largeur de " + largeur + " et une hauteur calculée de " + hauteurCalculee);
        log("top/bottom : " + maxTop + " / " + minBottom + " soit une hauteur de " + hauteur + " et une largeur calculée de " + largeurCalculee);
    }

    activeCamera = cameraOrtho;

    $("#changement-vue div#aerien").addClass('surligne');
    showMainIncrustations();
    hidePignonIncrustations();

    // On adapte la taille des incrustations au niveau de zoom : pour ça, pas d'autre choix
    // que de supprimer/recréer les incrustations.
    if (!modePDF)
        redimensionnerIncrustations();

    $("#legende").hide();
    $("#vue-aerienne").show();
    $("#transparent-overlay").show();
}

/*******************************    Gestion du menu contextuel    ***********************************************/

function addInfo(message) {
    var liText = "<li class=\"blank\">" + message + "</li>";

    $('.liste-deroulante').append(liText);
}

function addMenu(menuTitle, action, isActive = true) {

    var liText = liText = "<li data-action=\"" + action + "\"";

    if (action.match('moveFront')) liText += " class=\"moveFront";
    if (action.match('moveBack')) liText += " class=\"moveBack";
    if (action.match('add')) liText += " class=\"add";
    if (action.match('delete')) liText += " class=\"delete";
    if (action.match('choose')) liText += " class=\"choose";
    if (!isActive) liText += " disabled";
    liText += "\">" + menuTitle + "</li>";

    $('.liste-deroulante').append(liText);
}

function addSeparator() {
    var liText = "<hr>";
    $('.liste-deroulante').append(liText);
}


export function chooseFloorHole(traveeSelectionnee) {
    $('.popup-ouverture-image').each(function (i) {
        $(this).parent().removeClass();
        $(this).parent().addClass("normal");
    });

    $("#popup-plancher").show();
    $("#traveeSelectionnee").val(traveeSelectionnee);
    $("#overlay").show();
}


export function displayPignonOpenings(traveeSelectionnee) {
    // RAZ des figures
    $('.popup-ouverture-image').each(function (i) {
        $(this).parent().removeClass();
        $(this).parent().addClass("normal");
    });

    $("#popup-pignon").show();
    $("#traveeSelectionnee").val(traveeSelectionnee);
    $("#overlay").show();
}


export function displayOpenings(traveeSelectionnee, face) {
    // RAZ des figures
    $('.popup-ouverture-image').each(function (i) {
        $(this).parent().removeClass();
        $(this).parent().addClass("normal");
    });
    disableUnauthorizedOpenings(verifierContraintes(traveeSelectionnee));

    if (face == 'interieur')
        $("#popup-ouverture-in").show();
    else
        $("#popup-ouverture-out").show();

    $("#traveeSelectionnee").val(traveeSelectionnee);
    $("#overlay").show();
}

export function disableUnauthorizedOpenings(tableauOuverturesAutorisees) {

    $('.popup-ouverture-image').each(function (i) {

        if (!tableauOuverturesAutorisees.includes($(this).parent().attr('id'))) {
            $(this).parent().toggleClass("disabled");
        }
    });
}

export function displayContextualMenu(objet, x, y) {

    if (facesSelectionnees.length > 1) return;

    /*   -> ex : "Travee 1>AV"                       => décaler la travée + rajouter ouverture (si permis)
         -> ex : "Travee 1>PDAV"                     => seulement rajouter une ouverture
         -> ex : "Travee 1>AV>Ouverture F2>Vitre"    => seulement supprimer l'ouverture                */
    $('.liste-deroulante').empty();

    addInfo($("#message-info").html());
    addSeparator();
    if (objet.name.includes('Ouverture'))
        addMenu("Supprimer cette ouverture", 'deleteOuverture');
    else {
        if (objet.name.includes('>AV') || objet.name.includes('>AR')) {

            var decalageActuel = tableauTravees[objet.parent.name]['decalage'];
            if (decalageActuel < 0) {
                addMenu("Reculer cette travée", 'moveBackTravee', false);
                addMenu("Avancer cette travée", 'moveFrontTravee');
            } else {
                if (decalageActuel > 0) {
                    addMenu("Reculer cette travée", 'moveBackTravee');
                    addMenu("Avancer cette travée", 'moveFrontTravee', false);

                } else {
                    addMenu("Reculer cette travée", 'moveBackTravee');
                    addMenu("Avancer cette travée", 'moveFrontTravee');
                }
            }
            addSeparator();
        }
        if (objet.name.includes('plancher')) {
            addMenu("Choisir le solivage", 'chooseFloorHole');
        } else {

            if (objet.name.includes('PINT')) {
                addMenu("Choisir l'ouverture", 'addPignonOpening');
            } else {
                var nomFace = extraireFace(objet.name);
                // S'il existe déjà une ouverture sur ce module, on grise la possibilité d'en ajouter une autre.
                if (tableauTravees[extraireNomTravee(objet.name)]['nb_ouvertures_' + nomFace] > 0)
                    addMenu("Créer une ouverture", 'addOpening', false);
                else
                    addMenu("Créer une ouverture", 'addOpening');
            }
        }
    }
    addSeparator();
    addMenu("Annuler la sélection", 'unselect');


    // Suivant la position du curseur, on place le menu à gauche ou à droite de cette dernière.
    var left = (x >= (window.innerWidth / 2)) ? (x + 30) + 'px' : (x - $("#div-menu-contextuel").width() - 30) + 'px';
    $("#div-menu-contextuel").css({
        left: left,
        top: y - ($("#div-menu-contextuel").height() / 2) + 'px'
    });
    $("#div-menu-contextuel").show();
}



function saveAsImage() {
    var imgData, imgNode;
    var strDownloadMime = "image/octet-stream";

    try {
        var strMime = "image/jpeg";
        imgData = renderer.domElement.toDataURL(strMime);

        return imgData.replace(strMime, strDownloadMime);
    } catch (e) {
        console.log(e);
        return;
    }
}

var saveFile = function (strData, filename) {
    var link = document.createElement('a');
    if (typeof link.download === 'string') {
        document.body.appendChild(link); //Firefox requires the link to be in the body
        link.download = filename;
        link.href = strData;
        link.click();
        document.body.removeChild(link); //remove the link when done
    } else {
        location.replace(uri);
    }
}


export function displayGui() {

    var controller = new function () {
        this.afficherToit = true;
        this.afficherPlancher = true;
        this.afficherCotes = true;
        this.ossatureBois = false;


        this.sauvegarderCeProjet = function () {

            scene.updateMatrixWorld();
            var reference = exportProjet();

            var texteFinal = $("#popup-export .texte").html().replace('%s', reference);
            $("#popup-export span.texte").html(texteFinal);
            $("#popup-export").show();
            $("#overlay").show();

            navigator.permissions.query({
                name: "clipboard-write"
            }).then(result => {
                if (result.state == "granted" || result.state == "prompt") {
                    /* write to the clipboard now */
                    updateClipboard(reference);
                }
            });
        };


        this.importerUnProjet = function () {

            var refDevis = prompt("Veuillez saisir la référence de votre projet :", "test");
            if (refDevis === null) return;

            for (var i = 0; i < nbTravees; i++) {
                scene.remove(scene.getObjectByName(PREFIXE_TRAVEE + parseInt(i + 1)));
            }
            nbConstructions = nbTravees = nbOuvertures = 0;

            // Il faut vider les tableaux utilisés par le système.
            initObjetsSysteme();

            importProjet(refDevis);
            incrusterCotes();
            scene.updateMatrixWorld();
        };


        this.genererDemandePrix = function () {

            if (!verifierControlesMetier()) return;

            // Tout d'abord, on enregistre le projet en sous-marin, afin d'inclure sa référence dans le devis.
            scene.updateMatrixWorld();
            var reference = exportProjet();


            var modePDF = true;
            var screenshot1, screenshot2, screenshot3, screenshot4, screenshot5, screenshot6, screenshot7, screenshot8, screenshot9;

            var coordonneesClient = prompt("Veuillez indiquer le nom et prénom à faire figurer dans la demande de prix SVP.", "Client sans nom");
            if (coordonneesClient === null) return;
            var maintenant = new Date();

            $("#overlay").show();
            $("#popup-attente").show();

            // On prend tous les screenshots dans le bon ordre et dans le bon mode...
            if (!$("span:contains('ossatureBois')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('ossatureBois')").click();
            if (!$("span:contains('afficherCotes')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherCotes')").click();

            // 1 - la vue d'implantation
            activeCamera = cameraOrtho;
            scene.getObjectByName('ground').material = blankMaterial;
            scene.getObjectByName('boussole').material.color = COLOR_ARRAY['noir'];
            changerCouleurTextes(COLOR_ARRAY['noir']);
            scene.traverse(function (child) {
                if (child.name.includes('floor_excluded'))
                    child.visible = false;
            });
            afficherVueAerienne(modePDF);
            showMainIncrustations();
            hidePignonIncrustations();
            animate();
            screenshot1 = saveAsImage();
            $("#quitter-vue-aerienne").click();

            activeCamera = cameraOrtho;

            // 2 - une vue aérienne du plancher
            showPignonIncrustations();
            changePointOfView("dessus", modePDF);
            if ($("span:contains('afficherToit')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherToit')").click();
            animate();
            screenshot2 = saveAsImage();
            $("span:contains('afficherToit')").click();

            // 3 - une vue aérienne avec charpente
            hidePignonIncrustations();
            if ($("span:contains('afficherPlancher')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherPlancher')").click();
            animate();
            screenshot3 = saveAsImage();

            // Les vues suivantes n'auront pas de cotes.
            scene.getObjectByName('ground').material = groundMaterial;
            scene.traverse(function (child) {
                if (child.name.includes('floor_excluded'))
                    child.visible = true;
            });
            scene.getObjectByName('boussole').material.color = COLOR_ARRAY['blanc'];
            changerCouleurTextes(COLOR_ARRAY['blanc']);
            if ($("span:contains('afficherCotes')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherCotes')").click();

            // 4 - une vue de face
            changePointOfView("avant", modePDF);
            animate();
            screenshot4 = saveAsImage();

            // 5 - une vue de derrière
            changePointOfView("arriere", modePDF);
            animate();
            screenshot5 = saveAsImage();

            // 6 - une vue de gauche
            changePointOfView("gauche", modePDF);
            animate();
            screenshot6 = saveAsImage();

            // 7 - une vue de droite
            changePointOfView("droite", modePDF);
            animate();
            screenshot7 = saveAsImage();

            initPositionCamera(cameraOrtho);
            activeCamera = camera;

            // Enfin, quelques vues en perspective
            if ($("span:contains('afficherToit')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherToit')").click();
            camera.position.set(180, 100, 150);
            camera.lookAt(scene.position);
            animate();
            screenshot8 = saveAsImage();

            camera.position.set(-180, 100, -100);
            camera.lookAt(scene.position);
            animate();
            screenshot9 = saveAsImage();


            // Retour à la vue perspective
            changePointOfView("perspective");

            if ($("span:contains('ossatureBois')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('ossatureBois')").click();
            if (!$("span:contains('afficherToit')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherToit')").click();
            if (!$("span:contains('afficherPlancher')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('afficherPlancher')").click();
            animate();


            // Génération du rapport PDF
            jsreport.serverUrl = URL_JSREPORT;

            var donneesBrutes = {},
                modules = new Array(),
                donneesJSON;
            donneesBrutes.nomClient = coordonneesClient;
            donneesBrutes.dateDevis = maintenant.toLocaleDateString('fr-FR');
            donneesBrutes.referenceDevis = maintenant.getFullYear() + '' +
                ((maintenant.getMonth() + 1) < 10 ? '0' + (maintenant.getMonth() + 1) : (maintenant.getMonth() + 1)) + '' +
                maintenant.getDate() + '' + maintenant.getHours() + '' + maintenant.getMinutes();
            donneesBrutes.referenceProjet = reference;


            // C'est ici qu'on calcule le nombre de charpentes et de pignons : c'est plus simple.
            // On rajoute également dans l'inventaire les kits accessoires, correspondants à chaque construction.
            inventaire["CH1T"] = inventaire["CHTS"] = inventaire["PEXT"] = inventaire["PINT"] = 0;
            inventaire["ACC1"] = inventaire["ACC2"] = inventaire["ACC3"] = inventaire["ACC4"] = 0;
            var nbTraveesActuelles = 0;
            for (var travee in tableauTravees) {
                var numTraveeSuivante = parseInt(travee.substr(travee.indexOf(" ") + 1)) + 1;
                var traveeSuivante = tableauTravees["Travee " + numTraveeSuivante];

                if (tableauTravees[travee].rangDansConstruction === 1) {
                    inventaire["CH1T"]++;
                    inventaire["PEXT"]++;
                    nbTraveesActuelles = 1;
                }

                if (traveeSuivante) {
                    if (traveeSuivante.numConstruction != tableauTravees[travee].numConstruction) {
                        // On se trouve sur la dernière travée de cette construction.
                        inventaire["PEXT"]++;
                        if (nbTraveesActuelles > 0) inventaire["ACC" + nbTraveesActuelles]++;
                    } else {
                        // Il existe une autre travée dans la même construction
                        inventaire["PINT"]++;
                        inventaire["CHTS"]++;
                        nbTraveesActuelles++;
                    }
                } else {
                    inventaire["PEXT"]++;
                    if (nbTraveesActuelles > 0) inventaire["ACC" + nbTraveesActuelles]++;
                }
            }


            // On récupère la liste des modules présents sur le projet et on estime le coût global.
            var coutEstimeBas = 0,
                coutEstimeHaut = 0;
            var keys = Object.keys(inventaire).sort();
            for (var i = 0, key = keys[0]; i < keys.length; key = keys[++i]) {
                var correspondance = key;
                var module = {};
                for (var produit in PRODUITS) {
                    if (PRODUITS[produit].codeModule == key) {
                        correspondance = produit;
                        break;
                    }
                }
                module.codeModule = key;
                module.quantite = inventaire[key];
                module.referenceModule = PRODUITS[correspondance]['libelleModule'];
                modules.push(module);
                coutEstimeBas += (module.quantite * PRODUITS[correspondance].coutFourchetteBasse);
                coutEstimeHaut += (module.quantite * PRODUITS[correspondance].coutFourchetteHaute);
            }

            donneesBrutes.modules = modules;
            donneesBrutes.coutEstimeBas = coutEstimeBas;
            donneesBrutes.coutEstimeHaut = coutEstimeHaut;
            donneesBrutes.screenshot1 = screenshot1;
            donneesBrutes.screenshot2 = screenshot2;
            donneesBrutes.screenshot3 = screenshot3;
            donneesBrutes.screenshot4 = screenshot4;
            donneesBrutes.screenshot5 = screenshot5;
            donneesBrutes.screenshot6 = screenshot6;
            donneesBrutes.screenshot7 = screenshot7;
            donneesBrutes.screenshot8 = screenshot8;
            donneesBrutes.screenshot9 = screenshot9;
            donneesJSON = JSON.stringify(donneesBrutes);

            var request = {
                template: {
                    name: "devis",
                    recipe: "phantom-pdf",
                    engine: "handlebars",
                    phantom: {
                        "format": "A4",
                        "orientation": "landscape",
                        "headerHeight": "0cm",
                        "footer": "<div style='text-align:right;font-size:12px'>Dossier {{nomClient }} - page {#pageNum}/{#numPages}</div>",
                        "footerHeight": "0.5cm"
                    }
                },
                data: donneesJSON
            };

            jsreport.renderAsync(request).then(function (res) {
                res.download('Demande de prix Econologis.pdf')
                $("#popup-attente").hide();
                $("#overlay").hide();
            });
        };
    };

    var options = {
        Ajouter: function () {

            /* Les cas où l'ajout est interdit sont :
                1 - nb maxi de travées (au total) atteint (déjà géré dans creeTravee)
                2 - on atteint le nb maxi de travées par construction */
            if (nbTravees > 1) {
                var voisine = tableauTravees[PREFIXE_TRAVEE + nbTravees];
                if (voisine['rangDansConstruction'] >= NB_TRAVEES_MAXI) {
                    log(nbConstructions);
                    if (nbConstructions == 2) {
                        alerte("Vous avez atteint le nombre maximal de travées autorisées (" + NB_TRAVEES_MAXI + ").");
                        return;
                    } else {
                        $('#overlay').show();
                        $('#popup-decalage').show();
                        return;
                    }
                }
            }

            if (nbOuvertures > 0) {
                if (!confirm("Vous allez perdre toutes les ouvertures déjà créées. Continuer ?")) return;
                supprimerToutesOuvertures();
                recreerTrappes();
            }

            // On repasse volontairement en mode avec textures.
            if ($("span:contains('ossatureBois')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('ossatureBois')").click();

            var travee = creerTravee();
            if (travee) traitementCreationTravee(travee);
        },

        Supprimer: function () {
            if (nbTravees == 1) {
                alerte("Au moins une travée requise.");
                return;
            }
            if (nbOuvertures > 0) {
                if (!confirm("Vous allez perdre toutes les ouvertures déjà créées. Continuer ?")) return;
                supprimerToutesOuvertures();
                recreerTrappes();
            }

            // On repasse volontairement en texture classique.
            if ($("span:contains('ossatureBois')").parent().find("input[type='checkbox']").prop('checked'))
                $("span:contains('ossatureBois')").click();

            var nomTravee = PREFIXE_TRAVEE + nbTravees;
            var travee = scene.getObjectByName(nomTravee);
            scene.remove(travee);
            delete tableauTravees[nomTravee];
            tableauTravees.length--;

            // Suppression d'une travée -> on décale suivant X tout le monde vers la droite.
            for (var i = nbTravees - 1; i > 0; i--) {
                var traveePrecedente = scene.getObjectByName(PREFIXE_TRAVEE + i);
                traveePrecedente.translateX(LARGEUR_TRAVEE / 2);
                tableauTravees[traveePrecedente.name]['positionX'] += LARGEUR_TRAVEE / 2;
            }
            retirerObjetModifiable(nomTravee);
            nbTravees--;

            // On raffiche les cloisons de la travée de gauche
            var voisine = scene.getObjectByName(PREFIXE_TRAVEE + nbTravees);
            voisine.children[indicePDAV].visible = voisine.children[indicePDAR].visible = true;
            voisine.children[indiceToit].children[indicePignonDroit].visible = true;

            // On modifie l'incrustation pour les pignons de toiture.
            modifierIncrustation(voisine.name, 'PD', 'PEXT', true);
            hidePignonIncrustations();

            recalculerConstructions();
            incrusterCotes();

            // On déplace également la boussole pour qu'elle soit toujours à la même distance de la droite de la construction
            scene.getObjectByName('boussole').position.x = tableauTravees["Travee " + nbTravees].positionX + 50;
        }
    }

    var myGui = new dat.GUI();
    var guitravees = myGui.addFolder('Gestion des travées');
    guitravees.add(options, 'Ajouter');
    guitravees.add(options, 'Supprimer');
    guitravees.open();


    var guiEnv = myGui.addFolder("Préférences d'affichage");

    guiEnv.add(controller, 'afficherToit').onChange(function (actif) {

        for (var j = 1; j <= nbTravees; j++) {
            var leToit = scene.getObjectByName(PREFIXE_TRAVEE + j + '>Toit');
            if (!actif) {
                for (var i = 0; i < leToit.children.length; i++) {
                    if (leToit.children[i].name.includes('toit')) {
                        leToit.children[i].visible = false;
                    }
                }
            } else {
                for (var i = 0; i < leToit.children.length; i++) {
                    if (leToit.children[i].name.includes('toit')) {
                        leToit.children[i].visible = true;
                    }
                }
            }
        }
    });


    guiEnv.add(controller, 'afficherPlancher').onChange(function (actif) {
        if (!actif) {
            for (var i = 1; i <= nbTravees; i++) {
                var travee = scene.getObjectByName(PREFIXE_TRAVEE + i);
                travee.children[indiceRoof].visible = false;

                travee.children[indiceRoof].visible = false;
            }
        } else {
            for (var i = 1; i <= nbTravees; i++) {
                var travee = scene.getObjectByName(PREFIXE_TRAVEE + i);
                travee.children[indiceRoof].visible = true;
            }
        }
    });


    guiEnv.add(controller, 'afficherCotes').onChange(function (actif) {

        var cotesX = scene.getObjectByName('CoteX');
        var cotesY = scene.getObjectByName('CoteY');

        if (!actif) {
            if (cotesX) cotesX.visible = false;
            if (cotesY) cotesY.visible = false;
        } else {
            if (cotesX) cotesX.visible = true;
            if (cotesY) cotesY.visible = true;
        }
    });


    guiEnv.add(controller, 'ossatureBois').onChange(function (afficherBois) {

        var isTravee = new RegExp("^" + PREFIXE_TRAVEE + "[1-8]>AV|AR|PGAV|PGAR|PDAV|PDAR$");

        scene.traverse(function (child) {

            if (afficherBois) {

                if (child instanceof THREE.Mesh &&
                    !child.name.includes('Vitre') &&
                    !child.name.includes('Porte') &&
                    child.name != 'ground' &&
                    child.name != 'boussole') {

                    switch (child.material) {

                        case wallMaterial:
                            if (isTravee.test(child.name)) {
                                child.material = MPL_Material;
                                // Il faut récupérer le type d'ouverture présent sur le module.
                                objetsModifiables.forEach(function (objet) {
                                    if (objet.name.includes(child.name + ">Ouverture")) {
                                        var module = objet.name.substr(objet.name.lastIndexOf(' ') + 1, objet.name.length);

                                        switch (module) {
                                            case "PE":
                                                child.material = MPE_Material;
                                                break;
                                            case "F1d":
                                                child.material = MF1d_Material;
                                                break;
                                            case "F1g":
                                                child.material = MF1g_Material;
                                                break;
                                            case "F2":
                                                child.material = MF2_Material;
                                                break;
                                            case "PE+F1":
                                                child.material = MPEF_Material;
                                                break;
                                            case "PF":
                                                child.material = MPF_Material;
                                                break;
                                            case "PO":
                                                child.material = MPI_Material;
                                                break;
                                            case "PG1":
                                                child.material = MPG1_Material;
                                                break;
                                            case "PG2":
                                                child.material = MPG2_Material;
                                                break;
                                            case "PER":
                                                child.material = MPE_Material;
                                                break;
                                            case "F1dR":
                                                child.material = MF1d_Material;
                                                break;
                                            case "F1gR":
                                                child.material = MF1g_Material;
                                                break;
                                            case "F2R":
                                                child.material = MF2_Material;
                                                break;
                                        }
                                    }
                                });
                            }
                            break;

                        case pignonMaterial:
                            child.material = PEXT_Material;
                            break;

                        case roofMaterial:
                            child.material = CH1T_Material;
                            var newName = child.name.replace('excluded', 'transparent');
                            child.name = newName;
                            break;

                    }
                }

                if (child instanceof THREE.Group && child.name.includes('Ouverture') && !child.name.includes(' PO')) {
                    child.visible = false;
                }
            } else {

                // Texture d'origine
                if (child instanceof THREE.Mesh) {
                    if (!child.name.includes('Vitre') && !child.name.includes('Porte')) {

                        if (isTravee.test(child.name))
                            child.material = wallMaterial;
                    }

                    if (child.material == PEXT_Material) {
                        child.material = pignonMaterial;
                    }

                    if (child.material == CH1T_Material) {
                        child.material = roofMaterial;
                        var newName = child.name.replace('transparent', 'excluded');
                        child.name = newName;
                    }
                }

                if (child instanceof THREE.Group && child.name.includes('Ouverture') && !child.name.includes(' PO')) {
                    child.visible = true;
                }
            }
        });
    });
    guiEnv.open();


    var guiMisc = myGui.addFolder("Diverses actions");
    guiMisc.add(controller, 'genererDemandePrix');
    guiMisc.add(controller, 'sauvegarderCeProjet');
    guiMisc.add(controller, 'importerUnProjet');
    guiMisc.open();

}
