import { ChatVente } from "../achat-vente/chat-vente.js";
import { ChatUtility } from "../chat-utility.js";
import { SYSTEM_SOCKET_ID } from "../constants.js";
import { Grammar } from "../grammar.js";
import { Monnaie } from "../item-monnaie.js";
import { ITEM_TYPES } from "../item.js";
import { Misc } from "../misc.js";
import { RdDAudio } from "../rdd-audio.js";
import { RdDConfirm } from "../rdd-confirm.js";
import { RdDUtility } from "../rdd-utility.js";
import { SystemCompendiums } from "../settings/system-compendiums.js";

export class RdDBaseActor extends Actor {

  static _findCaracNode(carac, name) {
    return Object.entries(carac)
      .filter(it => Grammar.equalsInsensitive(it[1].label, name))
      .map(it => it[0])
      .find(it => it)
  }
  static $findCaracByName(carac, name) {
    const caracList = Object.entries(carac);
    let entry = Misc.findFirstLike(name, caracList, { mapper: it => it[0], description: 'caractéristique' });
    if (!entry || entry.length == 0) {
      entry = Misc.findFirstLike(name, caracList, { mapper: it => it[1].label, description: 'caractéristique' });
    }
    return entry && entry.length > 0 ? carac[entry[0]] : undefined;
  }

  static getDefaultImg(itemType) {
    return game.system.rdd.actorClasses[itemType]?.defaultIcon ?? defaultItemImg[itemType];
  }

  static init() {
    Hooks.on("preUpdateItem", (item, change, options, id) => Misc.documentIfResponsible(item.parent)?.onPreUpdateItem(item, change, options, id))
    Hooks.on("createItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onCreateItem(item, options, id))
    Hooks.on("deleteItem", (item, options, id) => Misc.documentIfResponsible(item.parent)?.onDeleteItem(item, options, id))
    Hooks.on("updateActor", (actor, change, options, actorId) => Misc.documentIfResponsible(actor)?.onUpdateActor(change, options, actorId))
  }

  static onSocketMessage(sockmsg) {
    switch (sockmsg.msg) {
      case "msg_remote_actor_call":
        return RdDBaseActor.onRemoteActorCall(sockmsg.data, sockmsg.userId);
    }
  }

  static remoteActorCall(callData, userId = undefined) {
    userId = userId ?? Misc.firstConnectedGMId();
    if (userId == game.user.id) {
      RdDBaseActor.onRemoteActorCall(callData, userId);
      return false;
    }
    else {
      game.socket.emit(SYSTEM_SOCKET_ID, { msg: "msg_remote_actor_call", data: callData, userId: userId });
      return true;
    }
  }

  static onRemoteActorCall(callData, userId) {
    if (userId == game.user.id) {
      const actor = RdDBaseActor.getRealActor(callData?.actorId, callData?.tokenId);
      if (Misc.isOwnerPlayerOrUniqueConnectedGM(actor)) { // Seul le joueur choisi effectue l'appel: le joueur courant si propriétaire de l'actor, ou le MJ sinon
        const args = callData.args;
        console.info(`RdDBaseActor.onRemoteActorCall: pour l'Actor ${callData.actorId}, appel de RdDBaseActor.${callData.method}(`, ...args, ')');
        actor[callData.method](...args);
      }
    }
  }

  static getRealActor(actorId, tokenId) {
    if (tokenId) {
      let token = canvas.tokens.get(tokenId)
      if (token) {
        return token.actor
      }
    }
    return game.actors.get(actorId)
  }

  getAlias() {
    if (this.token?.name != null && this.token != this.prototypeToken) {
      return this.token.name
    }
    return this.name
  }

  isPersonnageJoueur() { return false }

  static extractActorMin = (actor) => { return { id: actor?.id, type: actor?.type, name: actor?.name, img: actor?.img }; };

  /**
   * Cette methode surcharge Actor.create() pour ajouter si besoin des Items par défaut:
   * compétences et monnaies.
   *
   * @param {Object} actorData template d'acteur auquel ajouter des informations.
   * @param {Object} options   optionspour customiser la création
   */
  static async create(actorData, options) {
    // import depuis un compendium
    if (actorData instanceof Array) {
      return super.create(actorData, options);
    }
    // Création d'un acteur avec des items (uniquement en cas de duplication): pas besoin d'ajouter d'items
    if (actorData.items) {
      return await super.create(actorData, options);
    }
    actorData.items = [];
    if (actorData.type == "personnage") {
      const competences = await SystemCompendiums.getCompetences(actorData.type);
      actorData.items = actorData.items.concat(competences.map(i => i.toObject()))
        .concat(Monnaie.monnaiesStandard());
    }
    else if (actorData.type == "commerce") {
      actorData.items = actorData.items.concat(Monnaie.monnaiesStandard());
    }
    return super.create(actorData, options);
  }

  constructor(docData, context = {}) {
    if (!context.rdd?.ready) {
      foundry.utils.mergeObject(context, { rdd: { ready: true } });
      const ActorConstructor = game.system.rdd.actorClasses[docData.type];
      if (ActorConstructor) {
        if (!docData.img) {
          docData.img = ActorConstructor.defaultIcon;
        }
        return new ActorConstructor(docData, context);
      }
    }
    context.rdd = undefined
    super(docData, context);
  }

  findCaracByName(name) {
    name = Grammar.toLowerCaseNoAccent(name)
    switch (name) {
      case 'reve-actuel': case 'reve actuel':
        return this.system.carac.reve
      case 'chance-actuelle': case 'chance actuelle':
        return this.system.carac.chance
      case 'vie':
        return this.system.sante.vie
    }

    const carac = this.system.carac;
    return RdDBaseActor.$findCaracByName(carac, name);
  }

  getCaracByName(name) {
    switch (Grammar.toLowerCaseNoAccent(name)) {
      case 'reve-actuel': case 'reve actuel':
        return this.getCaracReveActuel();
      case 'chance-actuelle': case 'chance-actuelle':
        return this.getCaracChanceActuelle();
    }
    return this.findCaracByName(name);
  }

  /* -------------------------------------------- */
  async _preCreate(data, options, user) {
    await super._preCreate(data, options, user);

    // Configure prototype token settings
    const prototypeToken = {};
    if (this.type === "personnage") Object.assign(prototypeToken, {
      sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
    });
    this.updateSource({ prototypeToken });
  }

  /* -------------------------------------------- */
  prepareData() {
    super.prepareData()
    this.prepareActorData()
    this.cleanupConteneurs()
    this.computeEtatGeneral()
    this.computeEncTotal()
  }

  async prepareActorData() { }
  async computeEtatGeneral() { }
  /* -------------------------------------------- */
  findPlayer() {
    return game.users.players.find(player => player.active && player.character?.id == this.id);
  }

  isCreatureEntite() { return this.isCreature() || this.isEntite() }
  isCreature() { return false }
  isEntite(typeentite = []) { return false }
  isVehicule() { return false }
  isPersonnage() { return false }
  getItem(id, type = undefined) {
    const item = this.items.get(id);
    if (type == undefined || (item?.type == type)) {
      return item;
    }
    return undefined;
  }

  listeSuivants(filter = suivant => true) { return [] }
  listeSuivants(filter = suivant => true) { return [] }
  listItems(type = undefined) { return (type ? this.itemTypes[type] : this.items); }
  filterItems(filter, type = undefined) { return (type ? this.itemTypes[type] : this.items)?.filter(filter) ?? []; }
  findItemLike(idOrName, type) {
    return this.getItem(idOrName, type)
      ?? Misc.findFirstLike(idOrName, this.listItems(type), { description: Misc.typeName('Item', type) });
  }

  getMonnaie(id) { return this.findItemLike(id, 'monnaie'); }
  getEncombrementMax() { return 0 }

  /* -------------------------------------------- */
  async onPreUpdateItem(item, change, options, id) { }
  async onCreateItem(item, options, id) { }
  async onDeleteItem(item, options, id) { }
  async onUpdateActor(update, options, actorId) { }

  async onTimeChanging(oldTimestamp, newTimestamp) {
    this.items.filter(it => it.isFinPeriode(oldTimestamp, newTimestamp))
      .forEach(async it => await it.onFinPeriodeTemporel(oldTimestamp, newTimestamp))
  }

  async creerObjetParMJ(object) {
    if (!Misc.isFirstConnectedGM()) {
      RdDBaseActor.remoteActorCall({
        tokenId: this.token?.id,
        actorId: this.id,
        method: 'creerObjetParMJ',
        args: [object]
      });
      return;
    }
    await this.createEmbeddedDocuments('Item', [object])
  }

  /* -------------------------------------------- */
  async cleanupConteneurs() {
    if (Misc.isOwnerPlayerOrUniqueConnectedGM(this)) {
      let updates = this.itemTypes['conteneur']
        .filter(c => c.system.contenu.filter(id => this.getItem(id) == undefined).length > 0)
        .map(c => { return { _id: c._id, 'system.contenu': c.system.contenu.filter(id => this.getItem(id) != undefined) } });
      if (updates.length > 0) {
        await this.updateEmbeddedDocuments("Item", updates)
      }
    }
  }

  /* -------------------------------------------- */
  getFortune() {
    return Monnaie.getFortune(this.itemTypes['monnaie']);
  }

  /* -------------------------------------------- */
  async itemQuantiteIncDec(id, value) {
    let item = this.getItem(id);
    if (item && item.isInventaire()) {
      const quantite = Math.max(0, item.system.quantite + value);
      await item.update({ 'system.quantite': quantite });
    }
  }

  computePrixTotalEquipement() {
    return this.items.filter(it => it.isInventaire())
      .filter(it => !it.isMonnaie())
      .map(it => it.valeurTotale())
      .reduce(Misc.sum(), 0);
  }

  async payerSols(depense) {
    depense = Number(depense);
    if (depense == 0) {
      return;
    }
    let fortune = this.getFortune();
    console.log("payer", game.user.character, depense, fortune);
    // TODO: passer en handlebars
    let msg = "";
    if (fortune >= depense) {
      await Monnaie.optimiserFortune(this, fortune - depense);
      msg = `Vous avez payé <strong>${depense} Sols</strong>, qui ont été soustraits de votre argent.`;
      RdDAudio.PlayContextAudio("argent"); // Petit son
    } else {
      msg = "Vous n'avez pas assez d'argent pour payer cette somme !";
    }

    ChatMessage.create({
      whisper: ChatUtility.getOwners(this),
      content: msg
    })
  }

  async depenserSols(sols) {
    let reste = this.getFortune() - Number(sols);
    if (reste >= 0) {
      await Monnaie.optimiserFortune(this, reste);
    }
    return reste;
  }

  async ajouterSols(sols, fromActorId = undefined) {
    sols = Number(sols);
    if (sols == 0) {
      return;
    }
    if (sols < 0) {
      ui.notifications.error(`Impossible d'ajouter un gain de ${sols} <0`);
      return;
    }
    if (fromActorId && !game.user.isGM) {
      RdDBaseActor.remoteActorCall({
        userId: Misc.connectedGMOrUser(),
        tokenId: this.token?.id,
        actorId: this.id,
        method: 'ajouterSols', args: [sols, fromActorId]
      });
    }
    else {
      const fromActor = game.actors.get(fromActorId)
      await Monnaie.optimiserFortune(this, sols + this.getFortune());

      RdDAudio.PlayContextAudio("argent"); // Petit son
      ChatMessage.create({
        whisper: ChatUtility.getOwners(this),
        content: `Vous avez reçu <strong>${sols} Sols</strong> ${fromActor ? " de " + fromActor.name : ''}, qui ont été ajoutés à votre argent.`
      });
    }
  }

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

  getQuantiteDisponible(item) {
    return item?.isService() ? undefined : item?.getQuantite();
  }

  /* -------------------------------------------- */
  async achatVente(achat) {
    if (achat.vendeurId == achat.acheteurId) {
      ui.notifications.info("Inutile de se vendre à soi-même");
      return;
    }
    if (!Misc.isFirstConnectedGM()) {
      RdDBaseActor.remoteActorCall({
        actorId: achat.vendeurId ?? achat.acheteurId,
        method: 'achatVente',
        args: [achat]
      });
      return;
    }
    const cout = Number(achat.prixTotal ?? 0);
    const vendeur = achat.vendeurId ? game.actors.get(achat.vendeurId) : undefined;
    const acheteur = achat.acheteurId ? game.actors.get(achat.acheteurId) : undefined;
    const quantite = (achat.choix.nombreLots ?? 1) * (achat.vente.tailleLot);
    const itemVendu = vendeur?.getItem(achat.vente.item._id) ?? game.items.get(achat.vente.item._id);
    if (!itemVendu) {
      ChatUtility.notifyUser(achat.userId, 'warn', vendeur ? `Le vendeur n'a pas plus de ${achat.vente.item.name} !` : `Impossible de retrouver: ${achat.vente.item.name} !`);
      return;
    }
    if (vendeur && !vendeur.verifierQuantite(itemVendu, quantite)) {
      ChatUtility.notifyUser(achat.userId, 'warn', `Le vendeur n'a pas assez de ${itemVendu.name} !`);
      return
    }
    if (acheteur && !acheteur.verifierFortune(cout)) {
      ChatUtility.notifyUser(achat.userId, 'warn', `Vous n'avez pas assez d'argent pour payer ${Math.ceil(cout / 100)} sols !`);
      return;
    }
    await vendeur?.vendre(itemVendu, quantite, cout);
    await acheteur?.acheter(itemVendu, quantite, cout, achat)

    if (cout > 0) {
      RdDAudio.PlayContextAudio("argent");
    }
    const chatAchatItem = foundry.utils.duplicate(achat.vente);
    chatAchatItem.quantiteTotal = quantite;
    ChatMessage.create({
      user: achat.userId,
      speaker: { alias: (acheteur ?? vendeur).getAlias() },
      whisper: ChatUtility.getOwners(this),
      content: await renderTemplate('systems/foundryvtt-reve-de-dragon/templates/chat-achat-item.html', chatAchatItem)
    });

    if (!achat.vente.quantiteIllimite) {
      if (achat.vente.nbLots <= achat.choix.nombreLots) {
        ChatUtility.removeChatMessageId(achat.chatMessageIdVente);
      }
      else if (achat.chatMessageIdVente) {
        await ChatVente.diminuerQuantiteAchatVente(achat.chatMessageIdVente, achat.choix.nombreLots)
      }
    }
  }

  async vendre(item, quantite, cout) {
    await this.ajouterSols(cout);
    await this.decrementerQuantiteItem(item, quantite);
  }

  async acheter(item, quantite, cout, achat) {
    await this.depenserSols(cout)
    const createdItemId = await this.creerQuantiteItem(item, quantite)
    if (achat.choix.consommer && item.type == 'nourritureboisson' && createdItemId != undefined) {
      achat.choix.doses = achat.choix.nombreLots;
      await this.consommerNourritureboisson(createdItemId, achat.choix, achat.vente.actingUserId);
    }
  }

  verifierFortune(cout) {
    return this.getFortune() >= cout;
  }

  verifierQuantite(item, quantiteDemande) {
    const disponible = this.getQuantiteDisponible(item);
    return disponible == undefined || disponible >= quantiteDemande;
  }

  async consommerNourritureboisson(itemId, choix, userId) { }

  async decrementerQuantiteItem(item, quantite, options = { supprimerSiZero: true }) {
    if (item.isService()) {
      return;
    }
    const itemId = item.id;
    let resteQuantite = (item.system.quantite ?? 1) - quantite;
    if (resteQuantite <= 0) {
      if (options.supprimerSiZero) {
        await this.deleteEmbeddedDocuments("Item", [item.id]);
      }
      else {
        await this.updateEmbeddedDocuments("Item", [{ _id: itemId, 'system.quantite': 0 }]);
      }
      if (resteQuantite < 0) {
        ui.notifications.warn(`La quantité de ${item.name} était insuffisante, l'objet a donc été supprimé`)
      }
    }
    else if (resteQuantite > 0) {
      const realItem = this.getItem(item.id)
      realItem.update({ 'system.quantite': resteQuantite });
      await this.updateEmbeddedDocuments("Item", [{ _id: item.id, 'system.quantite': resteQuantite }]);
    }
  }

  async creerQuantiteItem(item, quantite) {
    if (this.canReceive(item)) {
      const isItemEmpilable = "quantite" in item.system;
      const baseItem = {
        type: item.type,
        img: item.img,
        name: item.name,
        system: foundry.utils.mergeObject(item.system, { quantite: isItemEmpilable ? quantite : undefined }, { inplace: false })
      };
      const newItems = isItemEmpilable ? [baseItem] : Array.from({ length: quantite }, (_, i) => baseItem);
      const items = await this.createEmbeddedDocuments("Item", newItems);
      return items.length > 0 ? items[0].id : undefined;
    }
  }

  /* -------------------------------------------- */
  async computeEncTotal() {
    if (!this.pack) {
      this.encTotal = this.items.map(it => it.getEncTotal()).reduce(Misc.sum(), 0);
      return this.encTotal;
    }
    return 0;
  }

  getEncTotal() {
    return Math.floor(this.encTotal ?? 0);
  }

  async createItem(type, name = undefined) {
    if (!name) {
      name = 'Nouveau ' + Misc.typeName('Item', type);
    }
    await this.createEmbeddedDocuments('Item', [{ name: name, type: type }], { renderSheet: true });
  }

  canReceive(item) {
    return false;
  }

  async processDropItem(params) {
    const targetActorId = this.id
    const sourceActorId = params.sourceActorId
    const sourceTokenId = params.sourceTokenId
    const itemId = params.itemId
    const destId = params.destId
    const srcId = params.srcId
    if (sourceActorId && sourceActorId != targetActorId) {
      console.log("Moving objects", sourceActorId, sourceTokenId, targetActorId, itemId);
      this.moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId);
      return false;
    }
    let result = true;
    const item = this.getItem(itemId);
    if (item?.isInventaire('all') && sourceActorId == targetActorId) {
      // rangement
      if (srcId != destId && itemId != destId) { // déplacement de l'objet
        const src = this.getItem(srcId);
        const dest = this.getItem(destId);
        const cible = this.getContenantOrParent(dest);
        const [empilable, message] = item.isInventaireEmpilable(dest);
        if (empilable) {
          await dest.empiler(item)
          result = false;
        }
        // changer de conteneur
        else if (!cible || this.conteneurPeutContenir(cible, item)) {
          await this.enleverDeConteneur(item, src, params.onEnleverConteneur);
          await this.ajouterDansConteneur(item, cible, params.onAjouterDansConteneur);
          if (message && !dest.isConteneur()) {
            ui.notifications.info(cible
              ? `${message}<br>${item.name} a été déplacé dans: ${cible.name}`
              : `${message}<br>${item.name} a été sorti du conteneur`);
          }
        }
      }
    }
    await this.computeEncTotal();
    return result;
  }

  getContenantOrParent(dest) {
    if (!dest || dest.isConteneur()) {
      return dest;
    }
    return this.getContenant(dest);
  }

  getContenant(item) {
    return this.itemTypes['conteneur'].find(it => it.system.contenu.includes(item.id));
  }


  /* -------------------------------------------- */
  conteneurPeutContenir(dest, moved) {
    if (!dest) {
      return true;
    }
    if (!dest.isConteneur()) {
      return false;
    }
    if (moved.isConteneurContenu(dest)) {
      ui.notifications.warn(`Impossible de déplacer un conteneur parent (${moved.name}) dans un de ses contenus ${dest.name} !`);
      return false;
    }

    // Calculer le total actuel des contenus
    const encContenu = dest.getEncContenu();
    const newEnc = moved.getEncTotal(); // Calculer le total actuel du nouvel objet
    const placeDisponible = Misc.keepDecimals(dest.system.capacite - encContenu - newEnc, 4)

    // Teste si le conteneur de destination a suffisament de capacité pour recevoir le nouvel objet
    if (placeDisponible < 0) {
      ui.notifications.warn(
        `Le conteneur ${dest.name} a une capacité de ${dest.system.capacite}, et contient déjà ${encContenu}.
        Impossible d'y ranger: ${moved.name} d'encombrement ${newEnc}!`);
      return false;
    }
    return true;
  }

  /* -------------------------------------------- */
  /** Ajoute un item dans un conteneur, sur la base de leurs ID */
  async ajouterDansConteneur(item, conteneur, onAjouterDansConteneur) {
    if (conteneur?.isConteneur()) {
      item.estContenu = true;
      const nouveauContenu = [...conteneur.system.contenu, item.id];
      await conteneur.update({ 'system.contenu': nouveauContenu });
      onAjouterDansConteneur(item.id, conteneur.id)
    }
    else {
      item.estContenu = false;
      await conteneur?.update({ 'system.-=contenu': undefined })
    }
  }

  /* -------------------------------------------- */
  /** Fonction de remise à plat de l'équipement (ie vide les champs 'contenu') */
  async nettoyerConteneurs() {
    RdDConfirm.confirmer({
      settingConfirmer: "confirmation-vider",
      content: `<p>Etes vous certain de vouloir vider tous les conteneurs ?</p>`,
      title: 'Vider les conteneurs',
      buttonLabel: 'Vider',
      onAction: async () => {
        const corrections = [];
        for (let item of this.items) {
          if (item.estContenu) {
            item.estContenu = undefined;
          }
          if (item.system.contenu != undefined) {
            if (item.type == 'conteneur') {
              corrections.push({ _id: item.id, 'system.contenu': [] });
            }
            else {
              corrections.push({ _id: item.id, 'system.-=contenu': undefined });
            }
          }
        }
        if (corrections.length > 0) {
          await this.updateEmbeddedDocuments('Item', corrections);
        }
      }
    });
  }

  /* -------------------------------------------- */
  buildSubConteneurObjetList(conteneurId, deleteList) {
    let conteneur = this.getItem(conteneurId);
    if (conteneur?.type == 'conteneur') { // Si c'est un conteneur
      for (let subId of conteneur.system.contenu) {
        let subObj = this.getItem(subId);
        if (subObj) {
          if (subObj.type == 'conteneur') {
            this.buildSubConteneurObjetList(subId, deleteList);
          }
          deleteList.push({ id: subId, conteneurId: conteneurId });
        }
      }
    }
  }

  /* -------------------------------------------- */
  async deleteAllConteneur(itemId, options) {
    let list = [];
    list.push({ id: itemId, conteneurId: undefined }); // Init list
    this.buildSubConteneurObjetList(itemId, list);
    await this.deleteEmbeddedDocuments('Item', list.map(it => it.id), options);
  }

  /* -------------------------------------------- */
  /**
   * Supprime un item d'un conteneur, sur la base de leurs ID
   */
  async enleverDeConteneur(item, conteneur, onEnleverDeConteneur) {
    if (conteneur) {
      if (conteneur.isConteneur()) {
        const contenu = conteneur.system.contenu.filter(id => id != item.id);
        await conteneur.update({ 'system.contenu': contenu });
        onEnleverDeConteneur();
      }
      else {
        await conteneur.update({ 'system.-=contenu': undefined })
      }
    }
    item.estContenu = false;
  }

  /* -------------------------------------------- */
  async moveItemsBetweenActors(itemId, sourceActorId, sourceTokenId) {
    let sourceActor = RdDBaseActor.getRealActor(sourceActorId, sourceTokenId)
    let itemsList = [{ id: itemId, conteneurId: undefined }]
    sourceActor.buildSubConteneurObjetList(itemId, itemsList); // Get itemId list

    const itemsDataToCreate = itemsList.map(it => sourceActor.getItem(it.id))
      .map(it => foundry.utils.duplicate(it))
      .map(it => { it.system.contenu = []; return it; });
    let newItems = await this.createEmbeddedDocuments('Item', itemsDataToCreate);

    let itemMap = this._buildMapOldNewId(itemsList, newItems);

    for (let item of itemsList) { // Second boucle pour traiter la remise en conteneurs
      // gestion conteneur/contenu
      if (item.conteneurId) { // l'Objet était dans un conteneur
        const newConteneurId = itemMap[item.conteneurId];
        const newConteneur = this.getItem(newConteneurId);
        const newItemId = itemMap[item.id]; // Get newItem

        console.log('New conteneur filling!', newConteneur, newItemId, item);
        const nouveauContenu = [...newConteneur.system.contenu, newItemId]
        await newConteneur.update({ 'system.contenu': nouveauContenu })
      }
    }
    const deletedItemIds = itemsList.map(it => it.id)
    await sourceActor.deleteEmbeddedDocuments('Item', deletedItemIds);
  }

  _buildMapOldNewId(itemsList, newItems) {
    let itemMap = {};
    for (let i = 0; i < itemsList.length; i++) {
      itemMap[itemsList[i].id] = newItems[i].id; // Pour garder le lien ancien / nouveau
    }
    return itemMap;
  }

  /* -------------------------------------------- */
  async postActorToChat(modeOverride) {
    let chatData = {
      doctype: 'Actor',
      id: this.id,
      type: this.type,
      img: this.img,
      pack: this.pack,
      name: this.getAlias(),
      system: { description: this.system.description }
    }
    renderTemplate('systems/foundryvtt-reve-de-dragon/templates/post-actor.html', chatData)
      .then(html => ChatMessage.create(RdDUtility.chatDataSetup(html, modeOverride)));
  }

  actionImpossible(action) {
    ui.notifications.info(`${this.getAlias()} ne peut pas faire cette action: ${action}`)

  }

  async jetEthylisme() { this.actionImpossible("jet d'éthylisme") }
  async rollAppelChance() { this.actionImpossible("appel à la chance") }
  async jetDeMoral() { this.actionImpossible("jet de moral") }

  async actionPrincipale(item, onActionItem = async () => { }) {
    switch (item.type) {
      case ITEM_TYPES.conteneur: return await item.sheet.render(true);
    }
    return undefined
  }
  async resetItemUse() { }
  async incDecItemUse(itemId, inc = 1) { }
  getItemUse(itemId) { return 0; }
  async finDeRound(options = { terminer: false }) { }
  isActorCombat() { return false }
  getCaracInit(competence) { return 0 }
  listActionsCombat() { return [] }
  listActionsPossessions() {
    return this.itemTypes[ITEM_TYPES.possession]
      .map(p => {
        return {
          name: p.name,
          action: 'possession',
          system: {
            competence: p.name,
            possessionid: p.system.possessionid,
          }
        }
      })
  }
}