<template>
    <div class="contributionFormContainer" id="ContributionFormContainer">
        <tabs
            :tabs="tabDescriptors"
            :active="activeId"
            :aria-label="tablistLabel"
            :close-label-template="closeLabelTemplate"
            @select="selectTab"
            @close="requestCloseTab"
        ></tabs>
        <div
            v-for="tab in tabs"
            :key="tab.id"
            :id="tab.id"
            role="tabpanel"
            :aria-labelledby="'tab_' + tab.id"
            tabindex="0"
            class="contribution_tabpanel"
            v-show="tab.id === activeId"
        >
            <p v-if="!tab.loaded" class="contribution_tab_loading" role="status" aria-live="polite">
                {{ messages.get('contribution', 'contribution_tab_loading') }}
            </p>
            <div v-else v-html="tab.html"></div>
        </div>
        <p class="sr-only" role="status" aria-live="polite" aria-atomic="true">{{ announcement }}</p>
    </div>
</template>

<script>
import tabs from "../../../common/components/tabs.vue";

export default {
  name: "contributionFormTabs",
  components: { tabs },
  props: {
    title: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      tabs: [
        { id: "main", title: this.title, html: this.readSourceHtml(), closable: false, locked: false, loaded: true, nodeClickedId: null, backup: {} }
      ],
      activeId: "main",
      scrollPosition: 0,
      usedScenario: 0,
      announcement: "",
      tabSeq: 0,
      contributionInstances: {},
      topicHandles: [],
      paramsURL: this.parseParamsFromUrl(window.location.toString())
    };
  },
  computed: {
    tabDescriptors() {
      return this.tabs.map(tab => ({ id: tab.id, title: tab.title, closable: tab.closable, locked: tab.locked }));
    },
    tablistLabel() {
      return this.messages.get("contribution", "contribution_tablist_label");
    },
    closeLabelTemplate() {
      return this.messages.get("contribution", "contribution_tab_close");
    }
  },
  mounted() {
    this.initializeTabContent(this.tabs[0], document.getElementById("main"), { wireCancel: false });
    window.require(["dojo/topic"], topic => {
      ["Contribution", "ButtonFunction", "SubTabResults", "FormContributionSelector", "removeSubContrib", "ContentContribution"].forEach(topicName => {
        this.topicHandles.push(topic.subscribe(topicName, (evtType, evtArgs) => this.handleEvents(evtType, evtArgs)));
      });
    });
  },
  beforeDestroy() {
    this.topicHandles.forEach(handle => handle.remove());
  },
  methods: {
    parseParamsFromUrl(url) {
      const queryString = url.substring(url.indexOf("?") + 1);
      return Object.fromEntries(new URLSearchParams(queryString));
    },

    // Le contenu du panneau principal est depose par le PHP dans un <template>
    // voisin (pas dans les donnees $data) pour eviter de le faire transiter par
    // un encodage JSON : ce HTML melange des libelles UTF-8 (issus du JSON de
    // parametrage du formulaire) et du gabarit ISO-8859-1.
    readSourceHtml() {
      const source = document.getElementById("contributionFormTabsSource");
      return source ? source.innerHTML : "";
    },

    // -- cycle de vie des onglets --------------------------------------

    initializeTabContent(tab, panelNode, { wireCancel = false, formType = "contribution_area_form_0" } = {}) {
      if (!panelNode) {
        return;
      }
      window.require(["dojo/parser"], parser => parser.parse(panelNode));
      this.wireFieldButtons(panelNode);
      this.wireDraftFields(panelNode);
      if (wireCancel) {
        this.wireCancelButton(panelNode, tab.id);
      }
      window.require(["apps/pmb/contribution/Contribution", "apps/pmb/contribution/ContributionFormEdit"], (Contribution, ContributionFormEdit) => {
        // Ne pas recreer Contribution si une instance existe deja pour cet onglet :
        // son constructeur rejoue les <script> du panneau (cf. Contribution.js
        // initScript()), ce qui recreerait en double les widgets Dojo qu'ils
        // instancient (ex. FormContributionTabSelector, Phase B, non touche).
        if (this.contributionInstances[tab.id]) {
          this.contributionInstances[tab.id].init();
        } else {
          this.contributionInstances[tab.id] = new Contribution(tab.id);
          this.contributionInstances[tab.id].init();
        }
        new ContributionFormEdit(formType, panelNode);
      });
    },

    wireFieldButtons(panelNode) {
      const selector = 'input[id$="_sel"][type="button"], input[id$="_search"][type="button"], input[id$="_edit"][type="button"]';
      panelNode.querySelectorAll(selector).forEach(node => {
        node.addEventListener("click", () => this.formClicked(node));
      });
    },

    wireDraftFields(panelNode) {
      panelNode.querySelectorAll('input[id*="_is_draft"][type="hidden"]').forEach(node => {
        if (node.value != "1") {
          return;
        }
        const match = /(\w*)_\d+_is_draft$/i.exec(node.id);
        if (!match) {
          return;
        }
        const containerNode = document.getElementById(match[1]);
        if (!containerNode) {
          return;
        }
        const label = containerNode.querySelector("label.etiquette");
        if (label && !document.getElementById(containerNode.id + "_etiquette_draft")) {
          const span = document.createElement("span");
          span.id = containerNode.id + "_etiquette_draft";
          span.innerHTML = "<i>" + this.messages.get("contribution", "onto_contribution_draft_editing") + "</i>";
          label.appendChild(span);
        }
      });
    },

    wireCancelButton(panelNode, tabId) {
      const cancelButton = panelNode.querySelector(".cancel_part > *");
      if (cancelButton) {
        cancelButton.removeAttribute("onclick");
        cancelButton.addEventListener("click", () => this.closeTab(tabId));
      }
    },

    fillIdFinder(originalId) {
      return originalId.replace(/_sel$|_search$|_edit$/, "");
    },

    formClicked(node) {
      if (window.scrollY) {
        this.scrollPosition = window.scrollY;
      }
      this.usedScenario = node.getAttribute("data-linked_scenario");
      const nodeClickedId = this.fillIdFinder(node.id);
      const hiddenValue = document.getElementById(nodeClickedId + "_value");
      let formURL = node.getAttribute("data-form_url");
      if (hiddenValue && hiddenValue.value) {
        const [base, query] = formURL.split("?");
        const params = new URLSearchParams(query);
        params.set("id", hiddenValue.value);
        formURL = base + "?" + params.toString();
      }
      const formTitle = node.getAttribute("data-form_property");
      this.tabSeq += 1;
      const tabId = "contribution_tab_" + this.tabSeq;
      this.openTab({
        id: tabId,
        href: formURL + "&tab_id=" + tabId + "&sub_title=" + formTitle,
        title: formTitle,
        nodeClickedId
      });
    },

    captureFieldBackup(nodeClickedId) {
      if (!nodeClickedId) {
        return {};
      }
      const backup = {};
      const selector = 'input[id^="' + nodeClickedId + '"][type="hidden"], input[id^="' + nodeClickedId + '"][type="text"], select[id^="' + nodeClickedId + '"]';
      document.querySelectorAll(selector).forEach(node => {
        const key = node.id.replace(nodeClickedId + "_", "");
        backup[key] = node.value;
      });
      return backup;
    },

    restoreFieldBackup(tab) {
      if (!tab.nodeClickedId) {
        return;
      }
      Object.keys(tab.backup).forEach(key => {
        const node = document.getElementById(tab.nodeClickedId + "_" + key);
        if (node && node.value !== tab.backup[key]) {
          node.value = tab.backup[key];
        }
      });
    },

    openTab({ id, href, title, nodeClickedId }) {
      const tab = {
        id,
        title,
        html: "",
        closable: true,
        locked: false,
        loaded: false,
        nodeClickedId,
        backup: this.captureFieldBackup(nodeClickedId)
      };
      this.tabs.push(tab);
      this.activeId = id;
      this.setClosableTabs();
      fetch(href, { credentials: "same-origin" })
        .then(response => response.text())
        .then(html => {
          tab.html = html;
          tab.loaded = true;
          this.$nextTick(() => {
            const panelNode = document.getElementById(id);
            this.initializeTabContent(tab, panelNode, { wireCancel: true });
            this.focusPanel(panelNode, title);
          });
        })
        .catch(err => console.error(err));
    },

    onFormLoaded(tabId, formType, uriTemp) {
      // tabId peut designer un identifiant interne au widget de selection Dojo
      // (Phase B, non gere ici) plutot qu'un de nos onglets : dans ce cas on
      // ignore l'evenement, sans repli sur un autre onglet (qui provoquerait
      // une reinitialisation en double de l'onglet en cours de chargement).
      const tab = this.tabs.find(t => t.id === tabId);
      if (!tab) {
        return;
      }
      if (uriTemp && tab.nodeClickedId) {
        this.setFieldValue(tab.nodeClickedId + "_value", uriTemp);
      }
      const panelNode = document.getElementById(tab.id);
      this.initializeTabContent(tab, panelNode, { wireCancel: true, formType });
    },

    setClosableTabs() {
      const lastIndex = this.tabs.length - 1;
      this.tabs.forEach((tab, index) => {
        if (index !== lastIndex) {
          tab.closable = false;
          tab.locked = this.tabs.length > 1;
        } else if (index !== 0) {
          tab.closable = true;
          tab.locked = false;
        } else {
          tab.locked = false;
          if (this.scrollPosition > 0) {
            window.scrollTo(0, this.scrollPosition);
            this.scrollPosition = 0;
          }
        }
      });
      this.applyFieldLocks();
    },

    applyFieldLocks() {
      this.tabs.forEach(tab => {
        const panelNode = document.getElementById(tab.id);
        if (panelNode) {
          panelNode.querySelectorAll("form").forEach(form => this.disableInputForm(form, tab.locked));
        }
      });
    },

    fieldLockHintId(form) {
      const formIdInput = form.querySelector('input[name="form_id"]');
      const baseId = (formIdInput && formIdInput.value) || form.id || "contribution_form";
      return baseId + "_locked_hint";
    },

    // Le message explicatif du verrouillage doit etre porte par aria-describedby
    // (un simple attribut title n'est pas restitue de facon fiable par les
    // lecteurs d'ecran) : un seul span sr-only par formulaire, reference par
    // tous ses champs desactives.
    setFieldLockHint(form, disabled) {
      const hintId = this.fieldLockHintId(form);
      let hint = document.getElementById(hintId);
      if (disabled) {
        if (!hint) {
          hint = document.createElement("span");
          hint.id = hintId;
          hint.className = "sr-only";
          hint.textContent = this.messages.get("contribution", "onto_contribution_input_title");
          form.insertBefore(hint, form.firstChild);
        }
      } else if (hint) {
        hint.remove();
      }
      Array.from(form.elements).forEach(field => {
        if (field.tagName !== "INPUT") {
          return;
        }
        const describedBy = (field.getAttribute("aria-describedby") || "").split(/\s+/).filter(id => id && id !== hintId);
        if (disabled) {
          describedBy.push(hintId);
        }
        if (describedBy.length) {
          field.setAttribute("aria-describedby", describedBy.join(" "));
        } else {
          field.removeAttribute("aria-describedby");
        }
      });
    },

    disableInputForm(form, disabled) {
      Array.from(form.elements).forEach(field => {
        field.disabled = disabled;
      });
      this.setFieldLockHint(form, disabled);
      const zone = form.querySelector("#el0Parent");
      if (!zone) {
        return;
      }
      let status = zone.querySelector(".contribution_overlay_status");
      if (disabled) {
        if (!status && zone.childElementCount === 0) {
          // Paragraphe de statut, pas un titre : ne doit pas casser la
          // hierarchie des titres du document.
          status = document.createElement("p");
          status.className = "overlayLibelle contribution_overlay_status";
          status.setAttribute("role", "status");
          status.setAttribute("aria-live", "polite");
          status.textContent = this.messages.get("contribution", "onto_contribution_form_inprogress");
          zone.appendChild(status);
        }
      } else if (status) {
        status.remove();
      }
    },

    focusPanel(panelNode, title) {
      if (panelNode) {
        panelNode.focus();
      }
      this.announce(this.messages.get("contribution", "contribution_tab_opened").replace("%s", title));
    },

    focusTab(tabId) {
      const button = document.getElementById("tab_" + tabId);
      if (button) {
        button.focus();
      }
    },

    announce(text) {
      this.announcement = "";
      this.$nextTick(() => {
        this.announcement = text;
      });
    },

    selectTab(tabDescriptor) {
      if (tabDescriptor.id === this.activeId) {
        return;
      }
      this.activeId = tabDescriptor.id;
      this.$nextTick(() => this.focusTab(this.activeId));
    },

    requestCloseTab(tabDescriptor) {
      const tab = this.tabs.find(t => t.id === tabDescriptor.id);
      if (!tab) {
        return;
      }
      this.restoreFieldBackup(tab);
      this.closeTab(tab.id);
    },

    closeTab(tabId) {
      const index = this.tabs.findIndex(t => t.id === tabId);
      if (index === -1) {
        return;
      }
      this.removeTabAt(index);
    },

    closeSubTab(tabId) {
      this.closeTab(tabId);
    },

    getIsDraftValue(tab) {
      if (!tab.nodeClickedId) {
        return "0";
      }
      const draftField = document.getElementById(tab.nodeClickedId + "_is_draft");
      return draftField ? draftField.value : "0";
    },

    destroyPanelWidgets(panelNode) {
      window.require(["dijit/registry"], registry => {
        registry.findWidgets(panelNode).forEach(widget => widget.destroyRecursive());
      });
    },

    removeTabAt(index, id) {
      const tab = this.tabs[index];
      const isDraft = this.getIsDraftValue(tab);
      const panelNode = document.getElementById(tab.id);
      if (panelNode) {
        this.destroyPanelWidgets(panelNode);
      }
      this.tabs.splice(index, 1);
      delete this.contributionInstances[tab.id];
      if (this.tabs.length) {
        this.activeId = this.tabs[this.tabs.length - 1].id;
      }
      this.setClosableTabs();
      if (this.tabs.length === 0) {
        window.location.href = "./empr.php?tab=contribution_area&lvl=contribution_area_list&last_id=" + (id || "");
        return;
      }
      const nodeToFill = tab.nodeClickedId;
      if (nodeToFill) {
        const uriNode = document.getElementById(nodeToFill + "_value");
        if (uriNode && uriNode.value && uriNode.value.split("_").includes("temp") && isDraft == 0) {
          uriNode.value = "";
          const draftField = document.getElementById(nodeToFill + "_is_draft");
          if (draftField) {
            draftField.value = "0";
          }
        }
        if (isDraft == 0) {
          const node = document.getElementById(nodeToFill);
          if (node && node.parentNode) {
            node.classList.remove("contribution_draft");
            const draftLabel = document.getElementById(node.parentNode.id + "_etiquette_draft");
            if (draftLabel) {
              draftLabel.remove();
            }
          }
        }
      }
      this.$nextTick(() => this.focusTab(this.activeId));
    },

    // -- integration avec Contribution.js / le selecteur legacy ---------

    fillField(data) {
      const tab = this.tabs.find(t => t.id === data.widgetId);
      const nodeToFill = tab ? tab.nodeClickedId : null;
      if (!nodeToFill) {
        return;
      }
      this.setFieldValue(nodeToFill + "_display_label", data.response.displayLabel);
      this.setFieldValue(nodeToFill + "_value", data.response.uri);
      this.setFieldValue(nodeToFill + "_is_draft", "0");
      window.require(["dijit/registry"], registry => {
        const resourceSelector = registry.byId(nodeToFill + "_display_label");
        if (resourceSelector) {
          resourceSelector.updateTemplate();
        }
      });
    },

    fillFieldDraft(data) {
      const tab = this.tabs.find(t => t.id === data.widgetId);
      const nodeToFill = tab ? tab.nodeClickedId : null;
      if (!nodeToFill) {
        return;
      }
      let displayLabel = this.messages.get("contribution", "contribution_draft_name_js");
      if (typeof data.response.displayLabel !== "undefined") {
        displayLabel = data.response.displayLabel;
      }
      this.getEditUrl(data, true);
      this.setFieldValue(nodeToFill + "_display_label", displayLabel);
      this.setFieldValue(nodeToFill + "_value", data.response.uri);
      this.setFieldValue(nodeToFill + "_is_draft", "1");
      const node = document.getElementById(nodeToFill);
      if (node && node.parentNode) {
        node.classList.add("contribution_draft");
        const label = node.parentNode.querySelector("label.etiquette");
        if (label && !document.getElementById(node.parentNode.id + "_etiquette_draft")) {
          const span = document.createElement("span");
          span.id = node.parentNode.id + "_etiquette_draft";
          span.innerHTML = "<i>" + this.messages.get("contribution", "onto_contribution_draft_editing") + "</i>";
          label.appendChild(span);
        }
      }
      window.require(["dijit/registry"], registry => {
        const resourceSelector = registry.byId(nodeToFill + "_display_label");
        if (resourceSelector) {
          resourceSelector.updateTemplate();
        }
      });
    },

    setFieldValue(id, value) {
      const node = document.getElementById(id);
      if (node) {
        node.value = value;
      }
    },

    getEditUrl(data, isDraft = false) {
      const tab = this.tabs.find(t => t.id === data.widgetId);
      const nodeToFill = tab ? tab.nodeClickedId : null;
      let editButton = null;
      if (nodeToFill) {
        editButton = document.getElementById(nodeToFill + "_edit");
        if (editButton) {
          this.usedScenario = editButton.getAttribute("data-linked_scenario");
        }
      }
      if (!data.response.uri) {
        return;
      }
      const params = new URLSearchParams();
      params.set("selector_data", JSON.stringify({
        entity_uri: data.response.uri,
        scenario: this.usedScenario,
        is_draft: isDraft,
        sub: this.paramsURL.sub
      }));
      fetch("./ajax.php?module=selectors&what=contribution&action=get_edit_url", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params.toString()
      })
        .then(response => response.text())
        .then(responseText => {
          if (!editButton || !nodeToFill) {
            return;
          }
          const selButton = document.getElementById(nodeToFill + "_sel");
          if (responseText) {
            editButton.setAttribute("data-form_url", responseText);
            editButton.type = "button";
            if (selButton) {
              selButton.type = "hidden";
            }
          } else {
            editButton.type = "hidden";
            if (selButton) {
              selButton.type = "button";
            }
          }
        })
        .catch(err => console.error(err));
    },

    removeEditButton(data) {
      const tab = this.tabs.find(t => t.id === data.widgetId);
      const nodeToFill = tab ? tab.nodeClickedId : null;
      if (!nodeToFill) {
        return;
      }
      const editButton = document.getElementById(nodeToFill + "_edit");
      if (editButton) {
        editButton.setAttribute("data-form_url", "");
        editButton.type = "hidden";
      }
      const selButton = document.getElementById(nodeToFill + "_sel");
      if (selButton && selButton.type === "hidden") {
        selButton.type = "button";
      }
    },

    eltClicked(data) {
      if (data.tabId) {
        const tab = this.tabs.find(t => t.id === data.tabId);
        if (tab && !tab.closable) {
          return;
        }
      }
      const type = data.id_authority ? "authority" : (data.type === "bulletin" ? "bulletin" : "record");
      const params = new URLSearchParams();
      params.set("selector_data", JSON.stringify({ type, id: data.id_authority ? data.id_authority : data.id }));
      fetch("./ajax.php?module=selectors&what=contribution&action=save_in_store", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: params.toString()
      })
        .then(response => response.json())
        .then(response => {
          if (typeof unloadOn === "function") {
            unloadOn();
          }
          const lastTab = this.tabs[this.tabs.length - 1];
          if (lastTab && lastTab.closable) {
            window.require(["dojo/topic"], topic => {
              topic.publish("Contribution", "savedForm", { widgetId: lastTab.id, response });
            });
          }
        })
        .catch(err => console.error(err));
    },

    handleEvents(evtType, evtArgs) {
      switch (evtType) {
        case "savedForm":
          this.fillField(evtArgs);
          this.closeTab(evtArgs.widgetId, evtArgs.response.id);
          this.getEditUrl(evtArgs);
          break;
        case "addEventOnButton":
          if (evtArgs.node) {
            this.wireFieldButtons(evtArgs.node.closest('[role="tabpanel"]') || evtArgs.node);
          }
          break;
        case "eltClicked":
          this.eltClicked(evtArgs);
          break;
        case "formLoaded":
          this.onFormLoaded(evtArgs.tabId, evtArgs.formType, evtArgs.uriTemp);
          break;
        case "savedSubDraftForm":
          this.fillFieldDraft(evtArgs);
          break;
        case "removeSubContrib":
          this.fillField(evtArgs);
          this.closeTab(evtArgs.widgetId, evtArgs.response.id);
          this.removeEditButton(evtArgs);
          if (typeof unloadOn === "function") {
            unloadOn();
          }
          break;
        case "closeForm":
          this.closeSubTab(evtArgs.widgetId);
          break;
      }
    }
  }
}
</script>
