// Specific code for the 'federation-structures' page

const apiGetAllUrl = "/typo3/index.php?route=/otcore/structures/all";
const apiGetByIdUrl = "/typo3/index.php?route=/otcore/structures/get";

// Converts numeric degrees to radians
function toRad(Value)
{
    return Value * Math.PI / 180;
}

//This function takes in latitude and longitude of two location and returns the distance between them as the crow flies (in km)
function sphericDistance(lat1, lon1, lat2, lon2)
{
    let R = 6371; // km
    let dLat = toRad(lat2-lat1);
    let dLon = toRad(lon2-lon1);
    lat1 = toRad(lat1);
    lat2 = toRad(lat2);

    let a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
    let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return R * c;
}

const practicesFr = {
    "BIG_BAND": "Big band",
    "BRASS_BAND": "Brass band",
    "ORCHESTRA_CLASS": "Classe d'orchestre",
    "ACCORDION_ORCHESTRA": "Orchestre d'accordéons",
    "HARMONY_ORCHESTRA": "Orchestre d'harmonie",
    "PHILHARMONIC_ORCHESTRA": "Orchestre philharmonique",
    "SYMPHONY_ORCHESTRA": "Orchestre symphonique",
    "STRING_ORCHESTRA": "Orchestre à cordes",
    "PLUCKED_ORCHESTRA": "Orchestre à plectres",
    "FANFARE_BAND": "Orchestre de fanfare",
    "BAGAD": "Bagad",
    "BANDAS": "Bandas ou Fanfare de rue",
    "BATTERY_FANFARE": "Batterie fanfare",
    "BATTUCADA": "Battucada",
    "FOLKLORIC_BAND": "Ensemble folklorique",
    "FIFE_AND_DRUM": "Fifres et tambours",
    "MARCHING_BAND": "Marching band ou Show parade",
    "HUNTING_HORNS": "Trompes de chasse",
    "CHILDRENS_CHOIR": "Choeur d'enfants",
    "FEMAL_CHOIR": "Choeur de femmes",
    "MENS_CHOIR": "Choeur d'hommes",
    "MIXED_CHORUS": "Choeur mixte",
    "VOCAL_BAND_UP_16": "Ensemble vocal (jusqu'à 16)",
    "CLARINET_CHOIR": "Ensemble de clarinettes",
    "COPPER_BAND": "Ensemble de cuivres",
    "FLUTE_ENSEMBLE": "Ensemble de flûtes",
    "SAXOPHONES_BAND": "Ensemble de saxophones",
    "VIOLIN_BAND": "Ensemble de violons",
    "PERCUSSION_BAND": "Ensemble de percussions",
    "CURRENT_MUSIC_GROUP": "Groupe de Musique actuelle",
    "CHAMBER_MUSIC_ENSEMBLE": "Ensemble de Musique de chambre",
    "TRADITIONAL_MUSIC_ENSEMBLE": "Ensemble de Musique traditionnelle",
    "JAZZ_BAND": "Ensemble de Jazz",
    "EDUCATION": "Enseignement",
    "CHEERLEADER": "Majorettes",
    "TROOP": "Troupe",
    "OTHER": "Autre"
}

// Specific JS used for the Structures layout
// > Needs to be loaded after the main.js script
$(document).ready(function() {

    const queryParameters = new URLSearchParams(window.location.search);

    // Init
    let document = $('html, body');
    let structureFrame = $('.ot-structures-frame').first();
    let structureFrameDetails = $('.ot-structures-frame-details').first();

    // let organizationId = structureFrame.data('org-id');

    let resultsPageDiv = structureFrame.find('.structures-page').first();
    let pleaseWaitSpan = structureFrame.find('.please-wait').first();
    let noResultSpan = structureFrame.find('.no-result').first();
    let errorMsgSpan = structureFrame.find(".error-message").first();
    let cardDivModel = structureFrame.find('.structure-card-model').first();
    let paginationBar = structureFrame.find('.pagination-bar').first();
    let paginationList = structureFrame.find('.pagination-list').first();
    let gotoFirstPage = structureFrame.find('.goto-first-page').first();
    let gotoLastPage = structureFrame.find('.goto-last-page').first();
    let resultsCountMessage = structureFrame.find('.results-count-message').first();
    let resultsCountVar = resultsCountMessage.find('.count').first();
    let detailsSection = structureFrame.find('.structure-see').first();

    let form = $("#structure-search-form");
    let whatInput = form.find("input[name='search-query']").first();
    let cityInput = form.find("input[name='search-city']").first();
    let latInput = form.find("input[name='lat']").first();
    let longInput = form.find("input[name='long']").first();
    let practiceSelect = form.find("select[name='search-practice']").first();
    let provinceSelect = form.find("select[name='search-province']").first();
    let federationSelect = form.find("select[name='search-federation']").first();
    let radiusSelect = form.find("select[name='search-radius']").first();
    let resetButton = form.find("button.reset-search").first();
    let submitButton = form.find("button.submit-search").first();

    let organizationId = parseInt(queryParameters.get('organization-id') || structureFrame.data('organization-id'));
    if (!organizationId) {
        pleaseWaitSpan.hide();
        errorMsgSpan.show();
        throw 'Missing organization-id parameter';
    }

    // #### Instanciate and populate leaflet maps
    let mapDiv = $('#structure-map').first();
    let mapId = $(mapDiv).attr("id");

    // Instanciate the map object  @see https://leafletjs.com/reference-1.6.0.html#map-factory
    const mapOptions = {scrollWheelZoom: false, zoomSnap: 0.25};
    map = L.map(mapId, mapOptions);
    map.setView([46.71, 2.14], 6);
    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);

    var listenMapMoves = false;

    var defaultsBounds = [51.03, -5.78, 41.2, 9.70];

    // Set the view
    let resetMapBounds = function () {
        area = null;
        bounds = L.latLngBounds(
            L.latLng(defaultsBounds[0], defaultsBounds[1]),
            L.latLng(defaultsBounds[2], defaultsBounds[3])
        );
        map.fitBounds(bounds);
    }
    resetMapBounds();

    var clusters = null;

    // Details view map
    const structureMapOptions = {scrollWheelZoom: true, zoomSnap: 0.25};
    let structureMap = L.map('structure-details-map', structureMapOptions);
    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(structureMap);

    // Call this method when a map is hidden or shown to force a re-rendering
    function resizeMaps() {
        map.invalidateSize();
        structureMap.invalidateSize();
    }


    // #### Filters

    // The current query
    var query = {};
    var area = null;

    // Update the current query with form data
    function updateQuery() {
        query['what'] = whatInput.val();
        query['lat'] = latInput.val();
        query['long'] = longInput.val();
        query['practice'] = practiceSelect.val();
        query['province'] = provinceSelect.val();
        query['federation'] = federationSelect.val();
        query['radius'] = radiusSelect.val();
    }

    function updateArea(long1, lat1, long2, lat2) {
        area = [long1, lat1, long2, lat2];
    }

    // Does the given structure match the current query
    function matchCurrentQuery(structure) {
        // Is the structure a member of the website federation?
        if (![structure.n1Id, structure.n2Id, structure.n3Id, structure.n4Id, structure.n5Id].includes(organizationId)) {
            return false;
        }

        // Filter by name
        if (query['what'] && !structure.name.toLowerCase().includes(query['what'].toLowerCase())) {
            return false;
        }

        // filter by geographical position
        if (query['lat'] && query['long']) {
            if (!structure.latitude || !structure.longitude) {
                return false;
            }

            let radius = Number(query['radius']) ?? 0;

            // radius is increased by 10km to approximate the city radius
            radius += 10;

            if (sphericDistance(query['lat'], query['long'], structure.latitude, structure.longitude) > radius) {
                return false;
            }
        }

        // filter by practice
        if (query['practice'] && !structure.practices.includes(query['practice'])) {
            return false;
        }

        if (query['province']) {
            let province = query['province'];

            // A leading '_' may have been added to prevent the '0' from being stripped by fluid
            province = province.replace('_', '');

            if (!structure.postalCode.startsWith(province)) {
                return false
            }
        }
        if (query['federation'] && !structure.parents.includes(Number(query['federation']))) {
            return false;
        }

        // filter by map bounds
        if (area !== null) {
            if (structure.longitude < area[0] ||
                structure.latitude < area[1] ||
                structure.longitude > area[2] ||
                structure.latitude > area[3]
            ) {
                if (structure.id == 498) {
                    console.log(structure.longitude, structure.latitude, area);
                    console.log(structure.longitude < area[0], structure.latitude < area[1], structure.longitude > area[2], structure.latitude > area[3])
                }
                return false;
            }
        }
        return true;
    }

    function populatePracticeSelect() {
        practiceSelect.children('option:not(:first)').remove();
        let has_options = false;

        for (const key in practicesFr) {
            let option = '<option value="' + key + '">' + practicesFr[key] + '</option>';
            practiceSelect.append(option);
            has_options = true;
        }
        if (has_options) {
            practiceSelect.prop("disabled", false);
        }
    }

    function populateFederationsSelect() {
        federationSelect.children('option:not(:first)').remove();
        let has_options = false;

        structures.forEach(function (structure) {
            if (structure.n1Id === organizationId && structure.principalType.endsWith('FEDERATION')) {
                let option = '<option value="' + structure.id + '">' + structure.name + '</option>';
                federationSelect.append(option);
                has_options = true;
            }
        })
        if (has_options) {
            federationSelect.prop("disabled", false);
        }
    }

    // #### Display results
    const itemsPerPage = 8;
    let currentPage = 1;

    updateQuery();

    // on page link clicked event
    function pageLinkClicked(e) {
        e.preventDefault();
        let page = $(this).data('page');
        if (page > 0) {
            currentPage = page;
        }
        refresh();
        $('body,html').animate({scrollTop: 0},500);
    }
    gotoFirstPage.on('click', pageLinkClicked);
    gotoLastPage.on('click', pageLinkClicked);

    function refresh(listOnly=false) {

        // Reinitialize current results
        pleaseWaitSpan.show();
        listenMapMoves = false;

        resultsPageDiv.find('.structure-card').remove();
        paginationList.find('.goto-page-li').remove();
        paginationList.hide();
        if (!listOnly && clusters !== null) {
            map.removeLayer(clusters);
        }

        // ** Update results
        let results = [];
        structures.forEach(function(structure) {
            if (matchCurrentQuery(structure)) {
                results.push(structure)
            }
        });

        sortKeys = function (x) {
            return x.addressCity.toLowerCase() + x.name.toLowerCase();
        }
        results.sort((a, b) => sortKeys(a).localeCompare(sortKeys(b)));

        // ** Show the results for the current page
        let index = 0;

        results.forEach(function(structure) {
            if (((currentPage - 1) * itemsPerPage) <= index && index < (currentPage * itemsPerPage)) {
                let cardDiv = $(cardDivModel).clone();
                let practiceTagModel = cardDiv.find('.structure-practice-model').first();

                cardDiv.data('id', structure.id);

                if (structure.practices !== null) {
                    let i = 1;
                    for (const practice of structure.practices) {
                        let tag = practiceTagModel.clone();

                        if (i <= 3) {
                            tag.text(practicesFr[practice]);
                        } else {
                            tag.text('+ ' + (practice.length - 3) + ' ...');
                        }
                        tag.removeClass('structure-practice-model')
                        tag.addClass('structure-practice')
                        tag.show();
                        practiceTagModel.parent().append(tag);

                        if (i > 3) { break; }
                        i++;
                    }
                }

                if (structure.logoId) {
                    let poster = cardDiv.find('.structure-poster').first().children('img').first();
                    poster.attr('src', 'https://api.opentalent.fr/app.php/_internal/secure/files/' + structure.logoId + '/0x60');
                }

                let cardTitle = cardDiv.find('.structure-name a').first();
                cardTitle.text(structure.name);
                cardTitle.attr('href', structure.website);

                cardDiv.find('.structure-details-address').first().text(
                    [structure.streetAddress, structure.postalCode, structure.addressCity].join(" ")
                );
                cardDiv.find('.structure-details-federation').first().text(structure.n1Name);

                cardDiv.find('.structure-see').first().data('organization-id', structure.id)

                cardDiv.show();
                cardDiv.removeClass('structure-card-model');
                cardDiv.addClass('structure-card');
                cardDiv.appendTo(resultsPageDiv);
            }
            index++;
        });

        // ** Update results on map
        if (!listOnly) {
            clusters = L.markerClusterGroup();

            results.forEach(function (item) {
                if (item.longitude && item.latitude) {
                    let marker = L.marker([item.latitude, item.longitude]);
                    marker.bindPopup(`<b>${item.name}</b><br/>${item.postalCode} ${item.addressCity}<br/><a href="${item.website}" target="_blank">${item.website}</a>`);
                    clusters.addLayer(marker);
                }
            });
            map.addLayer(clusters);
        }

        // ** Update results count and pagination
        let resultsCount = results.length;

        resultsCountMessage.show();
        resultsCountVar.text("" + resultsCount);

        let pagesCount = Math.floor(resultsCount / itemsPerPage) + 1;
        gotoLastPage.data("page", pagesCount);

        let pageLiModel = paginationList.find('.goto-page-li-model').first();

        let page_min = currentPage > 5 ? currentPage - 5 : 1;
        let page_max = currentPage < (pagesCount - 5) ? currentPage + 5 : pagesCount;

        for (let i = page_min; i <= page_max; i++) {
            let pageLi = pageLiModel.clone();

            let pageLink = pageLi.children('a').first();

            pageLink.text("" + i);
            pageLink.attr("data-page", i);
            pageLink.on('click', pageLinkClicked)

            if (i === currentPage) {
                pageLi.addClass('current');
            }
            pageLi.removeClass('goto-page-li-model')
            pageLi.addClass('goto-page-li')

            pageLi.show();

            pageLi.appendTo(paginationList);
        }
        paginationBar.show()
        paginationList.show()

        // Finalize
        pleaseWaitSpan.hide();
        listenMapMoves = true;
    }

    function fitMapToResults() {
        bounds = clusters.getBounds();
        if (bounds.isValid()) {
            map.fitBounds(bounds);
        }
    }

    // #### Load structures data and refresh
    var structures;

    populatePracticeSelect();

    function loadStructures() {
        structures = []

        $.ajax({
            type: 'GET',
            url: apiGetAllUrl,
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            timeout : 12000,
        })
        .done(function(res) {

            res.forEach(function(item) {
                structure = item;
                structure.n1Id = parseInt(structure.n1Id);
                structure.n2Id = parseInt(structure.n2Id);
                structure.n3Id = parseInt(structure.n3Id);
                structure.n4Id = parseInt(structure.n4Id);
                structure.n5Id = parseInt(structure.n5Id);
                structure.practices = structure.practices !== null ? structure.practices.split(",") : [];
                structures.push(structure);
            })

            populateFederationsSelect();
            refresh();
        })
        .fail(function(xhr, textStatus, errorThrown) {
            pleaseWaitSpan.hide();
            errorMsgSpan.show();
            throw 'Error while fetching the API data: ' + textStatus  + ' - ' + errorThrown;
        });
    }
    loadStructures();


    // #### Events

    // Update query
    form.on('submit', function(e) {
        e.preventDefault();
        updateQuery();
        area = null;
        currentPage = 1;
        refresh();
        if (Object.values(query).some((k) => k)) {
            // if it's not an empty query, fit to results
            fitMapToResults();
        }
    });

    submitButton.on('click', function() {
        form.submit();
    });

    $('.filters select', form).on('change', function() {
        form.submit();
    });

    // Reset search fields
    resetButton.on('click', function (e) {
        e.preventDefault();
        let form = $(this).closest('form');
        form.find('input').each(function () {
            $(this).val('');
        });
        form.find('select').each(function () {
            $(this).val('');
        });
        resetMapBounds();
        form.submit();
    });

    // Map goto commands
    $('img[data-map-fit]').on('click', function (e) {
        let goto = $(this).data('map-fit');
        let corners = goto.split(";");
        let p1 = corners[0].split(",");
        let p2 = corners[1].split(",");
        let bounds = L.latLngBounds(L.latLng(p1[0], p1[1]), L.latLng(p2[0], p2[1]));
        map.fitBounds(bounds);
    });

    // Toggle structures list and map view
    let filtersRow = form.find('.filters').first();
    let buttonsRow = form.find('.search-buttons').first();

    $('.activate-map-view').on('click', function (e) {
        e.preventDefault();
        if (structureFrame.hasClass('map-view')) {
            // already in map view
            return;
        }
        listenMapMoves = true;
        structureFrame.removeClass('list-view');
        structureFrame.addClass('map-view');

        // move reset and submit buttons
        submitButton.appendTo(buttonsRow);
        resetButton.prependTo(buttonsRow);
        buttonsRow.show();

        resizeMaps();
    })
    $('.activate-list-view').on('click', function (e) {
        e.preventDefault();
        if (structureFrame.hasClass('list-view')) {
            // already in list view
            return;
        }
        listenMapMoves = false;
        structureFrame.removeClass('map-view');
        structureFrame.addClass('list-view');

        // move reset and submit buttons
        submitButton.appendTo(filtersRow);
        resetButton.prependTo(filtersRow);
        buttonsRow.hide();

        resetButton.prependTo()


        resetMapBounds();
    })

    // Filter results on map moves
    map.on('zoomend moveend', function(e) {
        if (listenMapMoves) {
            let bounds = map.getBounds();
            updateArea(bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth())
            refresh(true);
        }
    })

    // ### Location filter
    let resultDropdownDiv = form.find('.city-search-dropdown').first();
    let inputName = resultDropdownDiv.siblings("input[name='search-city']").first();
    let inputLat = resultDropdownDiv.siblings("input[name='lat']").first();
    let inputLong = resultDropdownDiv.siblings("input[name='long']").first();
    let resultDiv = resultDropdownDiv.find('.city-search-results').first();
    let loadingDiv = resultDropdownDiv.find('.city-search-loading').first();
    let noResultDiv = resultDropdownDiv.find('.city-search-no-result').first();
    let geolocButton = form.find("button[name='search-localize']").first();

    function setNewLocation(name, long, lat) {
        inputName.val(name);
        inputLong.val(long);
        inputLat.val(lat);
        inputName.css("cursor", "pointer");
        resultDropdownDiv.hide();
        if (radiusSelect.val() === '') {
            radiusSelect.val('10')
        }
        form.submit();
    }

    function hideCityResults() {
        resultDropdownDiv.hide();
        // if no city was selected, clear the input
        if (!inputLat.val()) {
            inputName.val('');
            resultDiv.empty();
        }
        inputName.css("cursor", "inherit");
        inputName.removeClass('loading');
    }

    document.click(function() {
        hideCityResults();
    });

    $(document).keydown(function(e) {
        if(e.key === "Escape") {
            hideCityResults();
        }
    });

    inputName.click(function(e) {
        if (resultDiv.children('.city-search-item').length > 0) {
            resultDropdownDiv.show();
        }
        e.stopPropagation();
    });

    inputName.keypress(function (e) {
        if (e.key === "Enter") {

            let results = resultDiv.children('.city-search-item');

            if (inputName.hasClass('loading')) {
                // A request is currently running, enter will select the first result when the request will complete
                inputName.addClass('enter-pressed');
                e.preventDefault();
            } else if (results.length > 0) {
                // If results are displayed and no request is currently running, enter select the first result
                setNewLocation(results[0].text(), results[0].data("x"), results[0].data("y"))
                e.stopPropagation();
            }
        } else {
            inputName.addClass('loading');
        }
    });

    resultDiv.on('click', '.city-search-item', function (e) {
        setNewLocation($(this).text(), $(this).data("x"), $(this).data("y"))
        e.stopPropagation();
    });

    inputName.on('click', function(e) {
        if ($(this).is(":focus")) {
            $(this).val('');
        }
    });

    // gouv.fr REST geocoding api
    const addressApiUrl = "https://api-adresse.data.gouv.fr/search/?type=municipality&autocomplete=1&limit=5&q=";

    function populateCitySearchResults() {

        let query = inputName.val();
        let url = addressApiUrl + encodeURI(query);

        if (!query) {
            resultDropdownDiv.hide();
            return;
        }

        inputLat.val('');
        inputLong.val('');
        resultDropdownDiv.show();
        if (resultDiv.children('.city-search-item').length === 0) {
            loadingDiv.show()
        }
        noResultDiv.hide();

        $.ajax({
            type: 'GET',
            url: url,
            dataType: "json",
            contentType: "application/json; charset=utf-8"
        })
        .done(function(res) {
            console.log(res);
            let features = res.features;
            resultDiv.empty();

            if (!features.length > 0) {
                noResultDiv.show();
                resultDiv.hide();
            } else if (inputName.hasClass('enter-pressed')) {
                // the enter key has been pressed before the request terminate
                let f = features[0];
                setNewLocation(
                    f.properties.name + ' (' + f.properties.postcode + ')',
                    f.geometry.coordinates[0],
                    f.geometry.coordinates[1]
                )
                resultDiv.hide();
            } else {
                for (const f of features) {
                    let x = f.geometry.coordinates[0];
                    let y = f.geometry.coordinates[1];
                    let name = f.properties.name;
                    let postcode = f.properties.postcode;
                    let span = '<div class="city-search-item" data-x="' + x + '" data-y="' + y + '">' + name + ' (' + postcode + ')</div>';
                    resultDiv.append(span);
                }
                resultDiv.show();
            }
            inputName.removeClass('loading');
            inputName.removeClass('enter-pressed');
            loadingDiv.hide()
        })
        .fail(function(e) {
            console.error(e);
            loadingDiv.hide();
        });
    }

    var runningPopulate = null;

    inputName.on('input', function(e) {
        if (runningPopulate !== null) {
            window.clearTimeout(runningPopulate)
        }
        runningPopulate = window.setTimeout(populateCitySearchResults, 300);
    });

    geolocButton.on('click', function(e) {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(
                function (geoloc) {
                    setNewLocation(tr['around-me'], geoloc.coords.longitude, geoloc.coords.latitude)
                    e.stopPropagation();
                 },
                function () {
                    alert(tr['geoloc-unavailable']);
                }
            );
            e.stopPropagation();
        } else {
            alert(tr['geoloc-unsupported']);
        }
    });

    // Structure details
    function formatFrenchPhoneNumber(number) {
        const regexPhoneNumber = /\+33([1-9])(\d{2})(\d{2})(\d{2})(\d{2})$/;

        if (number && number.match(regexPhoneNumber)) {
            return number.replace(regexPhoneNumber, '0$1 $2 $3 $4 $5');
        } else {
            return number;
        }
    }

    function populateDetailsView(structure) {

        const structureHeader = structureFrameDetails.find('.structure-header').first();

        let logo = structureHeader.find('img.logo').first();
        let defaultLogo = structureHeader.find('img.default-logo').first();
        if (structure.logoId) {
            logo.attr('src', 'https://api.opentalent.fr/app.php/_internal/secure/files/' + structure.logoId + '/0x60');
            logo.show();
            defaultLogo.hide();
        } else {
            defaultLogo.show();
            logo.hide();
        }

        let title = structureHeader.find('.structure-name').first();
        title.text(structure.name);

        let fbLink = structureHeader.find('a.facebook').first();
        if (structure.facebook) {
            fbLink.attr('href', structure.facebook);
            fbLink.show();
        } else {
            fbLink.attr('href', '');
            fbLink.hide();
        }

        let twitterLink = structureHeader.find('a.twitter').first();
        if (structure.twitter) {
            twitterLink.attr('href', structure.twitter);
            twitterLink.show();
        } else {
            twitterLink.attr('href', '');
            twitterLink.hide();
        }

        let instagramLink = structureHeader.find('a.instagram').first();
        if (structure.instagram) {
            instagramLink.attr('href', structure.instagram);
            instagramLink.show();
        } else {
            instagramLink.attr('href', '');
            instagramLink.hide();
        }

        let practiceTagModel = structureFrameDetails.find('.structure-practice-model').first();
        let practiceContainer = practiceTagModel.parent();
        practiceContainer.children('.structure-practice').remove();

        if (structure.practices !== null) {
            let i = 1;
            for (const practice of structure.practices) {
                let tag = practiceTagModel.clone();

                if (i <= 3) {
                    tag.text(practicesFr[practice]);
                } else {
                    tag.text('+ ' + (practice.length - 3) + ' ...');
                }
                tag.removeClass('structure-practice-model')
                tag.addClass('structure-practice')
                tag.show();
                practiceContainer.append(tag);

                if (i > 3) { break; }
                i++;
            }
        }

        let contact = structureFrameDetails.find('.contact').first();
        contact.find('.address').first().text(
            [structure.streetAddress, structure.postalCode, structure.addressCity].join(" ")
        );

        let phoneLink = contact.find('.phone a').first();
        if (structure.telphone) {
            phoneLink.text(formatFrenchPhoneNumber(structure.telphone));
            phoneLink.attr('href', 'tel:' + structure.telphone);
        } else {
            phoneLink.text('');
            phoneLink.attr('href', '');
        }

        let mailLink = contact.find('.mail a').first();
        if (structure.email) {
            mailLink.text(structure.email);
            mailLink.attr('href', 'mailto:' + structure.email);
        } else {
            mailLink.text('');
            mailLink.attr('href', '');
        }

        let websiteLink = contact.find('.website a').first();
        if (structure.website) {
            websiteLink.text(structure.website);
            websiteLink.attr('href', structure.website);
        } else {
            websiteLink.text('');
            websiteLink.attr('href', '');
        }

        // contact.find('.province').first().text('...');

        let networkName = contact.find('.network').first();
        if (structure.n1Name) {
            networkName.text(structure.n1Name);
        } else {
            networkName.text('');
        }

        if (structure.longitude && structure.latitude) {
            // clear previous markers
            structureMap.eachLayer(function(layer) {
                if (layer instanceof L.MarkerClusterGroup) { map.removeLayer(layer) }
            })

            // add structure position
            let marker = L.marker([structure.latitude, structure.longitude]);
            marker.bindPopup(`<b>${structure.name}</b><br/>${structure.postalCode} ${structure.addressCity}<br/><a href="${structure.website}" target="_blank">${structure.website}</a>`);
            marker.addTo(structureMap);

            // zoom on the newly created marker
            var latLngs = [ marker.getLatLng() ];
            var markerBounds = L.latLngBounds(latLngs);
            structureMap.fitBounds(markerBounds);

            // show map
            structureFrameDetails.find('#structure-details-map').first().show();
        }

        // ux fix: don't know why, but the topbar is above the screen without this
        structureFrameDetails.scrollTop(0);
    }

    structureFrame.on('click', '.structure-see', function(e) {
        e.preventDefault();
        const organizationId = $(this).data('organization-id');

        structureFrame.hide();
        structureFrameDetails.show();

        $.ajax({
            type: 'GET',
            url: apiGetByIdUrl + '&organization-id=' + organizationId,
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            timeout : 5000
        })
        .done(function(structure) {
            structure.practices = structure.practices !== null ? structure.practices.split(",") : [];
            populateDetailsView(structure)

            structureFrameDetails.find('.please-wait').first().hide();
            structureFrameDetails.find('.content').first().show();
            resizeMaps();
        })
        .fail(function(xhr, textStatus, errorThrown) {
            pleaseWaitSpan.hide();
            errorMsgSpan.show();
            throw 'Error while fetching the API data: ' + textStatus  + ' - ' + errorThrown;
        });
    })

    structureFrameDetails.on('click', '.go-back', function (e) {
        e.preventDefault();

        structureFrameDetails.find('.please-wait').first().show();
        structureFrameDetails.find('.content').first().hide();
        structureFrameDetails.hide();
        structureFrame.show();
        resizeMaps();
    })
});
