<template>
    <li class="files-tree-node" role="none">
        <span
            class="files-tree-node-label"
            :class="{ 'is-folder': isFolder, 'is-selectable': isSelectable, 'is-current': isCurrent }"
            @click="onClick"
        >
            <span
                v-if="isExpandable"
                class="files-tree-node-icon files-tree-node-expand fa fa-chevron-right"
                :class="{ 'is-expanded': expanded }"
                aria-hidden="true"
                @click.stop="toggle"
            ></span>
            <span class="files-tree-node-icon" :class="typeIconClass" aria-hidden="true"></span>
            {{ item.title }}
        </span>
        <transition name="files-tree-node-slide">
            <ul v-if="isExpandable && expanded" class="files-tree-node-children">
                <files-tree-node
                    v-for="child in childNodes"
                    :key="child.id"
                    :item="child"
                    :selected="selected"
                    @file-selected="relayFileSelected"
                    @subst-file-selected="relaySubstFileSelected"
                ></files-tree-node>
            </ul>
        </transition>
    </li>
</template>

<script>
    /**
     * Noeud recursif de l'arbre "Gestion des fichiers" (dossiers substituables,
     * fichiers XML, fichiers de substitution _subst.xml). Remplace le dijit/Tree
     * de l'ancien rendu Dojo : memes types de noeuds (folder/file/substFile).
     * Simple clic : deplie/replie un dossier, ou selectionne un fichier / fichier
     * de substitution (l'ancien rendu Dojo exigeait un double-clic pour la
     * selection).
     *
     * Les enfants ne sont instancies qu'au depliage (v-if sur expanded) : l'arbre
     * complet represente ~980 noeuds et ~3200 elements DOM, dont le rendu integral
     * au chargement rendait l'ecran tres lent -- particulierement avec la charte
     * graphique Pure, qui ajoute UIkit dont l'observateur de mutations parcourt
     * le sous-arbre concerne a chaque modification du DOM.
     */
    export default {
        name: "files-tree-node",

        props: {
            /**
             * Noeud de l'arbre tel que fourni par misc_files::get_tree_element() :
             * { id, type, title, num_parent, children, files }.
             */
            item: { type: Object, required: true },
            /** Fichier/fichier de substitution actuellement selectionne (racine), pour le surlignage. */
            selected: { type: Object, default: null }
        },

        data() {
            return {
                expanded: false
            };
        },

        computed: {
            isFolder() {
                return this.item.type === "folder";
            },

            /** Un fichier n'est deplie que s'il a un fichier de substitution associe. */
            hasSubstFile() {
                return this.item.type === "file" && !!(this.item.children && this.item.children.length);
            },

            isExpandable() {
                return this.isFolder || this.hasSubstFile;
            },

            /** Selection active sur les fichiers et fichiers de substitution, jamais sur un dossier. */
            isSelectable() {
                return this.item.type !== "folder";
            },

            /** Ce noeud est-il le fichier / fichier de substitution actuellement affiche a droite ? */
            isCurrent() {
                if (!this.selected || !this.isSelectable) {
                    return false;
                }
                return this.item.id === this.selected.path + "/" + this.selected.filename;
            },

            /** Dossiers et fichiers : leurs "enfants" a afficher sont children (sous-dossiers ou fichier de subst) + files (fichiers du dossier). */
            childNodes() {
                return [].concat(this.item.children || [], this.item.files || []);
            },

            typeIconClass() {
                switch (this.item.type) {
                    case "folder":
                        return this.expanded ? "fa fa-folder-open" : "fa fa-folder";
                    case "file":
                        return "fa fa-file-text-o";
                    case "substFile":
                        return "fa fa-copy";
                    default:
                        return "";
                }
            }
        },

        methods: {
            toggle() {
                this.expanded = !this.expanded;
            },

            /**
             * Simple clic : deplie/replie un dossier, ou selectionne un fichier /
             * fichier de substitution (l'ancien rendu Dojo exigeait un double-clic
             * pour la selection, mais un simple clic est plus naturel et plus fiable).
             */
            onClick() {
                if (this.item.type === "file" || this.item.type === "substFile") {
                    this.select();
                } else if (this.isExpandable) {
                    this.toggle();
                }
            },

            select() {
                const path = this.item.id.substring(0, this.item.id.lastIndexOf("/"));
                const filename = this.item.id.substring(this.item.id.lastIndexOf("/") + 1);
                if (this.item.type === "file") {
                    this.$emit("file-selected", { path, filename, hasSubstFile: this.hasSubstFile });
                } else if (this.item.type === "substFile") {
                    this.$emit("subst-file-selected", { path, filename });
                }
            },

            /** Relais de l'evenement d'un descendant vers le parent (recursif jusqu'a filesTree.vue). */
            relayFileSelected(payload) {
                this.$emit("file-selected", payload);
            },

            relaySubstFileSelected(payload) {
                this.$emit("subst-file-selected", payload);
            },

            /**
             * Deplie ce noeud et tous ses descendants (bouton "tout deplier").
             * Les enfants n'existant qu'une fois le noeud deplie, on attend leur
             * rendu avant de poursuivre la descente.
             */
            expandAll() {
                if (!this.isExpandable) {
                    return;
                }
                this.expanded = true;
                this.$nextTick(() => {
                    this.$children.forEach(child => child.expandAll());
                });
            },

            /**
             * Replie ce noeud et tous ses descendants (bouton "tout replier").
             * On replie les descendants d'abord, tant qu'ils sont encore montes.
             */
            collapseAll() {
                this.$children.forEach(child => child.collapseAll());
                this.expanded = false;
            }
        }
    };
</script>
