import { TMRRencontres } from "./tmr-rencontres.js";
import { Misc } from "./misc.js";

/* -------------------------------------------- */
const TMRMapping = {
  A1: { type: "cite", label: "Cité Vide" },
  B1: { type: "plaines", label: "Plaines d’Assorh" },
  C1: { type: "necropole", label: "Nécropole de Kroak" },
  D1: { type: "fleuve", label: "Fleuve de l'Oubli" },
  E1: { type: "monts", label: "Monts de Kanaï" },
  F1: { type: "cite", label: "Cité Glauque" },
  G1: { type: "desolation", label: "Désolation de Demain" },
  H1: { type: "lac", label: "Lac d’Anticalme" },
  I1: { type: "plaines", label: "Plaines Grises" },
  J1: { type: "monts", label: "Monts Fainéants" },
  K1: { type: "cite", label: "Cité d’Onkause" },
  L1: { type: "fleuve", label: "Fleuve de l'Oubli" },
  M1: { type: "cite", label: "Cité Jalouse" },

  A2: { type: "desert", label: "Désert de Mieux" },
  B2: { type: "collines", label: "Collines de Dawell" },
  C2: { type: "marais", label: "Marais Glignants" },
  D2: { type: "cite", label: "Cité de Frost" },
  E2: { type: "plaines", label: "Plaines de Fiask" },
  F2: { type: "lac", label: "Lac de Misère" },
  G2: { type: "marais", label: "Marais Nuisants" },
  H2: { type: "collines", label: "Collines de Parta" },
  I2: { type: "foret", label: "Forêt Fade" },
  J2: { type: "desert", label: "Désert de Poly" },
  K2: { type: "foret", label: "Forêt Tamée" },
  L2: { type: "fleuve", label: "Fleuve de l'Oubli" },
  M2: { type: "necropole", label: "Nécropole de Logos" },

  A3: { type: "desolation", label: "Désolation de Demain" },
  B3: { type: "plaines", label: "Plaines de Rubéga" },
  C3: { type: "fleuve", label: "Fleuve de l'Oubli" },
  D3: { type: "gouffre", label: "Gouffre d’Oki" },
  E3: { type: "foret", label: "Forêt d’Estoubh" },
  F3: { type: "fleuve", label: "Fleuve de l'Oubli" },
  G3: { type: "gouffre", label: "Gouffre de Sun" },
  H3: { type: "foret", label: "Forêt de Ganna" },
  I3: { type: "monts", label: "Monts Grinçants" },
  J3: { type: "cite", label: "Cité Venin" },
  K3: { type: "plaines", label: "Plaines de Dois" },
  L3: { type: "lac", label: "Lac Laineux" },
  M3: { type: "monts", label: "Monts de Vdah" },

  A4: { type: "foret", label: "Forêt de Falconax" },
  B4: { type: "monts", label: "Monts Crâneurs" },
  C4: { type: "pont", label: "Pont de Giolii" },
  D4: { type: "lac", label: "Lac de Foam" },
  E4: { type: "plaines", label: "Plaines d’Orti" },
  F4: { type: "fleuve", label: "Fleuve de l'Oubli" },
  G4: { type: "sanctuaire", label: "Sanctuaire Blanc" },
  H4: { type: "plaines", label: "Plaines de Psark" },
  I4: { type: "plaines", label: "Plaines de Xiax" },
  J4: { type: "collines", label: "Collines d’Encre" },
  K4: { type: "pont", label: "Pont de Fah" },
  L4: { type: "sanctuaire", label: "Sanctuaire Mauve" },
  M4: { type: "gouffre", label: "Gouffre Grisant" },

  A5: { type: "plaines", label: "Plaines de Trilkh" },
  B5: { type: "collines", label: "Collines de Tanegy" },
  C5: { type: "marais", label: "Marais Flouants" },
  D5: { type: "fleuve", label: "Fleuve de l'Oubli" },
  E5: { type: "monts", label: "Monts Brûlants" },
  F5: { type: "cite", label: "Cité de Panople" },
  G5: { type: "pont", label: "Pont d’Ik" },
  H5: { type: "desert", label: "Désert de Krane" },
  I5: { type: "desolation", label: "Désolation de Demain" },
  J5: { type: "marais", label: "Marais de Jab" },
  K5: { type: "fleuve", label: "Fleuve de l'Oubli" },
  L5: { type: "collines", label: "Collines Suaves" },
  M5: { type: "cite", label: "Cité Rimarde" },

  A6: { type: "necropole", label: "Nécropole de Zniak" },
  B6: { type: "foret", label: "Forêt de Bust" },
  C6: { type: "cite", label: "Cité Pavois" },
  D6: { type: "fleuve", label: "Fleuve de l'Oubli" },
  E6: { type: "sanctuaire", label: "Sanctuaire de Plaine" },
  F6: { type: "fleuve", label: "Fleuve de l'Oubli" },
  G6: { type: "marais", label: "Marais Glutants" },
  H6: { type: "monts", label: "Monts Gurdes" },
  I6: { type: "necropole", label: "Nécropole de Xotar" },
  J6: { type: "lac", label: "Lac d’Iaupe" },
  K6: { type: "desolation", label: "Désolation de Demain" },
  L6: { type: "foret", label: "Forêt Gueuse" },
  M6: { type: "desolation", label: "Désolation de Demain" },

  A7: { type: "plaines", label: "Plaines de l’Arc" },
  B7: { type: "marais", label: "Marais Bluants" },
  C7: { type: "fleuve", label: "Fleuve de l'Oubli" },
  D7: { type: "plaines", label: "Plaines d’Affa" },
  E7: { type: "foret", label: "Forêt de Glusks" },
  F7: { type: "fleuve", label: "Fleuve de l'Oubli" },
  G7: { type: "cite", label: "Cité de Terwa" },
  H7: { type: "gouffre", label: "Gouffre de Kapfa" },
  I7: { type: "plaines", label: "Plaines de Troo" },
  J7: { type: "fleuve", label: "Fleuve de l'Oubli" },
  K7: { type: "cite", label: "Cité de Kolix" },
  L7: { type: "gouffre", label: "Gouffre d’Episophe" },
  M7: { type: "desert", label: "Désert de Lave" },

  A8: { type: "gouffre", label: "Gouffre de Shok" },
  B8: { type: "fleuve", label: "Fleuve de l'Oubli" },
  C8: { type: "foret", label: "Forêt Turmide" },
  D8: { type: "cite", label: "Cité d’Olak" },
  E8: { type: "plaines", label: "Plaines d’Iolise" },
  F8: { type: "lac", label: "Lac des Chats" },
  G8: { type: "plaines", label: "Plaines Sans Joie" },
  H8: { type: "foret", label: "Forêt d’Ourf" },
  I8: { type: "fleuve", label: "Fleuve de l'Oubli" },
  J8: { type: "monts", label: "Monts Barask" },
  K8: { type: "desert", label: "Désert de Fumée" },
  L8: { type: "monts", label: "Monts Tavelés" },
  M8: { type: "plaines", label: "Plaines Lavées" },

  A9: { type: "collines", label: "Collines de Korrex" },
  B9: { type: "lac", label: "Lac de Lucre" },
  C9: { type: "monts", label: "Monts Tuméfiés" },
  D9: { type: "pont", label: "Pont d’Orx" },
  E9: { type: "fleuve", label: "Fleuve de l'Oubli" },
  F9: { type: "plaines", label: "Plaines de Foe" },
  G9: { type: "desolation", label: "Désolation de Demain" },
  H9: { type: "collines", label: "Collines de Noirseul" },
  I9: { type: "fleuve", label: "Fleuve de l'Oubli" },
  J9: { type: "marais", label: "Marais Gronchants" },
  K9: { type: "sanctuaire", label: "Sanctuaire Noir" },
  L9: { type: "collines", label: "Collines Cornues" },
  M9: { type: "necropole", label: "Nécropole de Zonar" },

  A10: { type: "sanctuaire", label: "Sanctuaire d’Olis" },
  B10: { type: "monts", label: "Monts Salés" },
  C10: { type: "marais", label: "Marais de Dom" },
  D10: { type: "fleuve", label: "Fleuve de l'Oubli" },
  E10: { type: "gouffre", label: "Gouffre de Junk" },
  F10: { type: "marais", label: "Marais Zultants" },
  G10: { type: "cite", label: "Cité de Sergal" },
  H10: { type: "plaines", label: "Plaines Noires" },
  I10: { type: "lac", label: "Lac Wanito" },
  J10: { type: "fleuve", label: "Fleuve de l'Oubli" },
  K10: { type: "plaines", label: "Plaines Jaunes" },
  L10: { type: "desert", label: "Désert de Nicrop" },
  M10: { type: "foret", label: "Forêt de Jajou" },

  A11: { type: "desolation", label: "Désolation de Demain" },
  B11: { type: "cite", label: "Cité de Brilz" },
  C11: { type: "pont", label: "Pont de Roï" },
  D11: { type: "desolation", label: "Désolation de Demain" },
  E11: { type: "lac", label: "Lac de Glinster" },
  F11: { type: "cite", label: "Cité de Noape" },
  G11: { type: "fleuve", label: "Fleuve de l'Oubli" },
  H11: { type: "fleuve", label: "Fleuve de l'Oubli" },
  I11: { type: "pont", label: "Pont de Yalm" },
  J11: { type: "plaines", label: "Plaines de Miltiar" },
  K11: { type: "cite", label: "Cité Tonnerre" },
  L11: { type: "collines", label: "Collines de Kol" },
  M11: { type: "cite", label: "Cité Crapaud" },

  A12: { type: "plaines", label: "Plaines Sages" },
  B12: { type: "fleuve", label: "Fleuve de l'Oubli" },
  C12: { type: "lac", label: "Lac de Fricassa" },
  D12: { type: "collines", label: "Collines d’Huaï" },
  E12: { type: "monts", label: "Monts Ajourés" },
  F12: { type: "necropole", label: "Nécropole de Troat" },
  G12: { type: "plaines", label: "Plaines de Lufmil" },
  H12: { type: "collines", label: "Collines de Tooth" },
  I12: { type: "gouffre", label: "Gouffre Abimeux" },
  J12: { type: "cite", label: "Cité Folle" },
  K12: { type: "desolation", label: "Désolation de Demain" },
  L12: { type: "plaines", label: "Plaines Venteuses" },
  M12: { type: "collines", label: "Collines Révulsantes" },

  A13: { type: "fleuve", label: "Fleuve de l'Oubli" },
  B13: { type: "gouffre", label: "Gouffre des Litiges" },
  C13: { type: "desert", label: "Désert de Neige" },
  D13: { type: "cite", label: "Cité Sordide" },
  E13: { type: "plaines", label: "Plaines de Xnez" },
  F13: { type: "foret", label: "Forêt des Cris" },
  G13: { type: "plaines", label: "Plaines Calcaires" },
  H13: { type: "desolation", label: "Désolation de Demain" },
  I13: { type: "monts", label: "Monts Bigleux" },
  J13: { type: "gouffre", label: "Gouffre de Gromph" },
  K13: { type: "foret", label: "Forêt de Kluth" },
  L13: { type: "monts", label: "Monts Dormants" },
  M13: { type: "plaines", label: "Plaines d’Anjou" },

  A14: { type: "collines", label: "Collines de Stolis" },
  B14: { type: "necropole", label: "Nécropole de Gorlo" },
  C14: { type: "foret", label: "Forêt de Bissam" },
  D14: { type: "sanctuaire", label: "Sanctuaire Plat" },
  E14: { type: "monts", label: "Monts de Quath" },
  F14: { type: "plaines", label: "Plaines Brisées" },
  G14: { type: "desert", label: "Désert de Sek" },
  H14: { type: "plaines", label: "Plaines Blanches" },
  I14: { type: "cite", label: "Cité Destituée" },
  J14: { type: "desert", label: "Désert de Sank" },
  K14: { type: "necropole", label: "Nécropole d’Antinéar" },
  L14: { type: "plaines", label: "Plaines de Jislith" },
  M14: { type: "desolation", label: "Désolation de Demain" },

  A15: { type: "cite", label: "Cité de Mielh" },
  C15: { type: "plaines", label: "Plaines de Toué" },
  E15: { type: "foret", label: "Forêt des Furies" },
  G15: { type: "plaines", label: "Plaines des Soupirs" },
  I15: { type: "monts", label: "Monts des Dragées" },
  K15: { type: "collines", label: "Collines Pourpres" },
  M15: { type: "cite", label: "Cité de Klana" }
}

export const TMRType = {
  cite: { name: "cité", genre: "f" },
  sanctuaire: { name: "sanctuaire", genre: 'm' },
  plaines: { name: "plaines", genre: "fp" },
  pont: { name: "pont", genre: "m" },
  collines: { name: "collines", genre: "p" },
  foret: { name: "forêt", genre: "f" },
  monts: { name: "monts", genre: "p" },
  desert: { name: "désert", genre: "m" },
  fleuve: { name: "fleuve", genre: "m" },
  lac: { name: "lac", genre: "m" },
  marais: { name: "marais", genre: "m" },
  gouffre: { name: "gouffre", genre: "m" },
  necropole: { name: "nécropole", genre: "f" },
  desolation: { name: "désolation", genre: "f" }
}

/* -------------------------------------------- */
const caseSpecificModes = ["attache", "trounoir", "debordement", "reserve_extensible", "maitrisee"];

/* -------------------------------------------- */
const tmrRandomMovePatten =
  [{ name: 'top', x: 0, y: -1 },
  { name: 'topright', x: 1, y: -1 },
  { name: 'botright', x: 1, y: 1 },
  { name: 'bot', x: 0, y: 1 },
  { name: 'botleft', x: -1, y: 1 },
  { name: 'topleft', x: -1, y: -1 }
  ]

/* -------------------------------------------- */
export const tmrConstants = {
  col1_y: 30,
  col2_y: 55,
  cellw: 55,
  cellh: 55,
  gridx: 28,
  gridy: 28,
  // tailles
  third: 18,
  half: 27.5,
  twoThird: 36,
  full: 55,
  // decallages
  center: { x: 0, y: 0 },
  top: { x: 0, y: -11.5 },
  topLeft: { x: -11.5, y: -11.5 },
  left: { x: -11.5, y: 0 },
  bottomLeft: { x: -11.5, y: 11.5 },
  bottom: { x: 0, y: 11.5 },
  bottomRight: { x: 11.5, y: 11.5 },
  right: { x: 11.5, y: 0 },
  topRight: { x: 11.5, y: -11.5 },
}

// couleurs
export const tmrColors = {
  sort: 0xFF8800,
  tetes: 0xA000FF,
  souffle: 0x804040,
  queues: 0xAA4040,
  trounoir: 0x401060,
  demireve: 0x00FFEE,
  rencontre: 0xFF0000,
  casehumide: 0x1050F0,
}
export const tmrTokenZIndex = {
  sort: 40,
  tetes: 20,
  casehumide: 10,
  conquete: 30,
  rencontre: 50,
  trounoir: 60,
  demireve: 70,
  tooltip: 100,
}

/* -------------------------------------------- */

/* -------------------------------------------- */
export class TMRUtility {
  static init() {
    for (let coord in TMRMapping) {
      const tmr = TMRMapping[coord];
      tmr.coord = coord;
      tmr.genre = TMRType[tmr.type].genre;
    }
    let tmrByType = Misc.classify(Object.values(TMRMapping));
    for (const [type, list] of Object.entries(tmrByType)) {
      TMRType[type].list = list;
    }
  }

  /* -------------------------------------------- */
  static convertToTMRCoord(pos) {
    let letterX = String.fromCharCode(65 + (pos.x));
    return letterX + (pos.y + 1)
  }

  /* -------------------------------------------- */
  static verifyTMRCoord(coord) {
    let TMRregexp = new RegExp(/([A-M])(\d+)/g);
    let res = TMRregexp.exec(coord);
    if (res && res[1] && res[2]) {
      if (res[2] > 0 && res[2] < 16) {
        return true;
      }
    }
    return false;
  }

  /* -------------------------------------------- */
  static convertToCellPos(coordTMR) {
    let x = coordTMR.charCodeAt(0) - 65;
    let y = coordTMR.substr(1) - 1;
    return { x: x, y: y }
  }

  /* -------------------------------------------- */
  static getTMR(coord) {
    return TMRMapping[coord];
  }

  static getTMRLabel(coord) {
    return TMRMapping[coord]?.label ?? (coord + ": case inconnue");
  }

  static getTMRDescr(coord) {
    const tmr = TMRMapping[coord];
    return Grammar.articleDetermine(tmr.genre) + ' ' + tmr.label;
  }

  static isCaseHumide(tmr) {
    return tmr.type == 'fleuve' || tmr.type == 'lac' || tmr.type == 'marais';
  }

  /* -------------------------------------------- */
  /** Some debug functions  */
  static async setForceRencontre(index, force = undefined) {
    this.prochaineRencontre = TMRRencontres.getRencontre(index);
    if (this.prochaineRencontre) {
      if (force) {
        this.prochaineRencontre.force = force;
      }
      else {
        await TMRRencontres.evaluerForceRencontre(this.prochaineRencontre);
      }
      console.log("La prochaine rencontre sera:", this.prochaineRencontre.name, " force:", this.prochaineRencontre.force);
    }
    else {
      ui.notifications.warn("Pas de prochaine rencontre valide pour " + index);
    }
  }

  /* -------------------------------------------- */
  static isForceRencontre() {
    return this.prochaineRencontre;
  }
  /* -------------------------------------------- */
  static utiliseForceRencontre() {
    const rencontre = this.prochaineRencontre;
    this.prochaineRencontre = undefined;
    return rencontre;
  }

  /* -------------------------------------------- */
  static getDirectionPattern() {
    return Misc.rollOneOf(tmrRandomMovePatten);
  }

  static deplaceTMRAleatoire(actor, coord) {
    return TMRUtility.deplaceTMRSelonPattern(actor, coord, TMRUtility.getDirectionPattern(), 1);
  }

  /* -------------------------------------------- */
  static async deplaceTMRSelonPattern(actor, coord, direction, nTime) {
    for (let i = 0; i < nTime; i++) {
      let currentPos = TMRUtility.convertToCellPos(coord);
      currentPos.x = currentPos.x + direction.x;
      currentPos.y = currentPos.y + direction.y;
      if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Sortie de carte ! Ré-insertion aléatoire
        coord = TMRUtility.getTMR(TMRUtility.convertToTMRCoord(currentPos));
      } else {
        coord = await actor.reinsertionAleatoire('Sortie de carte');
      }
      console.log("Nouvelle case iteration !!!", i, coord);
    }
    return coord;
  }

  /* -------------------------------------------- */
  static getListTMR(terrain) {
    return TMRType[terrain].list;
  }

  static filterTMR(filter) {
    return Object.values(TMRMapping).filter(filter);
  }

  static filterTMRCoord(filter) {
    return TMRUtility.filterTMR(filter).map(it => it.coord);
  }

  static getTMRAleatoire(filter = it => true) {
    return Misc.rollOneOf(TMRUtility.filterTMR(filter))
  }

  /* -------------------------------------------- */
  static _checkTMRCoord(x, y) {
    if (x >= 0 && x < 13 && y >= 0 && y < 14) return true;
    if (x >= 0 && x < 13 && x % 2 == 0 && y == 14) return true;
    return false;
  }

  /* -------------------------------------------- */
  static computeRealPictureCoordinates(coordXY, tmrConstants) {
    let decallagePairImpair = (coordXY.x % 2 == 0) ? tmrConstants.col1_y : tmrConstants.col2_y;
    return {
      x: tmrConstants.gridx + (coordXY.x * tmrConstants.cellw),
      y: tmrConstants.gridy + (coordXY.y * tmrConstants.cellh) + decallagePairImpair
    }
  }

  /* -------------------------------------------- */
  static getSortsReserve(reserveList, coord) {
    // TODO : Gérer les têtes spéciales réserve!
    let tmrDescr = this.getTMR(coord);
    //console.log("Sort réserve : ", tmrDescr);
    if (tmrDescr.type == 'fleuve') { // Gestion de la reserve en Fleuve
      return reserveList.filter(it => TMRUtility.getTMR(it.coord).type == 'fleuve');
    }
    // Reserve sur un case "normale"
    return reserveList.filter(it => it.coord == coord);
  }

  /* -------------------------------------------- */
  /** Returns a list of case inside a given distance
   * 
   */
  static getTMRPortee(coord, portee) {
    let centerPos = this.convertToCellPos(coord);
    let posPic = this.computeRealPictureCoordinates(centerPos, tmrConstants);
    let caseList = [];
    for (let dx = -portee; dx <= portee; dx++) { // Loop thru lines
      for (let dy = -portee; dy <= portee; dy++) { // Loop thru lines
        const currentPos = { x: centerPos.x + dx, y: centerPos.y + dy };
        if (this._checkTMRCoord(currentPos.x, currentPos.y)) { // Coordinate is valie
          let dist = this.distancePosTMR(centerPos, currentPos);
          if (dist <= portee) {
            caseList.push(this.convertToTMRCoord(currentPos)); // Inside the area
          }
        }
      }
    }
    return caseList;
  }

  static distanceTMR(coord1, coord2) {
    let pos1 = this.convertToCellPos(coord1);
    let pos2 = this.convertToCellPos(coord2);
    return this.distancePosTMR(pos1, pos2);
  }

  static distancePosTMR(pos1, pos2) {
    const dx = pos2.x - pos1.x;
    const dy = pos2.y - pos1.y;
    const abs_dx = Math.abs(dx);
    const abs_dy = Math.abs(dy);
    const distance = Math.sign(dx) == Math.sign(dy) ? Math.max(abs_dx, abs_dy) : (abs_dx + abs_dy);
    return distance;
  }

}

