import { BoLDefaultRoll, BoLRoll } from "../controllers/bol-rolls.js";
import { BoLUtility } from "../system/bol-utility.js";

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

  static _healthLock = new Set()

  static async create(data, options) {

    // Case of compendium global import
    if (data instanceof Array) {
      return super.create(data, options);
    }
    // If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
    if (data.items) {
      let actor = super.create(data, options);
      return actor;
    }
    
    if (data.type == 'encounter') {
      data.system = { resources: { hero : { max : 0, value: 0 } } }
    }
    if (data.type == 'character') {
      data.system = { resources: { hp : { max : 10, value: 10 } } }
    }

    if (data.type == 'horde') {
      let weapon = {
        name: game.i18n.localize("BOL.ui.hordeAttack"), type: "item",
        img: "ui/icons/attaque_melee.webp",
        system: foundry.utils.duplicate(game.bol.config.defaultNaturalWeapon)
      }
      weapon.system.properties.attackModifiers = 1
      weapon.system.properties.damage = "1d6M"
      data.items = [weapon]

      data.img = "systems/bol/ui/icons/icon-horde-token.webp"
      data.prototypeToken = { texture: "systems/bol/ui/icons/icon-horde-token.webp" }
    }

    return super.create(data, options);
  }

  /** @override */
  prepareData() {

    if (this.type === 'character') {
      this.chartype = 'player'
      this.villainy = false
    }
    if (this.type === 'encounter') {
      this.chartype = 'tough'
      this.villainy = true
    }
    if (this.type === 'creature') { 
      this.villainy = true
    }
    if (this.type == "horde") {
      let weapon = this.items.find(i => i.type === "item" && i.system.subtype === "weapon")
      // Check if the horde attack is inline with the  hordesize
      if (weapon?.system?.properties?.attackModifiers != this.system.hordesize) {
        this.updateEmbeddedDocuments('Item', [{ _id: weapon.id, 'system.properties.attackModifiers': this.system.hordesize }])
      }
    }

    super.prepareData()
  }

  /* -------------------------------------------- */
  async _onCreateOperation(documents, operation, user) {
    await super._onCreateOperation(documents, operation, user);
  }

  /* -------------------------------------------- */
  getTokenSizeFromHordeSize(hordeSize) {
    hordeSize = hordeSize || this.system.hordesize
    // If size > 50 then  max is 50
    let size = Math.min(hordeSize, 20)
    // Compute the size of the token from 1 to 5
    let tokenSize = Math.max((size / 4), 1) // Never below 1
    return tokenSize
  }

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

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

  /* -------------------------------------------- */
  isHeroAdversary() {
    if (this.type === 'character') {
      return true
    }
    return (this.type === 'encounter' && this.chartype == "adversary")
  }

  /* -------------------------------------------- */
  getCharType() {
    if (this.type === 'character') {
      return "player"
    }
    return this.system.chartype
  }

  /* -------------------------------------------- */
  getVillainy() {
    return ( (this.type === 'encounter' && this.chartype == "adversary") || this.chartype == "creature")
  }
  isUndead() {
    return (this.type == "encounter" && this.system.isundead)
  }
  /* -------------------------------------------- */
  getInitiativeMalus() {
    if (this.type === 'encounter' && (this.chartype == "adversary" || this.chartype == "tough")) {
      return this.system.aptitudes.init.value
    }
    return 0
  }
  /* -------------------------------------------- */
  getBougette() {
    if (this.type == "character") {
      let b = foundry.utils.duplicate(this.system.bougette)
      b.label = game.i18n.localize(game.bol.config.bougetteState[String(this.system.bougette.value)])
      b.diceImg = "icons/dice/" + game.bol.config.bougetteDice[String(this.system.bougette.value)] + "black.svg"
      return b
    }
    return undefined
  }

  /* -------------------------------------------- */
  async rollBougette() {
    if (this.type == "character") {
      let attribute = foundry.utils.duplicate(this.system.attributes.vigor)
      let rollData = BoLRoll.getCommonRollData(this, "bougette", attribute, undefined)
      rollData.formula = game.bol.config.bougetteDice[String(this.system.bougette.value)]
      let r = new BoLDefaultRoll(rollData)
      r.roll()
    }
  }

  /* -------------------------------------------- */
  decBougette() {
    if (this.type == "character") {
      let bougette = foundry.utils.duplicate(this.system.bougette)
      bougette.value = Math.max(Number(bougette.value) - 1, 0)
      this.update({ 'system.bougette': bougette })
    }
  }

  /* -------------------------------------------- */
  updateResourcesData() {
    if (this.type == 'character') {
      let newVitality = 10 + this.system.attributes.vigor.value + this.system.resources.hp.bonus
      if (this.system.resources.hp.max != newVitality) {
        let actor = this
        let newHP = foundry.utils.duplicate(this.system.resources.hp)
        newHP.max = newVitality
        setTimeout(function () { actor.update({ 'system.resources.hp': newHP }) }, 800)
      }
      let maxPower = 10 + this.system.attributes.mind.value + this.system.resources.power.bonus
      if (this.system.resources.power.max != maxPower) {
        let actor = this
        let newPower = foundry.utils.duplicate(this.system.resources.power)
        newPower.max = maxPower
        setTimeout(function () { actor.update({ 'system.resources.power': newPower }) }, 800)
      }
    }
  }

  /* -------------------------------------------- */
  prepareDerivedData() {
    if (this.type == "vehicle") {

    } else {
      super.prepareDerivedData()
      if (this.id) {
        this.updateResourcesData()
        this.manageHealthState()
      }
    }
  }

  /* -------------------------------------------- */
  get details() {
    return this.system.details
  }
  addEffectModifiers(myList, dataPath) {
    for (let attr of myList) {
      attr.numModifier = 0
      attr.diceModifier = ""
      let effects = this.items.filter(i => i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier == dataPath + attr.key)
      for (let effect of effects) {
        if (Number(effect.system.properties.modifier)) {
          attr.numModifier += Number(effect.system.properties.modifier)
        } else {
          attr.diceModifier += "+" + effect.system.properties.modifier
        }
      }
    }
  }
  get attributes() {
    let attrList = foundry.utils.duplicate(Object.values(this.system.attributes))
    this.addEffectModifiers(attrList, "system.attributes.")
    return attrList
  }
  get aptitudes() {
    let aptList = Object.values(this.system.aptitudes)
    this.addEffectModifiers(aptList, "system.aptitudes.")
    return aptList
  }

  /* -------------------------------------------- */
  clearRoundModifiers() { // Process data/items that are finished at end of a round
    let foList = this.fightoptions
    for (let fo of foList) {
      if (fo.system.properties.used) {
        this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'system.properties.used': false }])
      }
    }
  }

  /* -------------------------------------------- */
  get defenseValue() {
    let defMod = 0
    let fo = this.getActiveFightOption()
    if (fo && fo.system.properties.fightoptiontype == "intrepid") {
      defMod += -2
    }
    if (fo && fo.system.properties.fightoptiontype == "fulldefense") {
      defMod += 2
    }
    if (fo && fo.system.properties.fightoptiontype == "twoweaponsdef" && !fo.system.properties.used) {
      defMod += 1
      this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'system.properties.used': true }])
    }
    if (fo && fo.system.properties.fightoptiontype == "defense") {
      defMod += 1
    }
    if (fo && fo.system.properties.fightoptiontype == "attack") {
      defMod += -1
    }
    // Apply defense effects
    for (let i of this.items) {
      if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("aptitudes.def")) {
        defMod += Number(i.system.properties.modifier)
      }
    }
    console.log("Defense : ", defMod)
    return this.system.aptitudes.def.value + defMod
  }

  /* -------------------------------------------- */
  getActiveFightOption() {
    let it = this.items.find(i => i.type === "feature" && i.system.subtype === "fightoption" && i.system.properties.activated)
    if (it) {
      return foundry.utils.duplicate(it)
    }
    return undefined
  }

  /* -------------------------------------------- */
  addXPLog(type, name, cost, value) {
    let xplog = {
      name: "XP : " + game.i18n.localize(type), type: "feature",
      img: "icons/magic/symbols/chevron-elipse-circle-blue.webp",
      system: {
        subtype: "xplog", properties: {
          xptype: type,
          xpdate: new Date().toLocaleDateString(),
          xpname: name,
          xpcost: cost,
          xpvalue: value
        }
      }
    }
    this.createEmbeddedDocuments('Item', [xplog])
  }

  /* -------------------------------------------- */
  incAttributeXP(key) {
    let attr = foundry.utils.duplicate(this.system.attributes[key])
    if (attr) {
      let nextXP = (attr.value == -1) ? 2 : attr.value + (attr.value + 1)
      let xp = foundry.utils.duplicate(this.system.xp)
      if (xp.total - xp.spent >= nextXP) {
        attr.value += 1
        xp.spent += nextXP
        this.update({ [`system.attributes.${key}`]: attr, [`system.xp`]: xp })
        this.addXPLog("attribute", key, nextXP, attr.value)
      } else {
        ui.notifications.warn("Pas assez de points d'expérience !")
      }
    }
  }

  /* -------------------------------------------- */
  incAptitudeXP(key) {
    let apt = foundry.utils.duplicate(this.system.aptitudes[key])
    if (apt) {
      let nextXP = (apt.value == -1) ? 1 : apt.value + 2
      let xp = foundry.utils.duplicate(this.system.xp)
      if (xp.total - xp.spent >= nextXP) {
        apt.value += 1
        xp.spent += nextXP
        this.update({ [`system.aptitudes.${key}`]: apt, [`system.xp`]: xp })
        this.addXPLog("aptitude", key, nextXP, apt.value)
      } else {
        ui.notifications.warn("Pas assez de points d'expérience !")
      }
    }
  }
  /* -------------------------------------------- */
  incCareerXP(itemId) {
    let career = this.items.get(itemId)
    if (career) {
      career = foundry.utils.duplicate(career)
      let nextXP = career.system.rank + 1
      let xp = foundry.utils.duplicate(this.system.xp)
      if (xp.total - xp.spent >= nextXP) {
        xp.spent += nextXP
        this.update({ [`system.xp`]: xp })
        this.updateEmbeddedDocuments('Item', [{ _id: career._id, 'system.rank': career.system.rank + 1 }])
        this.addXPLog("career", career.name, nextXP, career.system.rank + 1)
      } else {
        ui.notifications.warn("Pas assez de points d'expérience !")
      }
    }
  }

  /* -------------------------------------------- */
  async toggleFightOption(itemId) {
    let fightOption = this.items.get(itemId)
    let state
    let updates = []

    if (fightOption) {
      fightOption = foundry.utils.duplicate(fightOption)
      if (fightOption.system.properties.activated) {
        state = false
      } else {
        state = true
      }
      updates.push({ _id: fightOption._id, 'system.properties.activated': state }) // Update the selected one
      await this.updateEmbeddedDocuments("Item", updates) // Apply all changes
      // Then notify 
      ChatMessage.create({
        alias: this.name,
        whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
        content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state })
      })

    }
  }

  /*-------------------------------------------- */
  get armorMalusValue() { // used for Fight Options
    for (let armor of this.armors) {
      if (armor.system.properties.armorQuality?.includes("light")) {
        return 1
      }
      if (armor.system.properties.armorQuality?.includes("medium")) {
        return 2
      }
      if (armor.system.properties.armorQuality?.includes("heavy")) {
        return 3
      }
    }
    return 0
  }
  get resources() {
    return Object.values(this.system.resources)
  }
  get boleffects() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "boleffect")
  }
  get xplog() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "xplog")
  }
  get horoscopes() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "horoscope")
  }
  get boons() {
    return foundry.utils.duplicate(this.items.filter(i => i.type === "feature" && i.system.subtype === "boon") || []);
  }
  get flaws() {
    return foundry.utils.duplicate(this.items.filter(i => i.type === "feature" && i.system.subtype === "flaw") || []);
  }
  get careers() {
    return foundry.utils.duplicate(this.items.filter(i => i.type === "feature" && i.system.subtype === "career") || [])
  }
  get origins() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "origin");
  }
  get races() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "race");
  }
  get languages() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "language")
  }
  get fightoptions() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "fightoption")
  }
  get godsfaith() {
    return this.items.filter(i => i.type === "feature" && i.system.subtype === "godsfaith")
  }
  get features() {
    return this.items.filter(i => i.type === "feature")
  }
  get equipment() {
    return this.items.filter(i => i.type === "item")
  }
  get equipmentCreature() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && ((i.system.subtype === "weapon" && i.system.properties.natural === true) || (i.system.subtype === "armor")))
  }
  get armors() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "armor");
  }
  get helms() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "helm");
  }
  get shields() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "shield");
  }
  get vehicleWeapons() {
    return this.items.filter(i => i.type === "item" && i.system.category === "vehicleweapon")
  }
  get weapons() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "weapon")
  }
  get protections() {
    return this.armors.concat(this.helms).concat(this.shields)
  }
  get spells() {
    return this.items.filter(i => i.type === "item" && i.system.category === "spell");
  }
  get alchemy() {
    return this.items.filter(i => i.type === "item" && i.system.category === "alchemy");
  }
  get melee() {
    return this.weapons.filter(i => i.system.properties.melee === true);
  }
  get natural() {
    return this.weapons.filter(i => i.system.properties.natural === true);
  }
  get ranged() {
    return this.weapons.filter(i => i.system.properties.ranged === true);
  }

  get containers() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "container");
  }

  get treasure() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "currency");
  }

  get vehicles() {
    return this.items.filter(i => i.type === "item" && i.system.category === "vehicle");
  }

  get ammos() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && i.system.subtype === "ammunition");
  }

  get misc() {
    return this.items.filter(i => i.type === "item" && i.system.category === "equipment" && (i.system.subtype === "other" || i.system.subtype === "container" || i.system.subtype === "scroll" || i.system.subtype === "jewel"));
  }

  get bonusBoons() {
    let boons = this.items.filter(i => i.type === "feature" && i.system.subtype === "boon" && i.system.properties.isbonusdice)
    return foundry.utils.duplicate(boons || [])
  }
  get malusFlaws() {
    return foundry.utils.duplicate(this.items.filter(i => i.type === "feature" && i.system.subtype === "flaw" && i.system.properties.ismalusdice) || []);
  }

  isSorcerer() {
    return (this.careers.find(item => item.system.properties.sorcerer))
  }
  isAlchemist() {
    return (this.careers.find(item => item.system.properties.alchemist))
  }
  isAstrologer() {
    return (this.careers.find(item => item.system.properties.astrologer))
  }
  isPriest() {
    return (this.careers.find(item => item.system.properties.priest))
  }

  /*-------------------------------------------- */
  getPPCostArmor() {
    let armors = this.armors
    let ppCostArmor = 0
    for (let armor of armors) {
      if (armor.system.worn) {
        ppCostArmor += Number(armor.system.properties.modifiers.powercost) || 0
      }
    }
    return ppCostArmor
  }
  /*-------------------------------------------- */
  getDamageAttributeValue(attrDamage) {
    let attrDamageValue = 0
    if (attrDamage.includes("vigor")) {
      attrDamageValue = this.system.attributes.vigor.value
      if (attrDamage.includes("half")) {
        attrDamageValue = Math.floor(attrDamageValue / 2)
      }
      // Apply vigor effects
      for (let i of this.items) {
        if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("vigor")) {
          attrDamageValue += Number(i.system.properties.modifier)
        }
      }
    }
    return attrDamageValue
  }
  /*-------------------------------------------- */
  getArmorAgiMalus() {
    let malusAgi = 0
    for (let armor of this.protections) {
      if (armor.system.worn) {
        malusAgi += Number(armor.system.properties.modifiers.agility) || 0
      }
    }
    return malusAgi
  }
  /*-------------------------------------------- */
  getArmorInitMalus() {
    let malusInit = 0
    for (let armor of this.protections) {
      if (armor.system.worn) {
        malusInit += Number(armor.system.properties.modifiers.init) || 0
      }
    }
    return malusInit
  }

  /*-------------------------------------------- */
  spendPowerPoint(ppCost) {
    let newPP = this.system.resources.power.value - ppCost
    newPP = (newPP < 0) ? 0 : newPP
    this.update({ 'system.resources.power.value': newPP })
    return newPP
  }

  /*-------------------------------------------- */
  resetAlchemyStatus(alchemyId) {
    let alchemy = this.items.get(alchemyId)
    if (alchemy) {
      this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'system.properties.pccurrent': 0 }])
    }
  }

  /*-------------------------------------------- */
  spentAstrologyPoints(points) {
    let astrology = foundry.utils.duplicate(this.system.resources.astrologypoints)
    astrology.value -= points
    astrology.value = Math.max(astrology.value, 0)
    this.update({ 'system.resources.astrologypoints': astrology })
  }

  /*-------------------------------------------- */
  getHoroscopesBonus() {
    let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
      && it.system.properties.horoscopeanswer == "favorable")
    return astro
  }
  /*-------------------------------------------- */
  getHoroscopesMalus() {
    let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
      && it.system.properties.horoscopeanswer == "unfavorable")
    return astro
  }

  /*-------------------------------------------- */
  manageHoroscope(rollData) {
    //Spent points
    this.spentAstrologyPoints(rollData.astrologyPointsCost)
    if (rollData.horoscopeType == "minor") {
      let horoscope = {
        name: "SITUATION A SPECIFIER", type: "feature",
        img: "icons/magic/perception/eye-ringed-glow-angry-large-red.webp",
        system: {
          subtype: "horoscope", properties: {
            ishoroscopemajor: false,
            horoscopeanswer: (rollData.isSuccess) ? "favorable" : "unfavorable",
            rank: rollData.careerBonus
          }
        }
      }
      this.createEmbeddedDocuments('Item', [horoscope])
    }
    if (rollData.horoscopeType == "major") {
      let actorHoroscope = this
      if (rollData.targetId) {
        let token = game.scenes.current.tokens.get(rollData.targetId)
        actorHoroscope = token.actor
      }
      if (rollData.isSuccess) {
        actorHoroscope.addHeroPoints(1)
      } else {
        actorHoroscope.subHeroPoints(1)
      }
      rollData.horoscopeName = actorHoroscope.name
    }
    if (rollData.horoscopeType == "majorgroup") {
      let rID = foundry.utils.randomID(16)
      let horoscopes = foundry.utils.duplicate(game.settings.get("bol", "horoscope-group"))
      horoscopes[rID] = {
        id: rID,
        name: game.i18n.localize("BOL.ui.groupHoroscope") + this.name,
        maxDice: rollData.careerBonus,
        availableDice: rollData.careerBonus,
        type: (rollData.isSuccess) ? "bonus" : "malus"
      }
      game.settings.set("bol", "horoscope-group", horoscopes)
    }

  }

  /*-------------------------------------------- */
  getAstrologyPoints() {
    return this.system.resources.astrologypoints.value
  }
  /*-------------------------------------------- */
  removeHoroscopeMinor(rollData) {
    let toDel = []
    for (let horo of rollData.selectedHoroscope) {
      toDel.push(horo._id)
    }
    if (toDel.length > 0) {
      this.deleteEmbeddedDocuments('Item', toDel)
    }
  }

  /*-------------------------------------------- */
  async spendAlchemyPoint(alchemyId, pcCost) {
    let alchemy = this.items.get(alchemyId)
    if (alchemy) {
      pcCost = Number(pcCost) || 0
      if (this.system.resources.alchemypoints.value >= pcCost) {
        let newPC = this.system.resources.alchemypoints.value - pcCost
        newPC = (newPC < 0) ? 0 : newPC
        this.update({ 'data.resources.alchemypoints.value': newPC })
        newPC = alchemy.system.properties.pccurrent + pcCost
        await this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'system.properties.pccurrent': newPC }])
      } else {
        ui.notifications.warn(game.i18n.localize("BOL.ui.nomorealchemypoints"))
      }
    }
  }
  /*-------------------------------------------- */
  getAstrologerBonus() {
    let astrologer = this.careers.find(item => item.system.properties.astrologer)
    if (astrologer) {
      return astrologer.system.rank
    }
    return 0;
  }
  /*-------------------------------------------- */
  getAlchemistBonus() {
    let sorcerer = this.careers.find(item => item.system.properties.alchemist)
    if (sorcerer) {
      return sorcerer.system.rank
    }
    return 0;
  }
  /*-------------------------------------------- */
  getSorcererBonus() {
    let sorcerer = this.careers.find(item => item.system.properties.sorcerer)
    if (sorcerer) {
      return sorcerer.system.rank
    }
    return 0;
  }

  /*-------------------------------------------- */
  heroReroll() {
    if (this.type == 'character' || this.system.villainy == 'adversary') {
      return this.system.resources.hero.value > 0;
    }
    return false
  }
  /*-------------------------------------------- */
  getHeroPoints() {
    if (this.type == 'character' || this.system.villainy == 'adversary') {
      return this.system.resources.hero.value
    }
    return 0
  }
  /*-------------------------------------------- */
  getResourcesFromType() {
    let resources = {};
    if (this.type == 'encounter') {
      resources['hp'] = this.system.resources.hp;
      if (this.system.chartype != 'base') {
        resources['faith'] = this.system.resources.faith
        resources['power'] = this.system.resources.power
      }
      if (this.system.chartype == 'adversary' || this.system.chartype == 'creature') {
        resources['hero'] = foundry.utils.duplicate(this.system.resources.hero)
        resources['hero'].label = "BOL.resources.villainy"
      }
    } else {
      resources = this.system.resources;
    }
    return resources
  }

  buildFeatures() {
    return {
      "careers": {
        "label": "BOL.featureCategory.careers",
        "ranked": true,
        "items": this.careers
      },
      "origins": {
        "label": "BOL.featureCategory.origins",
        "ranked": false,
        "items": this.origins
      },
      "races": {
        "label": "BOL.featureCategory.races",
        "ranked": false,
        "items": this.races
      },
      "boons": {
        "label": "BOL.featureCategory.boons",
        "ranked": false,
        "items": this.boons
      },
      "flaws": {
        "label": "BOL.featureCategory.flaws",
        "ranked": false,
        "items": this.flaws
      },
      "languages": {
        "label": "BOL.featureCategory.languages",
        "ranked": false,
        "items": this.languages
      },
      "fightoptions": {
        "label": "BOL.featureCategory.fightoptions",
        "ranked": false,
        "items": this.fightoptions
      },
      "godsfaith": {
        "label": "BOL.featureSubtypes.gods",
        "ranked": false,
        "items": this.godsfaith
      },
      "boleffects": {
        "label": "BOL.featureSubtypes.effects",
        "ranked": false,
        "items": this.boleffects
      }
    }
  }

  buildCombat() {
    return {
      "melee": {
        "label": "BOL.combatCategory.melee",
        "weapon": true,
        "protection": false,
        "blocking": false,
        "ranged": false,
        "options": false,
        "items": this.melee
      },
      "natural": {
        "label": "BOL.combatCategory.natural",
        "weapon": true,
        "protection": false,
        "blocking": false,
        "ranged": false,
        "options": false,
        "items": this.natural
      },
      "ranged": {
        "label": "BOL.combatCategory.ranged",
        "weapon": true,
        "protection": false,
        "blocking": false,
        "ranged": true,
        "options": false,
        "items": this.ranged
      },
      "protections": {
        "label": "BOL.combatCategory.protections",
        "weapon": false,
        "protection": true,
        "blocking": false,
        "ranged": false,
        "options": false,
        "items": this.protections
      },
      "shields": {
        "label": "BOL.combatCategory.shields",
        "weapon": false,
        "protection": false,
        "blocking": true,
        "ranged": false,
        "options": false,
        "items": this.shields
      },
      "fightoptions": {
        "label": "BOL.combatCategory.fightOptions",
        "weapon": false,
        "protection": false,
        "blocking": false,
        "ranged": false,
        "options": true,
        "items": this.fightoptions
      }
    }
  }


  /*-------------------------------------------- */
  buildRollList() {
    let rolls = []
    for (let key in this.system.attributes) {
      let attr = this.system.attributes[key]
      rolls.push({ key: key, value: attr.value, name: attr.label, type: "attribute" })
    }
    for (let key in this.system.aptitudes) {
      if (key != "def") {
        let apt = this.system.aptitudes[key]
        rolls.push({ key: key, value: apt.value, name: apt.label, type: "aptitude" })
      }
    }
    return rolls
  }

  /*-------------------------------------------- */
  buildListeActions() {
    return this.melee.concat(this.ranged).concat(this.natural).concat(this.fightoptions)
  }

  /*-------------------------------------------- */
  async manageHealthState() {
    if (BoLActor._healthLock.has(this.id)) return
    BoLActor._healthLock.add(this.id)
    try {
      let hpID = "lastHP" + this.id
      let lastHP = await this.getFlag("world", hpID)
      if (lastHP != this.system.resources.hp.value && game.user.isGM) { // Only GM sends this
        await this.setFlag("world", hpID, this.system.resources.hp.value)
        let prone = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusProne"))
        let dead = this.effects.find(ef => ef.name == game.i18n.localize("EFFECT.StatusDead"))
        if (this.system.resources.hp.value <= 0) {
          if (!prone) {
            await this.createEmbeddedDocuments("ActiveEffect", [
              { name: game.i18n.localize('EFFECT.StatusProne'), img: 'icons/svg/falling.svg', statuses: ['prone'] }
            ])
          }
          if (this.system.resources.hp.value < -5 && !dead) {
            await this.createEmbeddedDocuments("ActiveEffect", [
              { name: game.i18n.localize('EFFECT.StatusDead'), img: 'icons/svg/skull.svg', statuses: ['dead'] }
            ])
          }
          ChatMessage.create({
            alias: this.name,
            whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
            content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.system.resources.hp.value, isHeroAdversary: this.isHeroAdversary() })
          })
        } else {
          if (prone) {
            await this.deleteEmbeddedDocuments("ActiveEffect", [prone.id])
          }
          if (dead) {
            await this.deleteEmbeddedDocuments("ActiveEffect", [dead.id])
          }
        }
      }
    } finally {
      BoLActor._healthLock.delete(this.id)
    }
  }

  /*-------------------------------------------- */
  async registerInit(rollData) {
    rollData.actor = undefined // Cleanup if present
    await this.setFlag("world", "last-initiative", rollData)
  }

  /*-------------------------------------------- */
  storeVitaliteCombat() {
    this.setFlag("world", "vitalite-before-combat", foundry.utils.duplicate(this.system.resources.hp))
  }
  /*-------------------------------------------- */
  async displayRecuperation() {
    let previousHP = this.getFlag("world", "vitalite-before-combat")
    let lossHP = previousHP.value - this.system.resources.hp.value
    //console.log(">>>>> RECUP INFO", previousHP, this.system.resources.hp.value)
    if (previousHP && lossHP > 0 && this.system.resources.hp.value > 0) {
      let msg = await ChatMessage.create({
        alias: this.name,
        whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
        content: await foundry.applications.handlebars.renderTemplate('systems/bol/templates/chat/chat-recup-information.hbs', {
          name: this.name,
          img: this.img,
          actorId: this.id,
          lossHP: lossHP,
          recupHP: Math.ceil(lossHP / 2)
        })
      })
    }
    this.unsetFlag("world", "vitalite-before-combat")
  }
  /*-------------------------------------------- */
  async applyRecuperation(recupHP) {
    let hp = foundry.utils.duplicate(this.system.resources.hp)
    //console.log("RECUP !!!!", hp, recupHP)
    hp.value += Number(recupHP)
    hp.value = Math.min(hp.value, hp.max)
    this.update({ 'system.resources.hp': hp })
    let msg = await ChatMessage.create({
      alias: this.name,
      whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
      content: game.i18n.format("BOL.chat.inforecup", { name: this.name, recupHP: recupHP })
    })
  }

  /*-------------------------------------------- */
  clearInitiative() {
    this.unsetFlag("world", "last-initiative")
  }

  /*-------------------------------------------- */
  getSize() {
    if (this.system.details.size.length > 0 && game.bol.config.creatureSize[this.system.details.size]) {
      return game.bol.config.creatureSize[this.system.details.size].order
    }
    return game.bol.config.creatureSize["medium"].order // Medium size per default
  }
  /*-------------------------------------------- */
  checkNumeric(myObject) {
    if (myObject) {
      for (let key in myObject) {
        if (myObject[key].value === null || isNaN(myObject[key].value)) {
          myObject[key].value = 0
        }
      }
    }
  }

  /*-------------------------------------------- */
  _preUpdate(data, options, userId) {
    if (data.system?.attributes) {
      this.checkNumeric(data.system.attributes)
    }
    if (data.system?.aptitudes) {
      this.checkNumeric(data.system.aptitudes)
    }
    if (data.system?.resources) {
      this.checkNumeric(data.system.resources)
    }
    // Apply changes in Horde size to Token width/height
    if (this.type == "horde") {
      if (data?.system?.hordesize) { // If horde size is changed}
        let newHP = data.system.hordesize * this.system.hordebasehp
        if (newHP != this.system.resources.hp.value) {
          data.system.resources = { hp: { value: newHP, max: newHP } } // Update HP
        }
        let tokenSize = this.getTokenSizeFromHordeSize(data?.system?.hordesize)
        if (this.isToken && (tokenSize !== this.token.width)) {
          this.token.update({ width: tokenSize, height: tokenSize })
        } else {
          if (tokenSize && (tokenSize !== this.prototypeToken.width)) {
            if (!foundry.utils.hasProperty(data, "prototypeToken.width")) {
              data.prototypeToken ||= {};
              data.prototypeToken.height = tokenSize;
              data.prototypeToken.width = tokenSize;
            }
          }
        }
      }
      if (data?.system?.hordebasehp) {
        let newHP = this.system.hordesize * data.system.hordebasehp
        if (newHP != this.system.resources.hp.value) {
          data.system.resources = { hp: { value: newHP, max: newHP } }
        }
      }
    }

    super._preUpdate(data, options, userId)
  }

  /*-------------------------------------------- */
  getInitiativeRank(rollData = undefined, isCombat = false, combatData = undefined) {
    let fvttInit = 4 // Pietaille par defaut
    if (this.type == 'character') {
      fvttInit = 5
      if (!rollData) {
        if (isCombat) {
          if (game.user.isGM) {
            if (this.hasPlayerOwner) {
              game.socket.emit("system.bol", { name: "msg_request_init_roll", data: { actorId: this.id, combatData } })
            } else {
              BoLRoll.aptitudeCheck(this, "init", undefined, combatData);
            }
          }
        }
      } else {
        if (rollData.isLegendary) {
          fvttInit = 10
        } else if (rollData.isCritical) {
          fvttInit = 9
        } else if (rollData.isSuccess) {
          fvttInit = 8
        } else if (rollData.isFumble) {
          fvttInit = 3
        }
      }
    }
    if (this.getCharType() == 'adversary') {
      fvttInit = 7
    }
    if (this.getCharType() == 'tough') {
      fvttInit = 6
    }
    if (this.getCharType() == 'creature') {
      let mySize = this.getSize()
      let sizeSmall = game.bol.config.creatureSize["small"].order
      let sizeMedium = game.bol.config.creatureSize["medium"].order
      if (mySize >= sizeSmall && mySize <= sizeMedium) {
        fvttInit = 6
      }
      if (mySize > sizeMedium) {
        fvttInit = 7
      }
    }
    return fvttInit
  }

  /*-------------------------------------------- */
  async subHeroPoints(nb) {
    let newHeroP = this.system.resources.hero.value - nb;
    newHeroP = (newHeroP < 0) ? 0 : newHeroP;
    await this.update({ 'system.resources.hero.value': newHeroP });
  }
  /*-------------------------------------------- */
  async addHeroPoints(nb) {
    let newHeroP = this.system.resources.hero.value + nb;
    newHeroP = (newHeroP < 0) ? 0 : newHeroP;
    await this.update({ 'system.resources.hero.value': newHeroP });
  }

  /*-------------------------------------------- */
  incDecResources(target, value) {
    let newValue = this.system.resources[target].value + value
    this.update({ [`system.resources.${target}.value`]: newValue })
  }
  /*-------------------------------------------- */
  async sufferDamage(damage) {
    let updates = {}
    let newHP = this.system.resources.hp.value - damage
    updates['system.resources.hp.value'] = newHP
    if (this.type == "horde") {
      let newSize = Math.ceil(newHP / this.system.hordebasehp)
      updates['system.hordesize'] = newSize
    }
    await this.update(updates)

  }

  /* -------------------------------------------- */
  getArmorFormula() {
    let protectWorn = this.protections.filter(item => item.system.worn)
    let formula = ""
    for (let protect of protectWorn) {
      if (protect.system.subtype == 'helm') {
        formula += "+1"
      } else if (protect.system.subtype == 'armor') {
        if (BoLUtility.getRollArmor()) {
          if (!protect.system.properties.soak.formula || protect.system.properties.soak.formula == "") {
            ui.notifications.warn(game.i18n.localize("BOL.ui.armornoformula", protect.name))
          } else {
            formula += "+" + " max(" + protect.system.properties.soak.formula + ",0)"
          }
        } else {
          if (protect.system.properties.soak.value == undefined) {
            ui.notifications.warn(game.i18n.localize("BOL.ui.armornoformula", protect.name))
          } else {
            formula += "+ " + protect.system.properties.soak.value
          }
        }
      }
    }
    console.log("Protect Formula", formula)
    return (formula == "") ? "0" : formula;
  }

  /* -------------------------------------------- */
  async rollProtection(itemId) {
    let armor = foundry.utils.duplicate(this.items.get(itemId))
    if (armor) {
      let armorFormula = "max(" + armor.system.properties.soak.formula + ", 0)"
      let rollArmor = new Roll(armorFormula)
      await rollArmor.roll()
      rollArmor.toMessage()
    }
  }

  /* -------------------------------------------- */
  rollWeaponDamage(itemId) {
    let weapon = foundry.utils.duplicate(this.items.get(itemId))
    if (weapon) {
      let r = new BoLDefaultRoll({ id: foundry.utils.randomID(16), isSuccess: true, mode: "weapon", weapon: weapon, actorId: this.id, actor: this })
      r.setSuccess(true)
      r.rollDamage()
    }
  }

  /* -------------------------------------------- */
  toggleEquipItem(item) {
    const equipable = item.system.properties.equipable;
    if (equipable) {
      let itemData = foundry.utils.duplicate(item);
      itemData.system.worn = !itemData.system.worn;
      return item.update(itemData);
    }
  }
}