import { Misc } from "./misc.js";
import { RdDDice } from "./rdd-dice.js";

/**
 * difficultés au delà de -10
 */
const levelDown = [
  { level: -11, score: 1, sign: 0, part: 0, epart: 2, etotal: 90 },
  { level: -12, score: 1, sign: 0, part: 0, epart: 2, etotal: 70 },
  { level: -13, score: 1, sign: 0, part: 0, epart: 2, etotal: 50 },
  { level: -14, score: 1, sign: 0, part: 0, epart: 2, etotal: 30 },
  { level: -15, score: 1, sign: 0, part: 0, epart: 2, etotal: 10 },
  { level: -16, score: 1, sign: 0, part: 0, epart: 0, etotal: 2 }
];
const levelImpossible = { score: 0, sign: 0, part: 0, epart: 0, etotal: 1 };
/**
 * Table des résultats spéciaux - inutilisée, conservée si on veut afficher la table
 */
const specialResults = [
  { part: 0, epart: 0, etotal: 0, min: 0, max: 0 },
  { part: 1, epart: 81, etotal: 92, min: 1, max: 5 },
  { part: 2, epart: 82, etotal: 92, min: 6, max: 10 },
  { part: 3, epart: 83, etotal: 93, min: 11, max: 15 },
  { part: 4, epart: 84, etotal: 93, min: 16, max: 20 },
  { part: 5, epart: 85, etotal: 94, min: 21, max: 25 },
  { part: 6, epart: 86, etotal: 94, min: 26, max: 30 },
  { part: 7, epart: 87, etotal: 95, min: 31, max: 35 },
  { part: 8, epart: 88, etotal: 95, min: 36, max: 40 },
  { part: 9, epart: 89, etotal: 96, min: 41, max: 45 },
  { part: 10, epart: 90, etotal: 96, min: 46, max: 50 },
  { part: 11, epart: 91, etotal: 97, min: 51, max: 55 },
  { part: 12, epart: 92, etotal: 97, min: 56, max: 60 },
  { part: 13, epart: 93, etotal: 98, min: 61, max: 65 },
  { part: 14, epart: 94, etotal: 98, min: 65, max: 70 },
  { part: 15, epart: 95, etotal: 99, min: 71, max: 75 },
  { part: 16, epart: 96, etotal: 99, min: 76, max: 80 },
  { part: 17, epart: 97, etotal: 100, min: 81, max: 85 },
  { part: 18, epart: 98, etotal: 100, min: 86, max: 90 },
  { part: 19, epart: 99, etotal: 100, min: 81, max: 95 },
  { part: 20, epart: 100, etotal: 100, min: 96, max: 100 }
];


const reussites = [
  { code: "etotal", isPart: false, isSign: false, isSuccess: false, isEchec: true,  isEPart: true,  isETotal: true,  ptTache: -4, ptQualite: -6, quality: "Echec total", condition: (target, roll) => roll >= target.etotal && roll <= 100 },
  { code: "epart",  isPart: false, isSign: false, isSuccess: false, isEchec: true,  isEPart: true,  isETotal: false, ptTache: -2, ptQualite: -4, quality: "Echec particulier", condition: (target, roll) => (roll >= target.epart && roll < target.etotal) },
  { code: "echec",  isPart: false, isSign: false, isSuccess: false, isEchec: true,  isEPart: false, isETotal: false, ptTache: 0, ptQualite: -2, quality: "Echec normal", condition: (target, roll) => (roll > target.score && roll < target.etotal) },
  { code: "norm",   isPart: false, isSign: false, isSuccess: true,  isEchec: false, isEPart: false, isETotal: false, ptTache: 1, ptQualite: 0, quality: "Réussite normale", condition: (target, roll) => (roll > target.sign && roll <= target.score) },
  { code: "sign",   isPart: false, isSign: true,  isSuccess: true,  isEchec: false, isEPart: false, isETotal: false, ptTache: 2, ptQualite: 1, quality: "Réussite significative", condition: (target, roll) => (roll > target.part && roll <= target.sign) },
  { code: "part",   isPart: true,  isSign: true,  isSuccess: true,  isEchec: false, isEPart: false, isETotal: false, ptTache: 3, ptQualite: 2, quality: "Réussite Particulière!", condition: (target, roll) => (roll > 0 && roll <= target.part) },
  { code: "error",  isPart: false, isSign: false, isSuccess: false, isEchec: true,  isEPart: true,  isETotal: true,  ptTache: 0, ptQualite: 0, quality: "Jet de dés invalide", condition: (target, roll) => (roll <= 0 || roll > 100) }
];

const reussiteSignificative = reussites.find(r => r.code == "sign");
const reussiteNormale = reussites.find(r => r.code == "norm");
const echecNormal = reussites.find(r => r.code == "echec");

export class RdDResolutionTable {
  static resolutionTable = this.build()

  /* -------------------------------------------- */
  static build() {
    let table = []
    for (var carac = 0; carac <= 30; carac++) {
      table[carac] = this._computeRow(carac);
    }
    return table;
  }

  /* -------------------------------------------- */
  static getResultat(code) {
    let resultat = reussites.filter(r => code == r.code);
    if (resultat == undefined) {
      resultat = reussites.find(r => r.code == "error");
    }
    return resultat;
  }

  static explain(rolled) {
    let message = "<br>Jet : <strong>" + rolled.roll + "</strong> sur " + rolled.score + "%";
    if (rolled.carac != null && rolled.finalLevel!= null) {
      message += " (" + rolled.carac + " à " + Misc.toSignedString(rolled.finalLevel) + ")";
    }
    return message;

  }
  /* -------------------------------------------- */
  static async roll(carac, finalLevel) {
    let chances = this.computeChances(carac, finalLevel);
    chances.showDice = true;
    let rolled = await this.rollChances(chances);
    rolled.carac = carac;
    rolled.finalLevel = finalLevel;
    return rolled;
  }

  /* -------------------------------------------- */
  static async rollChances(chances) {
    let myRoll = new Roll("d100").roll();
    myRoll.showDice = chances.showDice;
    await RdDDice.show(myRoll);
    chances.roll = myRoll.total;
    mergeObject(chances, this._computeReussite(chances, chances.roll));
    return chances;
  }

  /* -------------------------------------------- */
  static computeChances(carac, difficulte) {
    if (difficulte < -16) {
      return duplicate(levelImpossible);
    }
    if (difficulte < -10) {
      return duplicate(levelDown.find(levelData => levelData.level == difficulte));
    }
    return duplicate(this.resolutionTable[carac][difficulte + 10]);
  }

  /* -------------------------------------------- */
  static buildXpMessage(rolled, level) {
    if (rolled.isPart && level < 0) {
      const xp = Math.abs(level);
      const xpcarac = Math.floor(xp / 2);
      const xpcomp = xp - xpcarac;
      return "<br>Points d'expérience gagnés ! Carac: " + xpcarac + ", Comp: " + xpcomp;
    }
    return "";
  }


  /* -------------------------------------------- */
  static _computeReussite(chances, roll) {
    const reussite = reussites.find(x => x.condition(chances, roll));
    if (chances.needSignificative) {
      if (reussite.isSign) {
        return reussiteNormale;
      }
      if (reussite.isSuccess) {
        return echecNormal;
      }
    }
    return reussite;
  }

  /* -------------------------------------------- */
  static _computeRow(carac) {
    let dataRow = [
      this._computeCell(-10, Math.max(Math.floor(carac / 4), 1)),
      this._computeCell(-9, Math.max(Math.floor(carac / 2), 1))
    ]
    for (var diff = -8; diff <= 22; diff++) {
      dataRow[diff + 10] = this._computeCell(diff, Math.max(Math.floor(carac * (diff + 10) / 2), 1));
    }
    return dataRow;
  }

  /* -------------------------------------------- */
  static _computeCell(niveau, percentage) {
    return {
      niveau: niveau,
      score: percentage,
      sign: this._reussiteSignificative(percentage),
      part: this._reussitePart(percentage),
      epart: this._echecParticulier(percentage),
      etotal: this._echecTotal(percentage)
    }
  }

  /* -------------------------------------------- */
  static _reussiteSignificative(score) {
    return Math.floor(score / 2);
  }

  /* -------------------------------------------- */
  static _reussitePart(score) {
    return Math.ceil(score / 5);
  }

  /* -------------------------------------------- */
  static _echecParticulier(score) {
    return Math.ceil(score / 5) + 80;
  }

  /* -------------------------------------------- */
  static _echecTotal(score) {
    return Math.ceil(score / 10) + 91;
  }

  /* -------------------------------------------- */
  static buildHTMLTableExtract(caracValue, levelValue) {
    return this.buildHTMLTable(caracValue, levelValue, caracValue - 2, caracValue + 2, levelValue - 5, levelValue + 5)
  }

  static buildHTMLTable(caracValue, levelValue, minCarac = 1, maxCarac = 21, minLevel = -10, maxLevel = 11) {
    return this._buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel)
  }

  /* -------------------------------------------- */
  static _buildHTMLTable(caracValue, levelValue, minCarac, maxCarac, minLevel, maxLevel) {
    let countColonnes = maxLevel - minLevel;
    minCarac = Math.max(minCarac, 1);
    maxCarac = Math.min(maxCarac, 30);
    minLevel = Math.max(minLevel, -10);
    maxLevel = Math.max(Math.min(maxLevel, 22), minLevel + countColonnes);

    let table = $("<table class='table-resolution'/>")
      .append(this._buildHTMLHeader(this.resolutionTable[0], minLevel, maxLevel));

    for (var carac = minCarac; carac <= maxCarac; carac++) {
      table.append(this._buildHTMLRow(this.resolutionTable[carac], carac, caracValue, levelValue, minLevel, maxLevel));
    }
    return table;
  }

  static _buildHTMLHeader(dataRow, minLevel, maxLevel) {
    let tr = $("<tr/>");

    if (minLevel > -8) {
      tr.append($("<th class='table-resolution-level'/>").text("-8"))
    }
    if (minLevel > -7) {
      tr.append($("<th class='table-resolution-level'/>").text("..."));
    }
    for (let difficulte = minLevel; difficulte <= maxLevel; difficulte++) {
      tr.append($("<th class='table-resolution-level'/>").text(Misc.toSignedString(difficulte)));
    }
    return tr;
  }

  static _buildHTMLRow(dataRow, rowIndex, caracValue, levelValue, minLevel, maxLevel) {
    let tr = $("<tr/>");
    let max = maxLevel;

    if (minLevel > -8) {
      let score = dataRow[-8 + 10].score;
      tr.append($("<td class='table-resolution-carac'/>").text(score))
    }
    if (minLevel > -7) {
      tr.append($("<td/>"))
    }
    for (let difficulte = minLevel; difficulte <= max; difficulte++) {
      let td = $("<td/>");
      let score = dataRow[difficulte + 10].score;
      if (rowIndex == caracValue && levelValue == difficulte) {
        td.addClass('table-resolution-target');
      } else if (difficulte == -8) {
        td.addClass('table-resolution-carac');
      }
      tr.append(td.text(score));
    }
    return tr;
  }

}