/**
 * Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
 * @extends {Actor}
 */

import { RdDUtility } from "./rdd-utility.js";
import { TMRUtility } from "./tmr-utility.js";
import { RdDRollDialog } from "./rdd-roll-dialog.js";
import { RdDTMRDialog } from "./rdd-tmr-dialog.js";

export class RdDActor extends Actor {

  /* -------------------------------------------- */
  /**
   * Override the create() function to provide additional RdD functionality.
   *
   * This overrided create() function adds initial items 
   * Namely: Basic skills, money, 
   *
   * @param {Object} data        Barebones actor data which this function adds onto.
   * @param {Object} options     (Unused) Additional options which customize the creation workflow.
   *
   */
   
  static async create(data, options) {
    // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
    if (data.items) {
      return super.create(data, options);
    }
    
    data.items = [];
    let compendiumName = "";
    if (data.type == "personnage")
    {
      compendiumName = "foundryvtt-reve-de-dragon.competences";
    }
    if (data.type == "humanoide") 
    {
      compendiumName = "foundryvtt-reve-de-dragon.competences-humanoides";
    }
    if (data.type == "creature") 
    {
      compendiumName = "foundryvtt-reve-de-dragon.competences-creatures";
    }
    let competences = [];
    const pack = game.packs.get(compendiumName);
    await pack.getIndex().then(index => competences = index);
    for (let comp of competences)
    {
      let compItem = undefined;
      await pack.getEntity(comp._id).then(skill => compItem = skill);
      data.items.push(compItem);
    }

    return super.create(data, options);
  }
  
  /* -------------------------------------------- */  
  

  /* -------------------------------------------- */  
  prepareData() {
    super.prepareData();

    const actorData = this.data;
    const data = actorData.data;
    const flags = actorData.flags;

    // Make separate methods for each Actor type (character, npc, etc.) to keep
    // things organized.
    if (actorData.type === 'personnage') this._prepareCharacterData(actorData);
    if (actorData.type === 'creature') this.computeEtatGeneral(actorData);
    if (actorData.type === 'humanoide') this.computeEtatGeneral(actorData);
  }

  /* -------------------------------------------- */
  /**
   * Prepare Character type specific data
   */
  _prepareCharacterData(actorData) {
    // Initialize empty items
    RdDUtility.computeCarac(actorData.data);
    this.computeEtatGeneral();
  }
  
  /* -------------------------------------------- */
  getCurrentReve() {
    return this.data.data.reve.reve.value;
  }
  
  /* -------------------------------------------- */
  getBestDraconic() {
    
    let draconic = {name: "none", niveau:  -11 };
    for (const item of this.data.items) {
      //console.log(item);
      if ( item.data.categorie && item.data.categorie.toLowerCase() == "draconic") {
        if (item.data.niveau > draconic.niveau) draconic = duplicate(item);
      }
    }
    return draconic;
  }
  
  /* -------------------------------------------- */
  async deleteSortReserve(coordTMR) {
    let reserve = duplicate(this.data.data.reve.reserve);
    let len = reserve.list.length;
    let i = 0;
    let newTable = [];
    for( i=0; i < len; i++) {
      if (reserve.list[i].coord != coordTMR ) 
        newTable.push(reserve.list[i]);
    }
    if ( newTable.length != len ) {
      reserve.list = newTable;
      await this.update( {"data.reve.reserve": reserve } );    
    }
  }

  /* -------------------------------------------- */
  async performRoll( rollData ) {
    
    let result = new Roll("d100").roll().total;
    let quality = "Echec";
    let xpmsg = "";
    let tache = 0;
    //console.log(">>> ROLL", rollData.selectedCarac.label, rollData.rollTarget.score, myroll.total );
    if (result <= rollData.rollTarget.part) {
      quality = "Réussite Particulière!";
      if ( rollData.finalLevel < 0 ) {
        let xpcarac = Math.floor( Math.abs(rollData.finalLevel) / 2);
        let xpcomp  = (Math.abs(rollData.finalLevel) % 2 == 1) ? xpcarac+1 : xpcarac;
        xpmsg = "<br>Points d'expérience gagné ! " + xpcarac + " - " + xpcomp;
      }
      rollData.pointsDeTache = 4;  
      rollData.qualite = 2;
    } else if (result <= (rollData.rollTarget.score /2) ) {
      quality = "Réussite Significative";
      rollData.pointsDeTache = 2;
      rollData.qualite = 1;
    } else if (result <= (rollData.rollTarget.score) ) {
      quality = "Réussite Normale";
      rollData.pointsDeTache = 1;
      rollData.qualite = 0;
    } else if (result < (rollData.rollTarget.epart) ) {
      quality = "Echec Normal";
      rollData.pointsDeTache = 0;
      rollData.qualite = -2;
    } else if (result < (rollData.rollTarget.etotal) ) {
      quality = "Echec Particulier";
      rollData.pointsDeTache = -2;      
      rollData.qualite = -4;
    } else if (result >= (rollData.rollTarget.etotal) ) {
      quality = "Echec Total";
      rollData.pointsDeTache = -4;
      rollData.qualite = -6;
    }
    
    // Manage weapon categories when parrying (cf. page 115 )
    let need_significative = false; // Do we need to have a sgnificative ?
    let need_resist = false; // Do we need to have a sgnificative ?
    if ( rollData.arme && rollData.attackerRoll ) { // Manage parade depeding on weapon type, and change roll results
      let attCategory = RdDUtility.getArmeCategory( rollData.attackerRoll.arme );
      let defCategory = RdDUtility.getArmeCategory( rollData.arme );
      if ( defCategory == "bouclier"  )
        need_significative = true;
      else if ( attCategory != defCategory  ) 
        need_significative = true;
      if ( attCategory.match("epee") && ( defCategory == "hache" || defCategory == "lance") )
        need_resist = true;
    }

    // Sonne management or if need_significative is set
    if ( this.data.data.sante.sonne.value || need_significative) { 
      if (rollData.pointsDeTache >= 2  ) { // Reussite normale dès que significative
        quality = "Réussite Normale";
        rollData.pointsDeTache = 1;
        rollData.qualite = 0;
      } else if (rollData.pointsDeTache < 2 ) { // Not a "significative"
        quality = "Echec Normal";
        rollData.pointsDeTache = 0;
        rollData.qualite = -2;        
      }
    }
    
    // Fight management !
    let defenseMsg;
    let encaisser = false;
    let specialStr = "<br>Points de taches : " + rollData.pointsDeTache; // Per default
    if ( rollData.arme ) { // In case of fight, replace the "tache" message per dommages + localization. "tache" indicates if result is OK or not
      if (  rollData.attackerRoll) { // Defense case !
        if ( rollData.pointsDeTache > 0 ) { // Réussite !
          specialStr = "<br><strong>Attaque parée/esquivée !</strong>";
        } else {
          specialStr = "<br><strong>Esquive/Parade échouée, encaissement !</strong>";
          encaisser  = true;
        } 
      } else { // This is the attack roll!
        if ( rollData.pointsDeTache > 0 ) {
          let myroll = new Roll("2d10");
          myroll.roll();
          rollData.domArmePlusDom = parseInt(rollData.arme.data.dommages);
          if ( rollData.selectedCarac.label == "Mêlée" ) // +dom only for Melee
            rollData.domArmePlusDom += parseInt(this.data.data.attributs.plusdom.value);
          if ( rollData.selectedCarac.label == "Lancer" ) { // +dom only for Melee/Lancer
            let bdom = parseInt(this.data.data.attributs.plusdom.value);
            if ( bdom > parseInt(rollData.arme.data.dommages)*2 )  
              bdom = parseInt(rollData.arme.data.dommages)*2;
            rollData.domArmePlusDom += bdom
          }
          rollData.degats = parseInt(myroll.result) + rollData.domArmePlusDom;
          rollData.loc = RdDUtility.getLocalisation();
          for (let target of game.user.targets) {
            defenseMsg = RdDUtility.buildDefenseChatCard(this, target, rollData );
            specialStr = "<br><strong>Cible</strong> : " + target.actor.data.name;
          }
          specialStr += "<br>Dommages : " + rollData.degats + "<br>Localisation : " + rollData.loc.label;
        } else {
          specialStr = "<br>Echec ! Pas de dommages";        
        }
      }
    }
        
    // Sort management
    let lvl = ""
    if ( rollData.selectedSort) { // Lancement de sort !
      let draconic = rollData.selectedSort.data.draconic;
      specialStr = "<br>Lancement du sort <strong>" + rollData.selectedSort.name + "</strong> : " + draconic.charAt(0).toUpperCase() + draconic.slice(1) + "/" + rollData.selectedSort.data.difficulte + 
              "/" + rollData.selectedSort.data.caseTMR + "/R" + rollData.selectedSort.data.ptreve;
      specialStr += "<br>Depuis la case " + rollData.coord + " (" + TMRUtility.getTMRDescription(rollData.coord).label + ")";
      lvl = rollData.selectedDraconic.name +"/"+ rollData.selectedSort.name;
      let costReve = rollData.selectedSort.data.ptreve;
      let myReve = duplicate(this.data.data.reve.reve);
      if ( rollData.pointsDeTache > 0 ) { // Réussite du sort !
        if (rollData.pointsDeTache >= 4 ) costReve = Math.ceil(costReve/2);
        if (costReve < 1 ) costReve = 1;
        myReve.value = myReve.value - costReve; // Todo 0 pts de reve !!!!
        if (myReve.value < 0) myReve.value = 0;
        await this.update( {"data.reve.reve": myReve } );        
        specialStr += "<br>Réussite du sort pour " + costReve + " Points de Rêve";
        if ( !rollData.isSortReserve) {
          this.currentTMR.close(); // Close TMR !
        } else { // Mise en réserve
          let reserve = duplicate(this.data.data.reve.reserve);
          reserve.list.push( { coord: rollData.coord, sort: duplicate(rollData.selectedSort), draconic: duplicate(rollData.selectedDraconic) });
          await this.update( {"data.reve.reserve": reserve} );
          this.currentTMR.updateSortReserve();
        }
      } else {
        if ( rollData.pointsDeTache == -4) { // Echec total !
          costReve *= 2;
          myReve.value = myReve.value - costReve; // Todo 0 pts de reve !!!!
          if (myReve.value < 0) myReve.value = 0;
          await this.update( {"data.reve.reve": myReve } ); 
          specialStr += "<br><strong>Echec TOTAL</strong> du sort : " + costReve + " Points de Rêve";
        } else { 
          specialStr += "<br>Echec du sort !";
        }
        this.currentTMR.close(); // Close TMR !
      }
      if (myReve.value == 0) { // 0 points de reve
        ChatMessage.create( {title: "Zero Points de Reve !", content: this.name +  " est réduit à 0 Points de Rêve, et tombe endormi !" } );
        this.currentTMR.close(); // Close TMR !
      }
    } else {
      lvl = (rollData.competence) ? rollData.competence.name : rollData.bmValue;
    }

    // Save it for fight in the flags area
    await this.setFlag( 'world', 'rollData', null );
    await this.setFlag( 'world', 'rollData', rollData );
    
    // Final chat message
    let chatOptions = { content:  "<strong>Test : " + rollData.selectedCarac.label + " / " + lvl + "</strong><br>Jet : " + 
                                      rollData.selectedCarac.value + " / " + rollData.finalLevelStr + " -> " + rollData.rollTarget.score + "%<br><strong>Résutat : </strong>" +  result + "<br>" +
                                      "<strong>" + quality + "</strong>" + specialStr + xpmsg,
                          user: game.user._id,
                          title:  "Résultat du test"
                        }
    ChatMessage.create( chatOptions );
      
    // This an attack, generate the defense message
    if ( defenseMsg ) {
      if ( defenseMsg.toSocket) {
        game.socket.emit("system.foundryvtt-reve-de-dragon", {
          msg: "msg_defense",
          data: defenseMsg 
        } );
      } else { 
        defenseMsg.whisper = [ game.user];
        ChatMessage.create( defenseMsg );
      }
    }

    // Get damages!
    if ( encaisser ) {
      this.encaisserDommages( rollData.attackerRoll );
    }
}
  
  /* -------------------------------------------- */  
  updateCarac( caracName, caracValue )  
  {
    let caracpath = "data.carac." + caracName + ".value"
    this.update( { caracpath: caracValue } );
  }
  
  /* -------------------------------------------- */  
  async updateCreatureCompetence( compName, fieldName, compValue )
  {
    let comp = RdDUtility.findCompetence( this.data.items, compName);
    console.log( comp );
    if ( comp ) {
      const update = {_id: comp._id }
      if (fieldName == "niveau")
        update['data.niveau'] = compValue;
      else if (fieldName == "dommages")
        update['data.dommages'] = compValue;
      else 
        update['data.carac_value'] = compValue;
      console.log(update);
      const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
    }
  }

  /* -------------------------------------------- */  
  async updateCompetence( compName, compValue )
  {
    let comp = RdDUtility.findCompetence( this.data.items, compName);
    if ( comp ) {
      let troncList = RdDUtility.isTronc( compName );
      let maxNiveau = compValue;
      if ( troncList  ) {
        let message = "Vous avez modifié une compétence 'tronc'. Vérifiez que les compétences suivantes évoluent ensemble jusqu'au niveau 0 : ";
        for(let troncName of troncList) {
          message += "<br>" + troncName;
        }
        ChatMessage.create( { title : "Compétence Tron", 
        content: message } );
      }
      const update = {_id: comp._id, 'data.niveau': maxNiveau };
      const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
    } else {
      console.log("Competence not found", compName);
    }
  }
  
  /* -------------------------------------------- */  
  async updateCompetenceXP( compName, compValue )
  {
    let comp = RdDUtility.findCompetence( this.data.items, compName);
    if ( comp ) {
      const update = {_id: comp._id, 'data.xp': compValue };
      const updated = await this.updateEmbeddedEntity("OwnedItem", update); // Updates one EmbeddedEntity
    } else {
      console.log("Competence not found", compName);
    }
  }
  
  /* -------------------------------------------- */  
  async updateCompteurValue( fieldName, fieldValue )
  { 
    //console.log("Update", fieldName, fieldValue);
    let content;
    let compteurs = duplicate(this.data.data.compteurs);
    compteurs[fieldName].value =  fieldValue;
    await this.update( {"data.compteurs": compteurs } );
  }

  /* -------------------------------------------- */  
  computeEtatGeneral( ) 
  {
    let data = this.data.data;
    let state = 0;
    state = state - (data.sante.vie.max - data.sante.vie.value);
    if (data.sante.fatigue) // Creatures n'ont pas de fatigue
      state = state + RdDUtility.currentFatigueMalus(data.sante.fatigue.value, data.sante.endurance.max);
    data.compteurs.etat.value = state;
  }
  
  /* -------------------------------------------- */  
  async ajouterRefoulement( value=1) {
    let ret = "none";
    
    let refoulement = duplicate(this.data.data.reve.refoulement);
    refoulement.value = refoulement.value + value;
    let total = new Roll("d20").roll().total;
    if ( total <= refoulement.value ) {
      ChatMessage.create( { title : "Souffle de Dragon", 
                            content: game.user.name + " subit un Souffle de Dragon !" } );
      refoulement.value = 0;
      ret = "souffle";
    }
    await this.update( {"data.reve.refoulement": refoulement } );
    return ret;      
  }
  
  /* -------------------------------------------- */  
  async deleteTMRRencontreAtPosition( ) {
    let rencontres = duplicate(this.data.data.reve.rencontre);
    let len = rencontres.list.length;
    let i = 0;
    //console.log("List", rencontres, len);
    let newTable = [];
    for( i=0; i < len; i++) {
      if (rencontres.list[i].coord != this.data.data.reve.tmrpos.coord ) 
        newTable.push(rencontres.list[i]);
    }
    if ( newTable.length != len ) {
      rencontres.list = newTable;
      //console.log("Result: ", rencontres);
      await this.update( {"data.reve.rencontre": rencontres } );    
    }
  }
  
  /* -------------------------------------------- */  
  async addTMRRencontre( currentRencontre ) {    
    let rencontres = duplicate(this.data.data.reve.rencontre);
    let len = rencontres.list.length;
    let i = 0;
    let already = false;
    for( i=0; i < len; i++) {
      if (rencontres.list[i].coord == this.data.data.reve.tmrpos.coord ) 
        already = true;
    }
    if ( !already ) {
      rencontres.list.push( {coord: this.data.data.reve.tmrpos.coord, rencontre: currentRencontre} ); 
      await this.update( {"data.reve.rencontre": rencontres } );
    }
  }
  
  /* -------------------------------------------- */  
  async updatePointsDeReve( value ) {    
    let reve = duplicate(this.data.data.reve.reve);
    reve.value = reve.value + value;
    await this.update( {"data.reve.reve": reve } );
  }
  
  /* -------------------------------------------- */  
  testSiSonne( sante, endurance ) 
  {
    let result = new Roll("d20").roll().total;
    if ( result <= endurance.value)
       sante.sonne.value = false;
    if ( result > endurance.value || result == 20) // 20 is always a failure
       sante.sonne.value = true;
    if (result == 1) {
      sante.sonne.value = false;
      let xp  = parseInt(this.data.data.carac.constitution.xp) + parseInt(1);
      this.update( {"data.carac.constitution.xp": xp } ); // +1 XP !
      // TODO : Output to chat
    }
  }

  /* -------------------------------------------- */  
  GetNumberBlessures( blessuresListe ) 
  {
    let nbB = 0;
    for ( let b of blessuresListe) {
      nbB += ( b.active) ? 1 : 0;
    }
    return nbB;
  }

  /* -------------------------------------------- */  
  async santeIncDec(name, inc ) {
    const sante = duplicate(this.data.data.sante);
    let data = sante[name];
    let lastValue = data.value; // Useful for Endurance and Sonné
    data.value = data.value + inc;
    if ( data.value > data.max ) data.value = data.max;
    if ( data.value < 0 ) data.value = 0;
    
    if (name == "endurance") {
      if ( inc < 0 ) // Each endurance lost -> fatigue lost
        sante.fatigue.value = sante.fatigue.value - inc
      
      // If endurance is 0 -> -1 vie
      if ( data.value == 0 && sante.vie.value > 0)  {
        sante.vie.value = sante.vie.value - 1;
      }
      let diffVie = sante.vie.max - sante.vie.value;
      if ( data.value > data.max - (diffVie*2) ) {
        data.value = data.max - (diffVie*2);
      }
      if ( data.value < 0 ) data.value = 0; // Security
      
      let blessures   = this.data.data.blessures;
      let nbGraves    = this.GetNumberBlessures(blessures.graves.liste);
      let nbCritiques = this.GetNumberBlessures(blessures.critiques.liste);
      let maxEnd = Math.floor( data.max / nbGraves);
      if (data.value > maxEnd ) data.value = maxEnd;
      if ( nbCritiques > 0 && data.value > 1) data.value = 1;
      
      if (lastValue - data.value > 1) this.testSiSonne(sante, data); // Peut-être sonné si 2 points d'endurance perdus d'un coup
    }
    //console.log(name, inc, data.value);
    
    let diffEndurance = sante.endurance.max - this.data.data.sante.endurance.value;
    if ( sante.fatigue && sante.fatigue.value < diffEndurance) // If endurance lost, then the same amount of fatigue cannot be recovered
      sante.fatigue.value = diffEndurance;
    //console.log("SANTE::::", sante);
    
    await this.update( {"data.sante": sante } );
  }

  /* -------------------------------------------- */  
  async manageBlessureFromSheet( bType, index, active ) {
    let bList    = duplicate(this.data.data.blessures);
    let blessure = bList[bType+"s"].liste[index];
    blessure.active = !blessure.active;
    if ( !blessure.active ) {
      blessure.premiers_soins = 0;
      blessure.soins_complets = 0;
      blessure.jours = 0;
      blessure.localisation = "";  
    }
    //console.log("Blessure update", bType, index, blessure, bList );
    await this.update( { 'data.blessures': bList } );
  }

  /* -------------------------------------------- */  
  async setDataBlessureFromSheet( bType, index, psoins, pcomplets, jours, loc) {
    let bList    = duplicate(this.data.data.blessures);
    let blessure = bList[bType+"s"].liste[index];
    blessure.premiers_soins = psoins;
    blessure.soins_complets = pcomplets;
    blessure.jours = jours;
    blessure.localisation = loc;
    await this.update( { 'data.blessures': bList } );    
  }

  /* -------------------------------------------- */  
  manageBlessures( blessuresData  ) 
  {
    if ( blessuresData.legeres > 0 || blessuresData.graves > 0 || blessuresData.critiques > 0 ) {
      let blessures = duplicate(this.data.data.blessures);
      while ( blessuresData.legeres > 0 ) {
        let nLegeres = 0;
        for (let k=0; k<blessures.legeres.liste.length; k++) {
          let bless = blessures.legeres.liste[k];
          if ( !bless.active ) {
            bless.active = true;
            bless.loc = blessuresData.locName;
            blessuresData.legeres--;
          } else {
            nLegeres++;
          }
        }
        if ( nLegeres == 5) break;
      }
      
      if ( blessuresData.legeres > 0 ) 
        blessuresData.graves += 1;
      
      
      while ( blessuresData.graves > 0) {
        let nGraves = 0;
        for (let k=0; k<blessures.graves.liste.length; k++) {
          let bless = blessures.graves.liste[k];
          if ( !bless.active ) {
            bless.active = true;
            bless.loc = blessuresData.locName;
            blessuresData.graves--;
          } else {
            nGraves++;
          }
        }
        if ( nGraves == 2) break;
      }

      if ( blessuresData.graves > 0 ) 
        blessuresData.critiques = 1;
      
      if ( blessuresData.critiques > 0 ) {
        blessuresData.endurance = this.data.data.sante.endurance.value; // Patch with real endurance current value (ie end -> 0 when critique)
        blessures.critiques.liste[0].active = true;
        blessures.critiques.liste[0].loc  = blessuresData.locName;
      }
      
      this.update( { "data.blessures": blessures } );
    }
  }
  
  /* -------------------------------------------- */  
  async stressTest( ) {
    let compteurs = duplicate(this.data.data.compteurs);
    let stress = compteurs.stress;
    let xp     = compteurs.experience;

    let scoreTarget = RdDUtility.getResolutionField( this.data.data.carac.reve.value, 0 );
    scoreTarget.sig = Math.floor(scoreTarget.score / 2);

    let scoreValue = new Roll("d100").roll().total;
    let newXP  = xp.value;
    let factor = 0;
    let comment = "Echec Total (0%)"

    if (scoreValue <= scoreTarget.part ) {
      scoreValue = new Roll("d100").roll().total;
      if (scoreValue <= scoreTarget.sig) {   
        factor = 1.5;
        comment = "Double Particulière (150%)"
      } else {
        factor = 1;
        comment = "Particulière (100%)"
      }
    } else { 
      if ( scoreValue <= scoreTarget.sig ) {
        factor = 0.75;      
        comment = "Significative (75%)"
      } else { 
        if ( scoreValue <= scoreTarget.score ) {
          factor = 0.5;      
          comment = "Normale (50%)"
        } else if (scoreValue <= scoreTarget.epart) {
          factor = 0.2;
          comment = "Echec (20%)"
        } else if (scoreValue <= scoreTarget.etotal) {
          factor = 0.1;
          comment = "Echec (10%)"
        }
      }
    }

    let xpValue = Math.floor(stress.value * factor);
    stress.value -= xpValue;
    stress.value -= 1;
    if (stress.value < 0 ) stress.value = 0;

    ChatMessage.create( { title: "Jet de Stress", content: "Vous avez transformé "+ xpValue + " points de Stress en Expérience avec une réussite " + comment , 
          whisper: ChatMessage.getWhisperRecipients(game.user.name) } );

    xp.value += xpValue;
    await this.update( { "data.compteurs": compteurs } );
  }

  /* -------------------------------------------- */  
  async rollUnSort( coord ) {
    let draconicList = this.getDraconicList();
    let sortList = this.getSortList();
    
    let rollData = { 
      selectedCarac: this.data.data.carac.reve,
      etat: this.data.data.compteurs.etat.value, 
      draconicList: draconicList,
      sortList: sortList,
      selectedDraconic: draconicList[0],
      selectedSort: sortList[0],
      coord: coord,
      finalLevel: 0,
      bmValue: 0
    }
    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-sort.html', rollData);
    new RdDRollDialog("sort", html, rollData, this ).render(true);
  }
  
  /* -------------------------------------------- */  
  async rollCarac( caracName )
  {
    let rollData = { 
        "selectedCarac": this.data.data.carac[caracName],
        "bonusmalusTable": CONFIG.RDD.bonusmalus,
        "etat": this.data.data.compteurs.etat.value, 
        "finalLevel": 0,
        "bmValue": 0
      }
    console.log(caracName, rollData);
    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-roll-carac.html', rollData);
    new RdDRollDialog("carac", html, rollData, this ).render(true);
  }

  /* -------------------------------------------- */  
  getSortList( )  {
    let sortList = []
    for (const item of this.data.items) {
      if (item.type == "sort" ) 
        sortList.push(item);
    }
    return sortList;
  }

  /* -------------------------------------------- */  
  getDraconicList( )  {
    let draconicList = []
    for (const item of this.data.items) {
      if (item.type == "competence" && item.data.categorie == 'draconic' ) 
        draconicList.push(item);
    }
    return draconicList;
  }

  /* -------------------------------------------- */  
  async displayTMR( isRapide=false ) 
  {
    let minReveValue = (isRapide) ? 3 : 2;
    if (this.data.data.reve.reve.value <= minReveValue ) {
      ChatMessage.create( { title: "Montée impossible !", content: "Vous n'avez plus assez de Points de Reve pour monter dans les Terres Médianes", 
            whisper: ChatMessage.getWhisperRecipients(game.user.name) } );
      return;
    } 
    
    let data = { 
        fatigueHTML:"<table class='table-fatigue'>" + RdDUtility.makeHTMLfatigueMatrix( this.data.data.sante.fatigue.value,  this.data.data.sante.endurance.max ).html() + "</table>",
        draconic: this.getDraconicList(),
        sort: this.getSortList(),
        caracReve: this.data.data.carac.reve.value,
        pointsReve: this.data.data.reve.reve.value,
        isRapide: isRapide
      }    
    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-tmr.html', data );
    this.currentTMR = new RdDTMRDialog(html, this, data );
    this.currentTMR.render(true);
  }
  
  /* -------------------------------------------- */  
  rollArme( armeName ) 
  {   
    let armeItem = RdDUtility.findCompetence( this.data.items, armeName );
    if ( armeItem && armeItem.data.competence ) 
      this.rollCompetence( armeItem.data.competence, armeItem );
    else 
      this.rollCompetence( armeName ); //Bypass mode!
  }
  
  /* -------------------------------------------- */  
  async rollCreatureCompetence( compName, armeItem=undefined, attackerRoll=undefined )
  {
    let compItem = RdDUtility.findCompetence( this.data.items, compName);
    if ( compItem.data.iscombat ) {
      armeItem = { name: compName, data: { dommages: compItem.data.dommages} };
    }
    compItem.data.defaut_carac = "carac_creature"; // Fake default competence
    let rollData = { 
      "competence": compItem, 
      "arme": armeItem,
      "carac": { carac_creature: { label: compName, value: compItem.data.carac_value } },
      "bonusmalusTable": CONFIG.RDD.bonusmalus,
      "etat": this.data.data.compteurs.etat.value, 
      "bmValue": (attackerRoll) ? attackerRoll.bmValue : 0,
      "attackerRoll": attackerRoll,
      "finalLevel": 0
    }

    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html', rollData);
   if (armeItem)  {
      new RdDRollDialog("arme", html, rollData, this ).render(true);
    } else {
      new RdDRollDialog("competence", html, rollData, this ).render(true);
    }
  }

  /* -------------------------------------------- */  
  async rollCompetence( compName, armeItem=undefined, attackerRoll=undefined ) 
  {
    console.log("!!!!!!", compName, armeItem);
    let compItem = RdDUtility.findCompetence( this.data.items, compName);
    let rollData = { 
        "competence": compItem, 
        "arme": armeItem,
        "carac": this.data.data.carac,
        "bonusmalusTable": CONFIG.RDD.bonusmalus,
        "etat": this.data.data.compteurs.etat.value, 
        "bmValue": (attackerRoll) ? attackerRoll.bmValue : 0,
        "attackerRoll": attackerRoll,
        "finalLevel": 0
      }

    let html = await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/dialog-competence.html', rollData);
    if (armeItem)  {
      new RdDRollDialog("arme", html, rollData, this ).render(true);
    } else {
      new RdDRollDialog("competence", html, rollData, this ).render(true);
    }
  }
  
  /* -------------------------------------------- */  
  equiperObjet( itemID )
  {
    let item = this.getOwnedItem(itemID);
    if ( item && item.data.data ) {
      let update = duplicate(item);
      update.data.equipe = !update.data.equipe;
      this.updateEmbeddedEntity("OwnedItem", update); 
    }
  }
  
  /* -------------------------------------------- */  
  computeArmure( locData, domArmePlusDom ) 
  {
    let protection = 0;
    for (const item of this.data.items) {
      if (item.type == "armure" && item.data.equipe) {
        let update = duplicate(item);
        protection += new Roll(update.data.protection.toString()).roll().total;
        update.data.deterioration += domArmePlusDom;
        domArmePlusDom = 0; // Reset it
        if ( update.data.deterioration >= 10) {          
          update.data.deterioration = 0;
          if ( update.data.protection.toString().length == 1 ) 
            update.data.protection = "d"+update.data.protection+"-0";
          else {
            let regex = /d\(d+)\-(\d+)/g;
            let res = regex.exec( update.data.protection );
            update.data.protection = "d"+res[1]+"-"+(parseInt(res[2])+1);            
          }
          /* TODO - POST chat message */
        }
        this.updateEmbeddedEntity("OwnedItem", update); 
      }
    }
    console.log("Final protect", protection);
    return protection;
  }
  
  /* -------------------------------------------- */  
  encaisserDommages( attackerRoll ) 
  {    
    //let attackerRoll = rollData.attackerRoll;
    let degatsReel   = attackerRoll.degats - this.computeArmure(attackerRoll.loc, attackerRoll.domArmePlusDom);
    console.log("RollData from attacker!", attackerRoll, degatsReel);
    
    let result = RdDUtility.computeBlessuresSante(degatsReel);
    this.santeIncDec("vie", result.vie);
    this.santeIncDec("endurance", result.endurance);
    
    result.locName = attackerRoll.loc.label; // Add the localisation namme
    this.manageBlessures( result ); // Will upate the result table
    ChatMessage.create( {title: "Blessures !", content: this.data.name + " a encaissé : " +
      "<br>" + result.legeres    + " légères, " + result.graves     + " graves et " +
      result.critiques  + " critique." +
      "<br>Et perdu : " + 
      "<br>" + result.endurance + " Endurance et " + result.vie + " Points de Vie" } );
  
    this.computeEtatGeneral();
    this.sheet.render(true);
  }
  
  /* -------------------------------------------- */  
  parerAttaque( attackerRoll, armeId )
  {
    let armeItem = this.getOwnedItem(armeId); // Item.data.data !
    console.log("Going to PARY !!!!!!!!!", armeItem, attackerRoll.bmValue);    
    this.rollCompetence(  armeItem.data.data.competence, armeItem.data, attackerRoll );
  }
  
  /* -------------------------------------------- */  
  esquiverAttaque( attackerRoll )
  {
    console.log("Going to DODGE !!!!!!!!!", attackerRoll.bmValue);
    this.rollCompetence( "esquive", undefined, attackerRoll );
  }
  
  /* -------------------------------------------- */  
  /** @override */
  getRollData() {
    const data = super.getRollData();

    return data;
  }
}
