/* -------------------------------------------- */
import { TeDeumUtility } from "../common/tedeum-utility.js";
import { TeDeumRollDialog } from "../dialogs/tedeum-roll-dialog.js";

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

  /* -------------------------------------------- */
  /**
   * Override the create() function to provide additional SoS 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) {

    // 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 == 'pj') {
      const skills = await TeDeumUtility.loadCompendium("fvtt-te-deum.competences")
      data.items = data.items || []
      for (let skill of skills) {
        if (skill.system.isBase || skill.system.score == 1) {
          data.items.push(skill.toObject())
        }
      }
    }

    return super.create(data, options);
  }

  /* -------------------------------------------- */
  async prepareData() {
    super.prepareData()
  }

  /* -------------------------------------------- */
  prepareDerivedData() {
    super.prepareDerivedData();
  }

  /* -------------------------------------------- */
  _preUpdate(changed, options, user) {

    super._preUpdate(changed, options, user);
  }

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

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

  /* -------------------------------------------- */
  getBonusDegats() {
    return game.system.tedeum.config.BONUS_DEGATS[this.system.caracteristiques.puissance.value]
  }
  /* -------------------------------------------- */
  getNbArmures() {
    return game.system.tedeum.config.MAX_ARMURES_LOURDES[this.system.caracteristiques.puissance.value]
  }
  getNbActions() {
    return game.system.tedeum.config.ACTIONS_PAR_TOUR[this.system.caracteristiques.adresse.value]
  }

  /* -------------------------------------------- */
  getCompetences() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'competence') || [])
    return comp;
  }
  /* -------------------------------------------- */
  getGraces() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'grace') || [])
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getArmes() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'arme') || [])
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getEquipements() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'equipement') || [])
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getArmures() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'armure') || [])
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getBlessures() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'blessure') || [])
    for (let c of comp) {
      let blessDef = game.system.tedeum.config.blessures[c.system.typeBlessure]
      c.malus = blessDef.modifier
    }
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getMaladies() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'maladie') || [])
    for (let c of comp) {
      c.malus = "N/A"
      if (c.system.appliquee) {
        let malDef = game.system.tedeum.config.virulence[c.system.virulence]
        c.malus = malDef.modifier
      }
    }
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  getPoisons() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'poison') || [])
    for (let c of comp) {
      c.malus = "N/A"
      if (c.system.appliquee) {
        let poisDef = game.system.tedeum.config.virulencePoison[c.system.virulence]
        c.malus = poisDef.modifier
      }
    }
    TeDeumUtility.sortArrayObjectsByName(comp)
    return comp;
  }
  /* -------------------------------------------- */
  getSanteModifier() {
    let comp = foundry.utils.duplicate(this.items.filter(item => item.type == 'maladie') || [])
    let modTotal = 0
    for (let c of comp) {
      if (c.system.appliquee) {
        let maladieDef = game.system.tedeum.config.virulence[c.system.virulence]
        modTotal += maladieDef.modifier
      }
    }
    let simples= foundry.utils.duplicate(this.items.filter(item => item.type == 'simple') || [])
    for (let c of simples) {
      if (c.system.appliquee) {
        let simpleDef = game.system.tedeum.config.virulencePoison[c.system.virulence]
        modTotal += simpleDef.modifier
      }
    }
    let blessures = foundry.utils.duplicate(this.items.filter(item => item.type == 'blessure') || [])
    for (let c of blessures) {
      let blessDef = game.system.tedeum.config.blessures[c.system.typeBlessure]
      modTotal += blessDef.modifier
    }
    // Si le nombre de blessures est supérieur au score d'endurance, alors malus supplémentaire
    let endurance = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "endurance")
    if ( blessures.length > endurance.system.score) {
      modTotal += -1
    }    
    return modTotal
  }

  /* -------------------------------------------- */
  updateCarac(c, key) {
    c.key = key
    c.name = game.system.tedeum.config.caracteristiques[key].label
    c.generalqualite = game.system.tedeum.config.descriptionValeur[c.value].qualite
    c.qualite = game.system.tedeum.config.descriptionValeur[c.value][key]
    c.dice = game.system.tedeum.config.descriptionValeur[c.value].dice
    c.negativeDice = game.system.tedeum.config.descriptionValeur[c.value].negativeDice
  }

  /* -------------------------------------------- */
  prepareCaracteristiques() {
    let carac = foundry.utils.deepClone(this.system.caracteristiques) 
    for (let key in carac) {
      let c = carac[key]
      this.updateCarac(c, key)
    }
    return carac
  }
  /* -------------------------------------------- */
  prepareProvidence() {
    let providence = foundry.utils.deepClone(this.system.providence)
    providence.name = "Providence"
    providence.qualite = game.system.tedeum.config.providence[providence.value].labelM 
    providence.dice = game.system.tedeum.config.providence[providence.value].diceValue
    return providence
  }
  /* -------------------------------------------- */
  prepareCombat() {
    let combatLoc = foundry.utils.deepClone(this.system.localisation)
    for (let key in combatLoc) {
      combatLoc[key] = foundry.utils.mergeObject(combatLoc[key], game.system.tedeum.config.LOCALISATION[key])
      combatLoc[key].armures = []
      combatLoc[key].protectionTotal = 0
      let armures = this.getArmures()
      for (let armure of armures) {
        if (armure.system.equipe && armure.system.localisation[key].protege) {
          combatLoc[key].armures.push(armure)
          combatLoc[key].protectionTotal += armure.system.protection
        }
      }
      let endurance = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "endurance")
      combatLoc[key].endurance = endurance.system.score + game.system.tedeum.config.LOCALISATION[key].locMod
      combatLoc[key].touche = combatLoc[key].endurance + combatLoc[key].protectionTotal
    }
    return combatLoc
  }
  /* -------------------------------------------- */
  modifyProvidence(value) {
    let providence = foundry.utils.duplicate(this.system.providence)
    providence.value = Math.min(Math.max(providence.value + value, 0), 6)
    this.update( { "system.providence": providence } )
  }
  /* -------------------------------------------- */
  modifyXP(key, value) {
    let xp = this.system.caracteristiques[key].experience
    xp = Math.max(xp + value, 0)
    this.update( { [`system.caracteristiques.${key}.experience`]: xp } )
  }

  /* -------------------------------------------- */
  filterCompetencesByCarac(key) {
    let comp = this.items.filter(item => item.type == 'competence' && item.system.caracteristique == key)
    comp.forEach(c => {
      if (c.system.isBase) {
        c.system.score = this.system.caracteristiques[c.system.caracteristique].value
      }
      let caracDice = game.system.tedeum.config.descriptionValeur[this.system.caracteristiques[c.system.caracteristique].value].dice
      c.system.formula = caracDice + "+" + c.system.score
    })
    return foundry.utils.deepClone( comp || {} )
  }

  /* -------------------------------------------- */
  prepareArbreCompetences() {
    let arbre = foundry.utils.deepClone(this.system.caracteristiques)
    for (let key in arbre) {
      let c = arbre[key]
      this.updateCarac(c, key)
      c.competences = this.filterCompetencesByCarac(key)
    }
    return arbre
  }

  /* -------------------------------------------- */
  getItemById(id) {
    let item = this.items.find(item => item.id == id);
    if (item) {
      item = foundry.utils.duplicate(item)
    }
    return item;
  }

  /* -------------------------------------------- */
  async equipItem(itemId) {
    let item = this.items.find(item => item.id == itemId)
    if (item?.system) {
      if (item.type == "armure") {
        let armor = this.items.find(item => item.id != itemId && item.type == "armor" && item.system.equipped)
        if (armor) {
          ui.notifications.warn("You already have an armor equipped!")
          return
        }
      }
      if (item.type == "shield") {
        let shield = this.items.find(item => item.id != itemId && item.type == "shield" && item.system.equipped)
        if (shield) {
          ui.notifications.warn("You already have a shield equipped!")
          return
        }
      }
      let update = { _id: item.id, "system.equipped": !item.system.equipped };
      await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
    }
  }

  /* ------------------------------------------- */
  async buildContainerTree() {
    let equipments = foundry.utils.duplicate(this.items.filter(item => item.type == "equipment") || [])
    for (let equip1 of equipments) {
      if (equip1.system.iscontainer) {
        equip1.system.contents = []
        equip1.system.contentsEnc = 0
        for (let equip2 of equipments) {
          if (equip1._id != equip2.id && equip2.system.containerid == equip1.id) {
            equip1.system.contents.push(equip2)
            let q = equip2.system.quantity ?? 1
            equip1.system.contentsEnc += q * equip2.system.weight
          }
        }
      }
    }

    // Compute whole enc
    let enc = 0
    for (let item of equipments) {
      //item.data.idrDice = TeDeumUtility.getDiceFromLevel(Number(item.data.idr))
      if (item.system.equipped) {
        if (item.system.iscontainer) {
          enc += item.system.contentsEnc
        } else if (item.system.containerid == "") {
          let q = item.system.quantity ?? 1
          enc += q * item.system.weight
        }
      }
    }
    for (let item of this.items) { // Process items/shields/armors
      if ((item.type == "weapon" || item.type == "shield" || item.type == "armor") && item.system.equipped) {
        let q = item.system.quantity ?? 1
        enc += q * item.system.weight
      }
    }

    // Store local values
    this.encCurrent = enc
    this.containersTree = equipments.filter(item => item.system.containerid == "") // Returns the root of equipements without container

  }

  /* -------------------------------------------- */
  async equipGear(equipmentId) {
    let item = this.items.find(item => item.id == equipmentId);
    if (item?.system) {
      let update = { _id: item.id, "system.equipped": !item.system.equipped };
      await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
    }
  }

  /* -------------------------------------------- */
  clearInitiative() {
    this.getFlag("world", "initiative", -1)
  }

  /* -------------------------------------------- */
  getInitiativeScore() {
    let initiative = this.items.find(it=>it.type == "competence" && it.name.toLowerCase() == "initiative")
    if ( initiative ) {
      return initiative.system.score
    }
    ui.notifications.warn("Impossible de trouver la compétence Initiative pour l'acteur " + this.name)
    return -1;
  }

  /* -------------------------------------------- */
  async deleteAllItemsByType(itemType) {
    let items = this.items.filter(item => item.type == itemType);
    await this.deleteEmbeddedDocuments('Item', items);
  }

  /* -------------------------------------------- */
  async addItemWithoutDuplicate(newItem) {
    let item = this.items.find(item => item.type == newItem.type && item.name.toLowerCase() == newItem.name.toLowerCase())
    if (!item) {
      await this.createEmbeddedDocuments('Item', [newItem]);
    }
  }

  /* -------------------------------------------- */
  async incDecQuantity(objetId, incDec = 0) {
    let objetQ = this.items.get(objetId)
    if (objetQ) {
      let newQ = objetQ.system.quantity + incDec
      if (newQ >= 0) {
        await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantity': newQ }]) // pdates one EmbeddedEntity
      }
    }
  }
  
  /* -------------------------------------------- */
  getCommonRollData() {
    let rollData = TeDeumUtility.getBasicRollData()
    rollData.alias = this.name
    rollData.actorImg = this.img
    rollData.actorId = this.id
    rollData.img = this.img
    rollData.providence = this.prepareProvidence()
    rollData.santeModifier = this.getSanteModifier()
    
    return rollData
  }

  /* -------------------------------------------- */
  getCommonCompetence(compId) {
    let rollData = this.getCommonRollData()
    
    let competence = foundry.utils.duplicate(this.items.find(it => it.type =="competence" && it.id == compId))

    rollData.competence = competence
    let c = foundry.utils.duplicate(this.system.caracteristiques[competence.system.caracteristique])
    this.updateCarac( c, competence.system.caracteristique)
    rollData.carac = c
    rollData.img = competence.img

    return rollData
  }

  /* -------------------------------------------- */
  rollCompetence(compId) {
    let rollData = this.getCommonCompetence(compId)
    rollData.mode = "competence"
    rollData.title = rollData.competence.name
    this.startRoll(rollData).catch("Error on startRoll")
  }


  /* -------------------------------------------- */
  rollArme(armeId, compName = undefined) {
    let weapon = this.items.get(armeId)
    if (weapon) {
      weapon = foundry.utils.duplicate(weapon)
      let rollData = this.getCommonRollData()
      rollData.mode = "arme"
      rollData.arme = weapon
      rollData.img = weapon.img
      rollData.title = weapon.name
      
      // Setup competence + carac
      if (!compName) {
        compName = weapon.system.competence
      }      
      let competence = this.items.find(item => item.type == "competence" && item.name.toLowerCase() == compName.toLowerCase())    
      if ( competence) { 
        rollData.competence = competence
        let c = foundry.utils.duplicate(this.system.caracteristiques[competence.system.caracteristique])
        this.updateCarac( c, competence.system.caracteristique)
        rollData.carac = c
      } else {
        ui.notifications.warn("Impossible de trouver la compétence " + compName)  
        return
      }
      this.startRoll(rollData).catch("Error on startRoll")
    } else {
      ui.notifications.warn("Impossible de trouver l'arme concernée ")
    }
  }

  /* -------------------------------------------- */
  async startRoll(rollData) {
    console.log("startRoll", rollData)
    let rollDialog = await TeDeumRollDialog.create(this, rollData)
    rollDialog.render(true)
  }

}
