<template>
  <div class="header">
    <div class="logo"></div>
    <h1 class="appname">BDLG planner</h1>
    <nav class="tabs floating">
      <router-link class="tab" active-class="selected" to="/"> Accueil </router-link>
      <router-link class="tab" active-class="selected" to="/evenement">Planning</router-link>
      <router-link class="tab" active-class="selected" to="/competences">
        Gestion des compétences
      </router-link>
      <router-link class="tab" active-class="selected" to="/benevoles">
        Gestion des bénévoles
      </router-link>
      <router-link class="tab" active-class="selected" to="/planningIndividuel">
        Planning Individuel
      </router-link>
    </nav>
  </div>
  <div class="container">
    <router-view
      @export="exportStateToJson"
      @import="(e) => importJsonState(e, false)"
      @localSave="localSave"
      @solve="solve"
    />
    <div v-if="optimisationInProgress" class="modal">
      <div class="modal-content">
        <h3>Optimisation en cours</h3>
        <p>L'ordinateur calcul un meilleur planning</p>
        <div class="spinner"><span class="material-icons"> sync </span></div>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
import toast from "./utils/Toast";
import "@/assets/css/tabs.css";
import Evenement from "./models/Evenement";
import Benevole from "./models/Benevole";
import Creneau from "./models/Creneau";
import Competence from "./models/Competence";
import { SolverInput, SolverOutput } from "./models/SolverInput";
import Ressource, { IRessource, RessourceJSON } from "jc-timeline/lib/Ressource";
import { MutationTypes } from "./store/Mutations";
import dayjs from "dayjs";
import { StateJSON } from "./store/State";
const keyofEvent: Array<keyof Evenement> = ["name", "uuid", "start", "end"];
export default defineComponent({
  data() {
    return { optimisationInProgress: false };
  },
  mounted() {
    const previousState = window.localStorage.getItem("activeState");
    if (previousState) {
      this.importJsonState(JSON.parse(previousState), false);
    }
    window.onbeforeunload = () => {
      this.localSave();
    };
  },
  methods: {
    localSave() {
      window.localStorage.setItem("activeState", JSON.stringify(this.$store.getters.getJSONState));
    },
    exportStateToJson(): void {
      const obj: StateJSON = this.$store.getters.getJSONState;
      const mimeType = "data:text/json;charset=utf-8";
      const dummy = document.createElement("a");
      dummy.href = mimeType + ", " + encodeURI(JSON.stringify(obj));
      dummy.download =
        "Planning-" + this.$store.state.evenement.name + dayjs().format("-YYYY-MM-DD[.json]");
      document.body.appendChild(dummy);
      dummy.click();
      setTimeout(() => document.body.removeChild(dummy), 10000);
    },
    solve() {
      const meals = this.$store.state.creneauList.filter((o) => o.isMeal);
      const slots = this.$store.state.creneauList.filter((o) => !o.isMeal);
      const timeslots = slots.map((o) => o.toTimeslotRaw());
      const mealSlots = meals.map((o) => o.toMealSlot());
      const volonteerList = this.$store.state.benevoleList.map((b) => b.toVolonteeRaw());
      const constraints = this.$store.state.competenceList.map((c) => c.toSkill());
      const getAssignementPair = (c: Creneau) =>
        c.benevoleIdList.map((id) => {
          return { volonteerId: id, slotId: c.id };
        });
      const assignements = slots.flatMap(getAssignementPair);
      const mealAssignements = meals.flatMap(getAssignementPair);

      const body: SolverInput = {
        constraints,
        timeslots,
        mealSlots,
        volonteerList,
        assignements,
        mealAssignements,
      };
      const options = {
        method: "POST",
        body: JSON.stringify(body),
        headers: { "Content-type": "application/json; charset=UTF-8" },
      };
      this.optimisationInProgress = true;
      fetch(`http://localhost:8080/planning/solve`, options)
        .then((response) => response.json())
        .then(this.updatePlanningWithNewPairing)
        .catch((error) => {
          toast({ html: "Pas de réponse du serveur<br>" + error.toString(), classes: "error" });
          this.optimisationInProgress = false;
        });
    },
    updatePlanningWithNewPairing(data: SolverOutput) {
      this.optimisationInProgress = false;
      // Display message from the solver
      toast({
        html: "Le planning a été mis à jour <br>Score : " + data.score,
        classes: "success",
        displayLength: 10000,
      });
      data.message
        .split("\n")
        .filter((s) => s != "")
        .forEach((s) => toast({ html: s, classes: "warning", displayLength: 10000 }));
      // Remove previous timeslot assignement
      this.$store.commit(MutationTypes.clearBenevole2Creneau, undefined);
      // Add new timeslot assignement
      for (const pair of data.assignements) {
        this.$store.commit(MutationTypes.addBenevole2Creneau, {
          creneauId: pair.slotId,
          benevoleId: pair.volonteerId,
        });
      }
    },
    importJsonState(obj: StateJSON, preserve = false) {
      console.log(obj);
      // Remove previous content and load the main event title
      if (preserve == false) {
        this.$store.commit(MutationTypes.resetState, undefined);
        const e = Evenement.fromJSON(obj.evenement);
        for (const k of keyofEvent) {
          this.$store.commit(MutationTypes.editEvenement, {
            field: k,
            value: e[k],
          });
        }
      }
      // Import constraint
      obj.competences.forEach((c) => {
        this.$store.commit(MutationTypes.addConstraint, Competence.fromJSON(c));
      });
      // Import Benevoles
      obj.benevoles.forEach((b) => {
        this.$store.commit(MutationTypes.addBenevole, Benevole.fromJSON(b));
      });
      // Import creneau group
      const dict: { [k: string]: RessourceJSON } = {};
      obj.creneauGroups.forEach((element) => {
        dict[element.id] = element;
      });
      // map parent to children
      const creneauGroups = obj.creneauGroups.map((ressource) => {
        const iRessource: IRessource = { ...ressource };
        if (iRessource.parentId) {
          if (iRessource.parentId in dict) {
            iRessource.parent = dict[iRessource.parentId];
          } else {
            throw new Error("Missing parent of creneau group : " + ressource.id);
          }
        }
        return iRessource;
      });
      // Push the items to this application
      creneauGroups.forEach((r) => {
        this.$store.commit(MutationTypes.addCreneauGroup, new Ressource(r));
      });
      // Import Creneau
      for (let c of obj.creneaux) {
        const creneau = Creneau.fromJSON(c);
        this.$store.commit(MutationTypes.addCreneau, creneau);
        // add the creneau to the corresponding benevole
        for (const id of creneau.benevoleIdList) {
          const b = this.$store.getters.getBenevoleById(id);
          if (b) {
            this.$store.commit(MutationTypes.editBenevole, {
              id: id,
              field: "creneauIdList",
              value: Array.from(new Set([...b.creneauIdList, creneau.id])),
            });
          } else {
            throw new Error(`The benevole ${id} was not find `);
          }
        }
      }
    },
  },
});
</script>

<style>
.modal {
  display: flex;
  justify-content: center;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 100;
}
.modal-content {
  background: white;
  padding: 16px 32px;
  margin-top: 100px;
  height: min-content;
  border-radius: 2px;
}
.spinner {
  color: var(--color-accent-400);
  text-align: center;
}
.spinner > .material-icons {
  font-size: 3rem;
  animation: spin 2s linear infinite;
}
@keyframes spin {
  0% {
    transform: rotateZ(360deg);
  }
  100% {
    transform: rotateZ(0deg);
  }
}
</style>
