<template>
  <div id="visgraph" />

  <teleport to="#visgraph-legend">
    <div class="post-menu">
      <div class="list-group mt-4">
        <button
          type="button"
          class="list-group-item list-group-item-action btn btn-misc"
          @click="createRelationship"
        >
          <i class="fa fa-plus" /> {{ $t("visgraph.add_link") }}
        </button>
        <a
          type="button"
          class="list-group-item list-group-item-action btn btn-misc"
          id="exportCanvasBtn"
          @click="exportCanvasAsImage"
        >
          <i class="fa fa-camera fa-fw" />
          {{ $t("visgraph.screenshot") }}
        </a>
      </div>

      <div v-if="displayHelpMessage" class="alert alert-info mt-3">
        {{ $t("visgraph.create_link_help") }}
      </div>

      <div class="my-4 legend">
        <h3>{{ $t("visgraph.Legend") }}</h3>
        <div class="list-group">
          <label
            class="list-group-item"
            v-for="(layer, i) in legendLayers"
            :key="`layer-${i}`"
          >
            <input
              class="form-check-input me-1"
              type="checkbox"
              :value="layer.id"
              v-model="checkedLayers"
              @change="toggleLayer"
            />
            {{ layer.label }}
          </label>
        </div>
      </div>
    </div>
  </teleport>

  <teleport to="body">
    <modal
      v-if="modal.showModal"
      :modal-dialog-class="modal.modalDialogClass"
      @close="modal.showModal = false"
    >
      <template #header>
        <h2 class="modal-title">
          {{ $t(modal.title) }}
        </h2>
        <!-- {{ modal.data.id }} -->
      </template>
      <template #body>
        <div v-if="modal.action === 'delete'">
          <p>{{ $t("visgraph.delete_confirmation_text") }}</p>
        </div>
        <div v-else>
          <form>
            <div class="row">
              <div class="col-12 text-center">
                {{ $t("visgraph.between") }}<br />{{ $t("visgraph.and") }}
              </div>
              <div class="col">
                <small>{{ getPersonAge(modal.data.from) }}</small>
                <h4>{{ getPerson(modal.data.from).text }}</h4>
                <p class="text-start" v-if="relation && relation.title">
                  <span v-if="reverse">
                    {{
                      $t("visgraph.relation_from_to_like", [
                        getPerson(modal.data.from).text,
                        getPerson(modal.data.to).text,
                        relation.reverseTitle.fr.toLowerCase(),
                      ])
                    }}
                  </span>
                  <span v-else>
                    {{
                      $t("visgraph.relation_from_to_like", [
                        getPerson(modal.data.from).text,
                        getPerson(modal.data.to).text,
                        relation.title.fr.toLowerCase(),
                      ])
                    }}
                  </span>
                </p>
              </div>
              <div class="col text-end">
                <small>{{ getPersonAge(modal.data.to) }}</small>
                <h4>{{ getPerson(modal.data.to).text }}</h4>
                <p class="text-end" v-if="relation && relation.title">
                  <span v-if="reverse">
                    {{
                      $t("visgraph.relation_from_to_like", [
                        getPerson(modal.data.to).text,
                        getPerson(modal.data.from).text,
                        relation.title.fr.toLowerCase(),
                      ])
                    }}
                  </span>
                  <span v-else>
                    {{
                      $t("visgraph.relation_from_to_like", [
                        getPerson(modal.data.to).text,
                        getPerson(modal.data.from).text,
                        relation.reverseTitle.fr.toLowerCase(),
                      ])
                    }}
                  </span>
                </p>
              </div>
            </div>
            <div class="my-3">
              <VueMultiselect
                id="relation"
                label="title"
                track-by="id"
                :custom-label="customLabel"
                :placeholder="$t('visgraph.choose_relation')"
                :close-on-select="true"
                :multiple="false"
                :searchable="true"
                :options="relations"
                v-model="relation"
                :value="relation"
              />
            </div>
            <div class="form-check form-switch">
              <input
                class="form-check-input"
                type="checkbox"
                id="reverse"
                v-model="reverse"
              />
              <label class="form-check-label" for="reverse">{{
                $t("visgraph.reverse_relation")
              }}</label>
            </div>
          </form>
        </div>
      </template>
      <template #footer>
        <button
          class="btn"
          :class="modal.button.class"
          @click="submitRelationship"
        >
          {{ $t(modal.button.text) }}
        </button>
        <button
          class="btn btn-delete"
          v-if="modal.action === 'edit'"
          @click="dropRelationship"
        />
      </template>
    </modal>
  </teleport>
  <ul class="record_actions sticky-form-buttons">
    <li>
      <add-persons
        button-title="visgraph.add_person"
        modal-title="visgraph.add_person"
        :key="addPersons.key"
        :options="addPersons.options"
        @add-new-persons="addNewPersons"
        ref="addPersons"
      />
    </li>
  </ul>
</template>

<script>
import vis from "vis-network/dist/vis-network";
import { mapState, mapGetters } from "vuex";
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import VueMultiselect from "vue-multiselect";
import {
  getRelationsList,
  postRelationship,
  patchRelationship,
  deleteRelationship,
} from "./api";
import { splitId, getAge } from "./vis-network";
import { visMessages } from "./i18n";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";

export default {
  name: "App",
  components: {
    Modal,
    VueMultiselect,
    AddPersons,
  },
  props: ["household_id"],
  data() {
    return {
      container: "",
      checkedLayers: [],
      relations: [],
      displayHelpMessage: false,
      listenPersonFlag: "normal",
      newEdgeData: {},
      modal: {
        showModal: false,
        modalDialogClass: "modal-md",
        title: null,
        action: null,
        data: {
          type: "relationship",
          from: null,
          to: null,
          relation: null,
          reverse: false,
        },
        button: {
          class: null,
          text: null,
        },
      },
      canvas: null,
      link: null,
      addPersons: {
        key: "filiation",
        options: {
          type: ["person"],
          priority: null,
          uniq: false,
        },
      },
    };
  },
  computed: {
    ...mapGetters([
      "nodes",
      "edges",
      // not used 'isInWhitelist', 'isHouseholdLoading', 'isCourseLoaded', 'isRelationshipLoaded', 'isPersonLoaded', 'isExcludedNode', 'countLinksByNode', 'getParticipationsByCourse', 'getMembersByHousehold', 'getPersonsGroup',
    ]),
    ...mapState([
      "persons",
      "households",
      "courses",
      "excludedNodesIds",
      "updateHack",
      // not used 'links', 'relationships', 'whitelistIds', 'personLoadedIds', 'householdLoadingIds', 'courseLoadedIds', 'relationshipLoadedIds',
    ]),

    visgraph_data() {
      //console.log('::: visgraph_data :::', this.nodes.length, 'nodes,', this.edges.length, 'edges')
      return {
        nodes: this.nodes,
        edges: this.edges,
      };
    },

    refreshNetwork() {
      //console.log('--- refresh network')
      window.network.setData(this.visgraph_data);

      return 1;
    },

    legendLayers() {
      //console.log('--- refresh legend and rebuild checked Layers')
      // eslint-disable-next-line vue/no-side-effects-in-computed-properties
      this.checkedLayers = [];
      let layersDisplayed = [
        ...this.nodes.filter((n) => n.id.startsWith("household")),
        ...this.nodes.filter((n) => n.id.startsWith("accompanying")),
      ];
      layersDisplayed.forEach((layer) => {
        this.checkedLayers.push(layer.id);
      });
      return [...this.households, ...this.courses];
    },

    // eslint-disable-next-line vue/no-dupe-keys
    checkedLayers() {
      // required to refresh data checkedLayers
      //console.log('--- checkedLayers')
      return this.checkedLayers;
    },

    relation: {
      get() {
        return this.modal.data.relation;
      },
      set(value) {
        this.modal.data.relation = value;
      },
    },

    reverse: {
      get() {
        return this.modal.data.reverse;
      },
      set(value) {
        this.modal.data.reverse = value;
      },
    },
  },
  watch: {
    updateHack(newValue, oldValue) {
      //console.log(`--- updateHack ${oldValue} <> ${newValue}`)
      if (oldValue !== newValue) {
        this.forceUpdateComponent();
      }
    },
  },
  mounted() {
    //console.log('=== mounted: init graph')
    this.initGraph();
    this.listenOnGraph();
    this.getRelationsList();
    console.log(this.persons);

    this.canvas = document.getElementById("visgraph").querySelector("canvas");
    this.link = document.getElementById("exportCanvasBtn");
  },
  methods: {
    addNewPersons({ selected, modal }) {
      // console.log('@@@ CLICK button addNewPersons', selected);
      selected.forEach(function (item) {
        this.$store
          .dispatch("addMorePerson", item.result)
          .catch(({ name, violations }) => {
            if (name === "ValidationException" || name === "AccessException") {
              violations.forEach((violation) =>
                this.$toast.open({ message: violation }),
              );
            } else {
              this.$toast.open({ message: violations });
            }
          });
      }, this);
      this.$refs.addPersons.resetSearch(); // to cast child method
      modal.showModal = false;
    },
    initGraph() {
      this.container = document.getElementById("visgraph");
      // Instanciate vis objects in separate window variables, see vis-network.js
      window.network = new vis.Network(
        this.container,
        this.visgraph_data,
        window.options,
      );
    },
    forceUpdateComponent() {
      //console.log('!! forceUpdateComponent !!')
      // eslint-disable-next-line @typescript-eslint/no-unused-expressions
      this.refreshNetwork;
      this.$forceUpdate();
    },

    // events
    listenOnGraph() {
      window.network.on("selectNode", (data) => {
        if (data.nodes.length > 1) {
          throw "Multi selection is not allowed. Disable it in options.interaction !";
        }
        let node = data.nodes[0];
        let nodeType = splitId(node, "type");
        switch (nodeType) {
          case "person":
            let person = this.nodes.filter((n) => n.id === node)[0];
            //console.log('@@@@@@ event on selected Node', person.id)
            if (this.listenPersonFlag === "normal") {
              if (person.folded === true) {
                //console.log('   @@> expand mode event')
                this.$store.commit("unfoldPerson", person);
                this.$store.dispatch("fetchInfoForPerson", person);
              }
            } else {
              //console.log('   @@> create link mode event')
              this.listenStepsToAddRelationship(person);
            }
            break;

          case "household":
            let household = this.nodes.filter((n) => n.id === node)[0];
            //console.log('@@@@@@ event on selected Node', household.id)
            this.$store.dispatch("unfoldPersonsByHousehold", household);
            break;

          case "accompanying_period":
            let course = this.nodes.filter((n) => n.id === node)[0];
            //console.log('@@@@@@ event on selected Node', course.id)
            this.$store.dispatch("unfoldPersonsByCourse", course);
            break;

          default:
            throw "event is undefined for this type of node";
        }
        this.forceUpdateComponent();
      });
      window.network.on("selectEdge", (data) => {
        if (data.nodes.length !== 0 || data.edges.length !== 1) {
          return false; //we don't want to trigger nodeEdge or multiselect !
        }
        let link = data.edges[0];
        let linkType = splitId(link, "link");
        //console.log('@@@@@ event on selected Edge', data.edges.length, linkType, data)

        if (linkType.startsWith("relationship")) {
          //console.log('linkType relationship')

          let relationships = this.edges.filter((l) => l.id === link);
          if (relationships.length > 1) {
            throw "error: only one link is allowed between two person!";
          }

          let relationship = relationships[0];
          //console.log(relationship)

          this.editRelationshipModal({
            from: relationship.from,
            to: relationship.to,
            id: relationship.id,
            relation: relationship.relation,
            reverse: relationship.reverse,
          });
        }
      });
    },
    listenStepsToAddRelationship(person) {
      //console.log('   @@> listenStep', this.listenPersonFlag)
      if (this.listenPersonFlag === "step2") {
        //console.log('   @@> person 2', person)
        this.newEdgeData.to = person.id;
        this.addRelationshipModal(this.newEdgeData);
        this.displayHelpMessage = false;
        this.listenPersonFlag = "normal";
        this.newEdgeData = {};
      }
      if (this.listenPersonFlag === "step1") {
        //console.log('   @@> person 1', person)
        this.newEdgeData.from = person.id;
        this.listenPersonFlag = "step2";
      }
    },

    /// control Layers
    toggleLayer(value) {
      let id = value.target.value;
      //console.log('@@@@@@ toggle Layer', id)
      this.forceUpdateComponent();
      if (this.checkedLayers.includes(id)) {
        this.removeLayer(id);
      } else {
        this.addLayer(id);
      }
    },
    addLayer(id) {
      //console.log('+ addLayer', id)
      this.checkedLayers.push(id);
      this.$store.dispatch("excludedNode", ["remove", id]);
    },
    removeLayer(id) {
      //console.log('- removeLayer', id)
      this.checkedLayers = this.checkedLayers.filter((i) => i !== id);
      this.$store.dispatch("excludedNode", ["add", id]);
    },

    /// control Modal
    addRelationshipModal(edgeData) {
      //console.log('==- addRelationshipModal', edgeData)
      this.modal = {
        data: { from: edgeData.from, to: edgeData.to, reverse: false },
        action: "create",
        showModal: true,
        title: "visgraph.add_relationship_link",
        button: { class: "btn-create", text: "action.create" },
      };
    },
    editRelationshipModal(edgeData) {
      //console.log('==- editRelationshipModal', edgeData)
      this.modal = {
        data: edgeData,
        action: "edit",
        showModal: true,
        title: "visgraph.edit_relationship_link",
        button: { class: "btn-edit", text: "action.edit" },
      };
    },

    // form
    resetForm() {
      this.modal = {
        data: {
          type: "relationship",
          from: null,
          to: null,
          relation: null,
          reverse: false,
        },
        action: null,
        title: null,
        button: { class: null, text: null },
      };
      //console.log('==- reset Form', this.modal.data)
    },
    getRelationsList() {
      //console.log('fetch relationsList')
      return getRelationsList()
        .then(
          (relations) =>
            new Promise((resolve) => {
              //console.log('+ relations list', relations.results.length)
              this.relations = relations.results.filter(
                (r) => r.isActive === true,
              );
              resolve();
            }),
        )
        .catch();
    },
    customLabel(value) {
      //console.log('customLabel', value)
      return value.title && value.reverseTitle
        ? `${value.title.fr} ↔ ${value.reverseTitle.fr}`
        : "";
    },
    getPerson(id) {
      let person = this.persons.filter((p) => p.id === id);
      return person[0];
    },
    getPersonAge(id) {
      let person = this.getPerson(id);
      return getAge(person);
    },

    // actions
    createRelationship() {
      this.displayHelpMessage = true;
      this.listenPersonFlag = "step1"; // toggle listener in create link mode
      //console.log('   @@> switch listener to create link mode:', this.listenPersonFlag)
    },
    dropRelationship() {
      //console.log('delete', this.modal.data)
      deleteRelationship(this.modal.data).catch();
      this.$store.commit("removeLink", this.modal.data.id);
      this.modal.showModal = false;
      this.resetForm();
      this.forceUpdateComponent();
    },
    submitRelationship() {
      //console.log('submitRelationship', this.modal.action)
      switch (this.modal.action) {
        case "create":
          return postRelationship(this.modal.data)
            .then(
              (relationship) =>
                new Promise((resolve) => {
                  //console.log('post relationship response', relationship)
                  this.$store.dispatch("addLinkFromRelationship", relationship);
                  this.modal.showModal = false;
                  this.resetForm();
                  this.forceUpdateComponent();
                  resolve();
                }),
            )
            .catch((error) => {
              if (error.name === "ValidationException") {
                for (let v of error.violations) {
                  this.$toast.open({ message: v });
                  console.log(v);
                }
              } else {
                this.$toast.open({
                  message: "An error occurred",
                });
              }
            });

        case "edit":
          return patchRelationship(this.modal.data)
            .then(
              (relationship) =>
                new Promise((resolve) => {
                  //console.log('patch relationship response', relationship)
                  this.$store.commit("updateLink", relationship);
                  this.modal.showModal = false;
                  this.resetForm();
                  this.forceUpdateComponent();
                  resolve();
                }),
            )
            .catch();

        default:
          throw "uncaught action";
      }
    },

    // export image
    async exportCanvasAsImage() {
      let filename = `filiation_${this.household_id}.jpg`,
        mime = "image/jpeg",
        quality = 0.85,
        footer = `© Chill ${new Date().getFullYear()}`,
        timestamp = `${visMessages.fr.visgraph.relationship_household} n° ${this.household_id} — ${new Date().toLocaleString()}`;

      // resolve toBlob in a Promise
      const getCanvasBlob = (canvas) =>
        new Promise((resolve) => {
          canvas.toBlob((blob) => resolve(blob), mime, quality);
        });

      // build image from new temporary canvas
      let tmpCanvas = document.createElement("canvas");
      tmpCanvas.width = this.canvas.width;
      tmpCanvas.height = this.canvas.height;

      let ctx = tmpCanvas.getContext("2d");
      ctx.beginPath();
      ctx.fillStyle = "#fff";
      ctx.fillRect(0, 0, tmpCanvas.width, tmpCanvas.height);
      ctx.fillStyle = "#9d4600";
      ctx.fillText(footer + " — " + timestamp, 5, tmpCanvas.height - 10);
      ctx.drawImage(this.canvas, 0, 0);

      return await getCanvasBlob(tmpCanvas).then((blob) => {
        let url = document.createElement("a");
        url.download = filename;
        url.href = window.URL.createObjectURL(blob);
        url.click();
        console.log("url", url.href);
        URL.revokeObjectURL(url.href);
      });
    },
  },
};
</script>

<style src="vis-network/dist/dist/vis-network.min.css"></style>
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

<style lang="scss" scoped>
div#visgraph {
  height: 700px;
  margin: auto;
}
div#visgraph-legend {
  div.post-menu.legend {
  }
}
.modal-mask {
  background-color: rgba(0, 0, 0, 0.25);
}
.debug {
  margin: 1em;
  padding: 1em;
  color: dimgray;
  font-style: italic;
  font-size: 80%;
}
</style>
