/* -------------------------------------------- */
import { RdDCalendrierEditeur } from "./rdd-calendrier-editeur.js";
import { RdDAstrologieEditeur } from "./rdd-astrologie-editeur.js";
import { HtmlUtility } from "./html-utility.js";
import { RdDResolutionTable } from "./rdd-resolution-table.js";
import { RdDUtility } from "./rdd-utility.js";
import { Grammar } from "./grammar.js";
import { RdDDice } from "./rdd-dice.js";
import { Misc } from "./misc.js";

/* -------------------------------------------- */
const dossierIconesHeures = 'systems/foundryvtt-reve-de-dragon/icons/heures/'
const heuresList = ["vaisseau", "sirene", "faucon", "couronne", "dragon", "epees", "lyre", "serpent", "poissonacrobate", "araignee", "roseau", "chateaudormant"];
const heuresDef = {
  "vaisseau": { label: "Vaisseau", lettreFont: 'v', saison: "printemps", heure: 0, icon: 'hd01.svg' },
  "sirene": { label: "Sirène", lettreFont: 'i', saison: "printemps", heure: 1, icon: 'hd02.svg' },
  "faucon": { label: "Faucon", lettreFont: 'f', saison: "printemps", heure: 2, icon: 'hd03.svg' },
  "couronne": { label: "Couronne", lettreFont: '', saison: "ete", heure: 3, icon: 'hd04.svg' },
  "dragon": { label: "Dragon", lettreFont: 'd', saison: "ete", heure: 4, icon: 'hd05.svg' },
  "epees": { label: "Epées", lettreFont: 'e', saison: "ete", heure: 5, icon: 'hd06.svg' },
  "lyre": { label: "Lyre", lettreFont: 'l', saison: "automne", heure: 6, icon: 'hd07.svg' },
  "serpent": { label: "Serpent", lettreFont: 's', saison: "automne", heure: 7, icon: 'hd08.svg' },
  "poissonacrobate": { label: "Poisson Acrobate", lettreFont: 'p', saison: "automne", heure: 8, icon: 'hd09.svg' },
  "araignee": { label: "Araignée", lettreFont: 'a', saison: "hiver", heure: 9, icon: 'hd10.svg' },
  "roseau": { label: "Roseau", lettreFont: 'r', saison: "hiver", heure: 10, icon: 'hd11.svg' },
  "chateaudormant": { label: "Château Dormant", lettreFont: 'c', saison: "hiver", heure: 11, icon: 'hd12.svg' }
};
const saisonsDef = {
  "printemps": { label: "Printemps" },
  "ete": { label: "Eté" },
  "automne": { label: "Automne" },
  "hiver": { label: "Hiver" }
};

const RDD_MOIS_PAR_AN = 12;
export const RDD_JOUR_PAR_MOIS = 28;
const RDD_HEURES_PAR_JOUR = 12;
const RDD_MINUTES_PAR_HEURES = 120;
const MAX_NOMBRE_ASTRAL = 12;

/* -------------------------------------------- */
export class RdDCalendrier extends Application {

  static createCalendrierPos() {
    return { top: 200, left: 200 };
  }

  static getDefSigne(moisRdD) {
    moisRdD = moisRdD % RDD_MOIS_PAR_AN;
    return Object.values(heuresDef).find(h => h.heure == moisRdD);
  }

  static getCalendrier(index) {
    let calendrier = {
      heureRdD: 0, // Index dans heuresList / heuresDef[x].heure
      minutesRelative: 0,
      indexJour: index,
      annee: Math.floor(index / (RDD_JOUR_PAR_MOIS * RDD_MOIS_PAR_AN)),
      moisRdD: Math.floor(index / RDD_JOUR_PAR_MOIS) % RDD_MOIS_PAR_AN,
      jour: (index % RDD_JOUR_PAR_MOIS) // Le calendrier stocke le jour en 0-27, mais en 1-28 à l'affichage
    }
    calendrier.moisLabel = RdDCalendrier.getDefSigne(calendrier.moisRdD).label;
    return calendrier;
  }

  /* -------------------------------------------- */
  async initCalendrier() {
    // Calendrier
    this.calendrier = duplicate(game.settings.get("foundryvtt-reve-de-dragon", "calendrier"));
    this.calendrier.annee = this.calendrier.annee ?? (this.calendrier.moisRdD == RDD_MOIS_PAR_AN ? 1 : 0);
    this.calendrier.moisRdD = (this.calendrier.moisRdD ?? 0) % RDD_MOIS_PAR_AN;

    //console.log("CALENDRIER", this.calendrier);
    if (this.calendrier == undefined || this.calendrier.moisRdD == undefined) {
      this.calendrier = RdDCalendrier.getCalendrier(0);
      if (game.user.isGM) { // Uniquement si GM
        game.settings.set("foundryvtt-reve-de-dragon", "calendrier", this.calendrier);
      }
    }
    // position
    this.calendrierPos = duplicate(game.settings.get("foundryvtt-reve-de-dragon", "calendrier-pos"));
    if (this.calendrierPos == undefined || this.calendrierPos.top == undefined) {
      this.calendrierPos = RdDCalendrier.createCalendrierPos();
      if (game.user.isGM) { // Uniquement si GM
        game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", this.calendrierPos);
      }
    }
    // nombre astral
    if (game.user.isGM) {
      this.listeNombreAstral = this._loadListNombreAstral();
      await this.rebuildListeNombreAstral(false); // Ensure always up-to-date
    }
    console.log(this.calendrier, this.calendrierPos, this.listeNombreAstral);
  }

  /* -------------------------------------------- */
  _loadListNombreAstral() {
    const listeNombreAstraux = game.settings.get("foundryvtt-reve-de-dragon", "liste-nombre-astral");
    return listeNombreAstraux ?? [];
  }

  /* -------------------------------------------- */
  static get defaultOptions() {
    const options = super.defaultOptions;
    options.template = "systems/foundryvtt-reve-de-dragon/templates/calendar-template.html";
    options.popOut = false;
    options.resizable = false;
    return options;
  }

  /* -------------------------------------------- */
  getDateFromIndex(index) {
    const date = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
    return (date.jour+1) + ' ' + heuresDef[heuresList[date.moisRdD]].label;
  }

  /* -------------------------------------------- */
  getNumericDateFromIndex(index = undefined) {
    const dateRdD = RdDCalendrier.getCalendrier(index ?? this.getCurrentDayIndex());
    return {
      day: dateRdD.jour + 1,
      month: heuresList[dateRdD.moisRdD]
    }
  }

  /* -------------------------------------------- */
  getCurrentHeure() {
    return heuresList[this.calendrier.heureRdD];
  }

  /* -------------------------------------------- */
  getCurrentDayIndex() {
    return (((this.calendrier.annee ?? 0) * RDD_MOIS_PAR_AN + (this.calendrier.moisRdD ?? 0)) * RDD_JOUR_PAR_MOIS) + (this.calendrier.jour ?? 0);
  }

  /* -------------------------------------------- */
  getIndexFromDate(jour, mois) {
    return (heuresDef[mois].heure * RDD_JOUR_PAR_MOIS) + jour - 1;
  }
  /* -------------------------------------------- */
  getJoursSuivants(num) {
    let jours = [];
    let index = this.getCurrentDayIndex();
    for (let i = 0; i < num; i++) {
      jours[i] = { label: this.getDateFromIndex(index + i), index: index + i };
    }
    return jours;
  }

  /* -------------------------------------------- */
  async ajouterNombreAstral(index, showDice = true) {
    const nombreAstral = await RdDDice.rollTotal("1dh", { showDice: showDice, hideDice: !showDice, rollMode: "selfroll" });
    const dateFuture = this.getDateFromIndex(index);
    if (showDice) {
        ChatMessage.create({
        whisper: ChatMessage.getWhisperRecipients("GM"),
        content: `Le chiffre astrologique du ${dateFuture} sera le ${nombreAstral}`
      });
    }
    return {
      nombreAstral: nombreAstral,
      valeursFausses: [],
      index: index
    }
  }

  /* -------------------------------------------- */
  getCurrentNombreAstral() {
    let indexDate = this.getCurrentDayIndex();
    return this.getNombreAstral(indexDate);
  }

  /* -------------------------------------------- */
  resetNombreAstral() {
    this.listeNombreAstral = [];
    game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);

    game.socket.emit("system.foundryvtt-reve-de-dragon", {
      msg: "msg_reset_nombre_astral",
      data: {}
    });
  }

  /* -------------------------------------------- */
  getNombreAstral(indexDate) {
    if (this.listeNombreAstral == undefined) {
      this.listeNombreAstral = this._loadListNombreAstral() || [];
    }
    let liste = this.listeNombreAstral;
    if ( game.user.isGM ) {
      if ( typeof(liste) != 'Array' || liste.length == 0 ) {
        this.rebuildListeNombreAstral();
        liste = this.listeNombreAstral;
      }
    }
    let astralData = liste.find((nombreAstral, i) => nombreAstral.index == indexDate);

    return astralData?.nombreAstral;
  }

  /* -------------------------------------------- */
  async rebuildListeNombreAstral(showDice = true) {
    if (game.user.isGM) {
      let jourCourant = this.getCurrentDayIndex();

      let newList = [];
      for (let i = 0; i < MAX_NOMBRE_ASTRAL; i++) {
        let dayIndex = jourCourant + i;
        let na = this.listeNombreAstral.find(n => n.index == dayIndex);
        if (na) {
          newList[i] = duplicate(na);
        } else {
          newList[i] = await this.ajouterNombreAstral(dayIndex, showDice );
        }
      }
      //console.log("SAVE list", newList, jourCourant);
      this.listeNombreAstral = newList;
      game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);
    }
  }

  /* -------------------------------------------- */
  async onCalendarButton(ev) {
    ev.preventDefault();
    const calendarAvance = ev.currentTarget.attributes['data-calendar-avance'];
    const calendarSet = ev.currentTarget.attributes['data-calendar-set'];
    if (calendarAvance) {
      await this.incrementTime(Number(calendarAvance.value));
    }
    else if (calendarSet) {
      this.positionnerHeure(Number(calendarSet.value));
    }
    this.updateDisplay();
  }

  /* -------------------------------------------- */
  async incrementTime(minutes = 0) {
    this.calendrier.minutesRelative += minutes;
    if (this.calendrier.minutesRelative >= RDD_MINUTES_PAR_HEURES) {
      this.calendrier.minutesRelative -= RDD_MINUTES_PAR_HEURES;
      this.calendrier.heureRdD += 1;
    }
    if (this.calendrier.heureRdD >= RDD_HEURES_PAR_JOUR) {
      this.calendrier.heureRdD -= RDD_HEURES_PAR_JOUR;
      this.incrementerJour();
    }
    game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));
    // Notification aux joueurs
    game.socket.emit("system.foundryvtt-reve-de-dragon", {
      msg: "msg_sync_time",
      data: duplicate(this.calendrier)
    });
  }

  /* -------------------------------------------- */
  incrementerJour() {
    const index = this.getCurrentDayIndex() + 1;
    this.calendrier = RdDCalendrier.getCalendrier(index);
    this.rebuildListeNombreAstral();
  }

  /* -------------------------------------------- */
  syncPlayerTime(calendrier) {
    this.calendrier = duplicate(calendrier); // Local copy update
    this.updateDisplay();
  }

  /* -------------------------------------------- */
  async positionnerHeure(indexHeure) {
    if (indexHeure <= this.calendrier.heureRdD) {
      this.incrementerJour();
    }
    this.calendrier.heureRdD = indexHeure;
    this.calendrier.minutesRelative = 0;
    game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));
  }

  /* -------------------------------------------- */
  fillCalendrierData(formData = {}) {
    let moisKey = heuresList[this.calendrier.moisRdD];
    let heureKey = heuresList[this.calendrier.heureRdD];

    const mois = heuresDef[moisKey];
    const heure = heuresDef[heureKey];

    //console.log(moisKey, heureKey);
    formData.heureKey = heureKey;
    formData.moisKey = moisKey;
    formData.jourMois = this.calendrier.jour + 1;
    formData.nomMois = mois.label; // heures et mois nommés identiques
    formData.iconMois = dossierIconesHeures + mois.icon;
    formData.nomHeure = heure.label;
    formData.iconHeure = dossierIconesHeures + heure.icon;
    formData.nomSaison = saisonsDef[mois.saison].label;
    formData.heureRdD = this.calendrier.heureRdD;
    formData.minutesRelative = this.calendrier.minutesRelative;
    formData.isGM = game.user.isGM;
    return formData;
  }

  /* -------------------------------------------- */
  getLectureAstrologieDifficulte(dateIndex) {
    let indexNow = this.getCurrentDayIndex();
    let diffDay = dateIndex - indexNow;
    return - Math.floor(diffDay / 2);
  }

  /* -------------------------------------------- */
  async requestNombreAstral(request) {
    if (game.user.isGM) { // Only GM
      console.log(request);
      let jourDiff = this.getLectureAstrologieDifficulte(request.date);
      let niveau = Number(request.astrologie.data.niveau) + Number(request.conditions) + Number(jourDiff) + Number(request.etat);
      let rollData = {
        caracValue: request.carac_vue,
        finalLevel: niveau,
        showDice: false,
        rollMode: "blindroll"
      };
      await RdDResolutionTable.rollData(rollData);
      let nbAstral = this.getNombreAstral(request.date);
      request.rolled = rollData.rolled;
      request.isValid = true;
      if (!request.rolled.isSuccess) {
        request.isValid = false;
        nbAstral = await RdDDice.rollTotal("1dhr" + nbAstral, { showDice: true, rollMode: "selfroll" });
        // Mise à jour des nombres astraux du joueur
        let astralData = this.listeNombreAstral.find((nombreAstral, i) => nombreAstral.index == request.date);
        astralData.valeursFausses.push({ actorId: request.id, nombreAstral: nbAstral });
        game.settings.set("foundryvtt-reve-de-dragon", "liste-nombre-astral", this.listeNombreAstral);
      }
      request.nbAstral = nbAstral;
      if (game.user.isGM) {
        RdDUtility.responseNombreAstral(request);
      } else {
        game.socket.emit("system.foundryvtt-reve-de-dragon", {
          msg: "msg_response_nombre_astral",
          data: request
        });
      }
    }
  }

  /* -------------------------------------------- */
  findHeure(heure) {
    heure = Grammar.toLowerCaseNoAccentNoSpace(heure);
    let parHeureOuLabel = Object.values(heuresDef).filter(it => (it.heure + 1) == parseInt(heure) || Grammar.toLowerCaseNoAccentNoSpace(it.label) == heure);
    if (parHeureOuLabel.length == 1) {
      return parHeureOuLabel[0];
    }
    let parLabelPartiel = Object.values(heuresDef).filter(it => Grammar.toLowerCaseNoAccentNoSpace(it.label).includes(heure));
    if (parLabelPartiel.length > 0) {
      parLabelPartiel.sort(Misc.ascending(h => h.label.length));
      return parLabelPartiel[0];
    }
    return undefined;
  }

  /* -------------------------------------------- */
  getAjustementAstrologique(heureNaissance, name = undefined) {
    let defHeure = this.findHeure(heureNaissance);
    if (defHeure) {
      let hn = defHeure.heure;
      let chiffreAstral = this.getCurrentNombreAstral() ?? 0;
      let heureCourante = this.calendrier.heureRdD;
      let ecartChance = (hn + chiffreAstral - heureCourante) % RDD_HEURES_PAR_JOUR;
      switch (ecartChance) {
        case 0: return 4;
        case 4: case 8: return 2;
        case 6: return -4;
        case 3: case 9: return -2;
      }
    }
    else if (name) {
      ui.notifications.warn(name + " n'a pas d'heure de naissance, ou elle est incorrecte : " + heureNaissance);
    }
    else {
      ui.notifications.warn(heureNaissance + " ne correspond pas à une heure de naissance");
    }
    return 0;
  }

  /* -------------------------------------------- */
  getData() {
    let formData = super.getData();

    this.fillCalendrierData(formData);

    this.setPos(this.calendrierPos);
    return formData;
  }

  /* -------------------------------------------- */
  setPos(pos) {
    return new Promise(resolve => {
      function check() {
        let elmnt = document.getElementById("calendar-time-container");
        if (elmnt) {
          elmnt.style.bottom = null;
          let xPos = (pos.left) > window.innerWidth ? window.innerWidth - 200 : pos.left;
          let yPos = (pos.top) > window.innerHeight - 20 ? window.innerHeight - 100 : pos.top;
          elmnt.style.top = (yPos) + "px";
          elmnt.style.left = (xPos) + "px";
          resolve();
        } else {
          setTimeout(check, 30);
        }
      }
      check();
    });
  }

  /* -------------------------------------------- */
  updateDisplay() {
    let data = this.fillCalendrierData();
    // Rebuild data
    let dateHTML = `Jour ${data.jourMois} de ${data.nomMois} (${data.nomSaison})`
    if (game.user.isGM) {
      dateHTML = dateHTML + " - NA: " + (this.getCurrentNombreAstral() ?? "indéterminé");
    }
    for (let handle of document.getElementsByClassName("calendar-date-rdd")) {
      handle.innerHTML = dateHTML;
    }
    for (let heure of document.getElementsByClassName("calendar-heure-texte")) {
      heure.innerHTML = data.nomHeure;
    }
    for (const minute of document.getElementsByClassName("calendar-time-disp")) {
      minute.innerHTML = `${data.minutesRelative} minutes`;
    }
    for (const heureImg of document.getElementsByClassName("calendar-heure-img")) {
      heureImg.src = data.iconHeure;
    }
  }

  /* -------------------------------------------- */
  async saveEditeur(calendrierData) {
    this.calendrier.minutesRelative = Number(calendrierData.minutesRelative);
    this.calendrier.jour = Number(calendrierData.jourMois) - 1;
    this.calendrier.moisRdD = RdDCalendrier.getDefSigne(calendrierData.moisKey);
    this.calendrier.heureRdD = RdDCalendrier.getDefSigne(calendrierData.heureKey); // Index dans heuresList
    game.settings.set("foundryvtt-reve-de-dragon", "calendrier", duplicate(this.calendrier));

    await this.rebuildListeNombreAstral();

    game.socket.emit("system.foundryvtt-reve-de-dragon", {
      msg: "msg_sync_time",
      data: duplicate(this.calendrier)
    });

    this.updateDisplay();
  }

  /* -------------------------------------------- */
  async showCalendarEditor() {
    let calendrierData = duplicate(this.fillCalendrierData());
    if (this.editeur == undefined) {
      calendrierData.jourMoisOptions = this.buildJoursMois();
      calendrierData.heuresOptions = [0, 1];
      calendrierData.minutesOptions = Array(RDD_MINUTES_PAR_HEURES).fill().map((item, index) => 0 + index);
      let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-editor-template.html', calendrierData);
      this.editeur = new RdDCalendrierEditeur(html, this, calendrierData)
    }
    this.editeur.updateData(calendrierData);
    this.editeur.render(true);
  }

  static buildJoursMois() {
    return Array(RDD_JOUR_PAR_MOIS).fill().map((item, index) => 1 + index);
  }

  /* -------------------------------------------- */
  async showAstrologieEditor() {
    let calendrierData = duplicate(this.fillCalendrierData());
    let astrologieArray = [];
    for (let astralData of this.listeNombreAstral) {
      astralData.humanDate = this.getDateFromIndex(astralData.index);
      for (let vf of astralData.valeursFausses) {
        let actor = game.actors.get(vf.actorId);
        console.log(vf.actorId, actor);
        vf.actorName = (actor) ? actor.name : "Inconnu";
      }
      astrologieArray.push(duplicate(astralData));
    }
    //console.log("ASTRO", astrologieArray);
    calendrierData.astrologieData = astrologieArray;
    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/calendar-astrologie-template.html', calendrierData);
    let astrologieEditeur = new RdDAstrologieEditeur(html, this, calendrierData)
    astrologieEditeur.updateData(calendrierData);
    astrologieEditeur.render(true);
  }

  /* -------------------------------------------- */
  /** @override */
  async activateListeners(html) {
    super.activateListeners(html);

    HtmlUtility._showControlWhen($(".gm-only"), game.user.isGM);

    await this.updateDisplay();

    html.find('.calendar-btn').click(ev => this.onCalendarButton(ev));

    html.find('.calendar-btn-edit').click(ev => {
      ev.preventDefault();
      this.showCalendarEditor();
    });

    html.find('.astrologie-btn-edit').click(ev => {
      ev.preventDefault();
      this.showAstrologieEditor();
    });

    html.find('#calendar-move-handle').mousedown(ev => {
      ev.preventDefault();
      ev = ev || window.event;
      let isRightMB = false;
      if ("which" in ev) { // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
        isRightMB = ev.which == 3;
      } else if ("button" in ev) { // IE, Opera 
        isRightMB = ev.button == 2;
      }

      if (!isRightMB) {
        dragElement(document.getElementById("calendar-time-container"));
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

        function dragElement(elmnt) {
          elmnt.onmousedown = dragMouseDown;
          function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;

            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
          }

          function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // calculate the new cursor position:
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            // set the element's new position:
            elmnt.style.bottom = null
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
          }

          function closeDragElement() {
            // stop moving when mouse button is released:
            elmnt.onmousedown = null;
            document.onmouseup = null;
            document.onmousemove = null;
            let xPos = (elmnt.offsetLeft - pos1) > window.innerWidth ? window.innerWidth - 200 : (elmnt.offsetLeft - pos1);
            let yPos = (elmnt.offsetTop - pos2) > window.innerHeight - 20 ? window.innerHeight - 100 : (elmnt.offsetTop - pos2)
            xPos = xPos < 0 ? 0 : xPos;
            yPos = yPos < 0 ? 0 : yPos;
            if (xPos != (elmnt.offsetLeft - pos1) || yPos != (elmnt.offsetTop - pos2)) {
              elmnt.style.top = (yPos) + "px";
              elmnt.style.left = (xPos) + "px";
            }
            game.system.rdd.calendrier.calendrierPos.top = yPos;
            game.system.rdd.calendrier.calendrierPos.left = xPos;
            if (game.user.isGM) {
              game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
            }
          }
        }
      } else if (isRightMB) {
        game.system.rdd.calendrier.calendrierPos.top = 200;
        game.system.rdd.calendrier.calendrierPos.left = 200;
        if (game.user.isGM) {
          game.settings.set("foundryvtt-reve-de-dragon", "calendrier-pos", duplicate(game.system.rdd.calendrier.calendrierPos));
        }
        this.setPos(game.system.rdd.calendrier.calendrierPos);
      }
    });
  }

}