import { SoSCardDeck } from "./sos-card-deck.js";
import { SoSUtility } from "./sos-utility.js";
import { SoSFlipDialog } from "./sos-flip-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 SoSActor 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;
    }

    let items = [];
    let compendiumName = "foundryvtt-shadows-over-sol.skills";
    if ( compendiumName ) {
      let skills = await SoSUtility.loadCompendium(compendiumName);
      items = skills.map(i => i.toObject());
    }
    compendiumName = "foundryvtt-shadows-over-sol.consequences";
    if ( compendiumName ) {
      let consequences = await SoSUtility.loadCompendium(compendiumName)
      items = items.concat( consequences.map(i => i.toObject()) );
    }
    data.items = items;
    console.log(data);

    return super.create(data, options);
  }

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

    this.checkDeck();
    this.controlScores();
  }

  /* -------------------------------------------- */
  checkDeck() {
    if ( !this.cardDeck && this.hasPlayerOwner ) {
      this.cardDeck = new SoSCardDeck();
      this.cardDeck.initCardDeck( this, this.system.internals.deck );
    } 
    if ( !this.hasPlayerOwner ) {
      this.cardDeck = game.system.sos.gmDeck.GMdeck;
      console.log("DECK : ", this.cardDeck);
    }
  }

  /* -------------------------------------------- */
  getDeckSize() {
    return this.cardDeck.getDeckSize();
  }

  /* -------------------------------------------- */
  getEdgesCard( ) {
    let edgesCard = duplicate(this.cardDeck.data.cardEdge);
    for (let edge of edgesCard) {
      edge.path = `systems/foundryvtt-shadows-over-sol/img/cards/${edge.cardName}.webp`
    }
    return edgesCard;
  }
  /* -------------------------------------------- */
  resetDeckFull( ) {
    this.cardDeck.shuffleDeck();
    this.cardDeck.drawEdge( this.system.scores.edge.value );
    this.saveDeck();
  }
  /* -------------------------------------------- */
  drawNewEdge( ) {  
    this.cardDeck.drawEdge( 1 );
    this.saveDeck();
  }

  /* -------------------------------------------- */
  discardEdge( cardName ) {  
    this.cardDeck.discardEdge( cardName  );
    this.saveDeck();
  }
  
  /* -------------------------------------------- */
  resetDeck( ) {
    this.cardDeck.resetDeck();
    this.saveDeck();
  }
  
  /* -------------------------------------------- */
  saveDeck( ) {
    let deck = { deck: duplicate(this.cardDeck.data.deck), 
        discard: duplicate(this.cardDeck.data.discard),
        cardEdge: duplicate(this.cardDeck.data.cardEdge) 
    }
    if ( this.hasPlayerOwner ) {
        this.update( { 'data.internals.deck': deck });
    } else {
      game.settings.set("foundryvtt-shadows-over-sol", "gmDeck", deck );
    }
  }

  /* -------------------------------------------- */
  getDefense( ) {
    return this.system.scores.defense;    
  }  
  /* -------------------------------------------- */
  computeDefense() {
    return { value: Math.ceil((this.system.stats.speed.value + this.system.stats.perception.value + this.system.stats.dexterity.value) / 2) + this.system.scores.defense.bonusmalus,
             critical: this.system.stats.speed.value + this.system.stats.perception.value + this.system.stats.dexterity.value + this.system.scores.defense.bonusmalus
            }
  }  
  /* -------------------------------------------- */
  getEdge( ) {
    return this.system.scores.edge.value;
  }  
  /* -------------------------------------------- */
  getEncumbrance( ) {
    return this.system.scores.encumbrance.value;
  }  
  computeEncumbrance( ) {
    return this.system.stats.strength.value + this.system.scores.encumbrance.bonusmalus;
  }

  /* -------------------------------------------- */
  computeEdge( ) {
    return Math.ceil( (this.system.stats.intelligence.value + this.system.stats.charisma.value) / 2) + this.system.scores.edge.bonusmalus;
  }
  /* -------------------------------------------- */
  getShock( ) {
    return this.system.scores.shock.value;
  }
  computeShock() {
    return Math.ceil( this.system.stats.endurance.value + this.system.stats.determination.value + this.system.scores.dr.value) + this.system.scores.shock.bonusmalus;
  }
  /* -------------------------------------------- */
  getWound( ) {
    return this.system.scores.wound.value;
  }
  computeWound() {
    return Math.ceil( (this.system.stats.strength.value + this.system.stats.endurance.value) / 2) + this.system.scores.wound.bonusmalus;
  }
  
  /* -------------------------------------------- */
  getSkillExperience( skillName ) {
    return this.items.filter( item => item.type == 'skillexperience' && item.system.skill == skillName);
  }

  /* -------------------------------------------- */
  async wornObject( itemID) {
    let item = this.items.get(itemID);
    if (item && item.system) {
      let update = { _id: item.id, "system.worn": !item.system.worn };
      await this.updateEmbeddedDocuments("Item", [update]);
    }
  }
  
  /* -------------------------------------------- */
  async equipObject(itemID) {
    let item = this.items.get(itemID)
    if (item && item.system) {
      let update = { _id: item.id, "system.equiped": !item.system.equiped };
      await this.updateEmbeddedDocuments("Item", [update]);
    }
  }
  
  /* -------------------------------------------- */
  async controlScores() {
    // Defense check
    let defenseData = this.getDefense();
    let newDefenseData = this.computeDefense();
    if ( defenseData.value != newDefenseData.value || defenseData.critical != newDefenseData.critical) {
      await this.update( {'data.scores.defense': newDefenseData});
    }
    // Edge check
    if ( this.getEdge() != this.computeEdge() ) {
      await this.update( {'data.scores.edge.value': this.computeEdge()});
    }
    // Encumbrance
    if ( this.getEncumbrance() != this.system.stats.strength.value ) {
      await this.update( {'data.scores.encumbrance.value': this.computeEncumbrance()   });
    }
    // Shock
    if ( this.getShock() != this.computeShock() ) {
      await this.update( {'data.scores.shock.value': this.computeShock() });
    }
    // Wounds
    if ( this.getWound() != this.computeWound() ) {
      await this.update( {'data.scores.wound.value': this.computeWound() });
    }
  }

  /* -------------------------------------------- */
  async updateWound(woundName, value) {
    let wounds = duplicate(this.system.wounds)
    wounds[woundName] = value;
    await this.update( { 'system.wounds': wounds } );
  }

  /* -------------------------------------------- */
  async updateSkill(skillName, value) {
    let skill = this.items.find( item => item.name == skillName);
    if (skill) {
      const update = { _id: skill.id, 'system.value': value };
      const updated = await this.updateEmbeddedDocuments("Item", [ update] ); // Updates one EmbeddedEntity
    }
  }
  /* -------------------------------------------- */
  async updateSkillExperience(skillName, value) {
    let skill = this.items.find( item => item.name == skillName);
    if (skill) {
      const update = { _id: skill.id, 'system.xp': value };
      const updated = await this.updateEmbeddedDocuments("Item", [update]); // Updates one EmbeddedEntity
    }
  }

  /* -------------------------------------------- */
  getApplicableConsequences( ) {
    let consequences = this.items.filter( item => item.type == 'consequence' && item.system.severity != 'none');
    return consequences;
  }

  /* -------------------------------------------- */
  async rollStat( statKey ) {

    let flipData = {
      mode: 'stat',
      stat: duplicate(this.system.stats[statKey]),
      actor: this,
      modifierList: SoSUtility.fillRange(-10, +10),
      tnList: SoSUtility.fillRange(6, 20),
      consequencesList: duplicate( this.getApplicableConsequences() ),
      weaknessList: this.items.filter( item => item.type == 'weakness' ),
      wounds: duplicate( this.system.wounds),
      malusConsequence: 0,
      bonusConsequence: 0,
      woundMalus: 0
    }
    let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
    new SoSFlipDialog(flipData, html).render(true);
  }
  
  /* -------------------------------------------- */
  async rollSkill( skill ) {
    let flipData = {
      mode: 'skill',
      statList: duplicate(this.system.stats),
      selectedStat: 'strength',
      consequencesList: duplicate( this.getApplicableConsequences() ),
      wounds: duplicate( this.system.wounds),
      skillExperienceList: this.getSkillExperience( skill.name),
      skill: duplicate(skill),
      actor: this,
      modifierList: SoSUtility.fillRange(-10, +10),
      tnList: SoSUtility.fillRange(6, 20),
      malusConsequence: 0,
      bonusConsequence: 0,
      woundMalus: 0,
      bonusSkillXP: 0
    }
    flipData.statList['nostat'] = { label: "No stat (ie defaulting skills)", value: 0, cardsuit: "none" }
    let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
    new SoSFlipDialog(flipData, html).render(true);
  }
  
  /* -------------------------------------------- */
  async rollWeapon( weapon ) {
    let target = SoSUtility.getTarget();
    let skill, selectedStatName;
    if ( weapon.system.category == 'ballistic' || weapon.system.category == 'laser'  ) {
      skill = this.items.find( item => item.name == 'Guns');
      selectedStatName = 'dexterity';
    } else if ( weapon.system.category == 'melee' )  {
      skill = this.items.find( item => item.name == 'Melee');
      selectedStatName = 'dexterity';
    } else if ( weapon.system.category == 'grenade' ) {
      skill = this.items.find( item => item.name == 'Athletics');
      selectedStatName = 'dexterity';
    }

    let flipData = {
      mode: 'weapon',
      weapon: duplicate(weapon),
      statList: duplicate(this.system.stats),
      target: target,
      selectedStat: selectedStatName,
      consequencesList: duplicate( this.getApplicableConsequences() ),
      skillExperienceList: this.getSkillExperience( skill.name),
      wounds: duplicate( this.system.wounds),
      skill: duplicate(skill),
      actor: this,
      modifierList: SoSUtility.fillRange(-10, +10),
      tnList: SoSUtility.fillRange(6, 20),
      malusConsequence: 0,
      bonusConsequence: 0,
      woundMalus: 0,
      bonusSkillXP: 0
    }

    console.log(flipData);

    flipData.statList['nostat'] = { label: "No stat (ie defaulting skills)", value: 0, cardsuit: "none" }
    let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/dialog-flip.html', flipData);
    new SoSFlipDialog(flipData, html).render(true);
  }

  /* -------------------------------------------- */
  async checkDeath( ) {
    if ( this.system.scores.currentwounds.value >= this.system.scores.wound.value*2) {
      let woundData = {
        name: this.name, 
        wounds: this.system.wounds,
        currentWounds: this.system.scores.currentwounds.value,
        totalWounds: this.system.scores.wound.value  
      }
      let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-character-death.html',  woundData );
      ChatMessage.create( { content: html, whisper: ChatMessage.getWhisperRecipients(this.name).concat(ChatMessage.getWhisperRecipients("GM") ) } )
    }
  }

  /* -------------------------------------------- */
  computeCurrentWounds( ) {
    let wounds = this.system.wounds;
    return wounds.light + (wounds.moderate*2) + (wounds.severe*3) + (wounds.critical*4);
  }

  /* -------------------------------------------- */
  async applyConsequenceWound( severity, consequenceName) {
    if ( severity == 'none') return; // Nothing !

    let wounds = duplicate(this.system.wounds);
    if (severity == 'light' ) wounds.light += 1;
    if (severity == 'moderate' ) wounds.moderate += 1;
    if (severity == 'severe' ) wounds.severe += 1;
    if (severity == 'critical' ) wounds.critical += 1;

    let sumWound = wounds.light + (wounds.moderate*2) + (wounds.severe*3) + (wounds.critical*4);
    let currentWounds = duplicate(this.system.scores.currentwounds);
    currentWounds.value = sumWound;
    await this.update( { 'data.scores.currentwounds': currentWounds, 'data.wounds': wounds } );

    let woundData = { 
      name: this.name, 
      consequenceName: consequenceName,
      severity: severity, 
      wounds: wounds,
      currentWounds: sumWound,
      totalWounds: this.system.scores.wound.value
    }
    let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-consequence.html',  woundData );
    ChatMessage.create( { content: html, whisper: ChatMessage.getWhisperRecipients(this.name).concat(ChatMessage.getWhisperRecipients("GM")) } )

    this.checkDeath();
  }

  /* -------------------------------------------- */
  async addObjectToContainer( itemId, containerId ) {
    let container = this.items.find( item => item.id == containerId && item.type == 'container')
    let object = this.items.find( item => item.id == itemId )
    console.log("Found", container, object)
    if ( container ) {
      if ( object.type == 'container') {
        ui.notifications.warn("Only 1 level of container... sorry");
        return
      }
      let alreadyInside = this.items.filter( item => item.system.containerid && item.system.containerid == containerId);
      if ( alreadyInside.length >= container.system.container ) {
        ui.notifications.warn("Container is already full !");
      } else { 
        setTimeout(function() { this.updateEmbeddedDocuments( "Item", [{ _id: object.id, 'system.containerid':containerId }])}, 800 )
      }
    } else if ( object && object.system.containerid) { // remove from container
      setTimeout(function() { this.updateEmbeddedDocuments( "Item", [{ _id: object.id, 'system.containerid':"" }])}, 800 )
    }
  }

  /* -------------------------------------------- */
  async applyWounds( flipData ) {
    if ( flipData.damageStatus == 'no_damage') {
      let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-nodamage-taken.html', flipData );
      ChatMessage.create( { content: html, whisper: ChatMessage.getWhisperRecipients(this.name).concat(ChatMessage.getWhisperRecipients("GM")) } );        
      return;
    }

    let wounds = duplicate(this.system.wounds);
    for (let wound of flipData.woundsList ) {
      if (wound == 'L' ) wounds.light += 1;
      if (wound == 'M' ) wounds.moderate += 1;
      if (wound == 'S' ) wounds.severe += 1;
      if (wound == 'C' ) wounds.critical += 1;
    }
    // Compute total
    let sumWound = wounds.light + (wounds.moderate*2) + (wounds.severe*3) + (wounds.critical*4);
    let currentWounds = duplicate(this.system.scores.currentwounds);
    currentWounds.value = sumWound;
    if ( sumWound >= this.system.scores.wound.value) {
      let bleeding = this.items.find( item => item.type == 'consequence' && item.name == 'Bleeding');
      let newSeverity = SoSUtility.increaseConsequenceSeverity( bleeding.system.severity );
      await this.updateEmbeddedDocuments( "Item", [ { _id: bleeding.id, 'system.severity': newSeverity} ] );
      flipData.isBleeding = newSeverity;
    }
    // Stun consequence
    if ( flipData.nbStun > 0) {
      let stun = this.items.find( item => item.type == 'consequence' && item.name == 'Stun');
      let newSeverity = stun.system.severity;
      for(let i=0; i<flipData.nbStun; i++) {
        newSeverity = SoSUtility.increaseConsequenceSeverity( newSeverity );
      }
      await this.updateEmbeddedDocuments( "Item", [ { _id: stun.id, 'system.severity': newSeverity} ] );
      flipData.isStun = newSeverity;
    }
    await this.update( { 'data.scores.currentwounds': currentWounds, 'system.wounds': wounds } );

    flipData.defenderName = this.name;
    flipData.wounds = wounds;
    flipData.currentWounds = sumWound;
    flipData.totalWounds = this.system.scores.wound.value;
    let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-taken.html', flipData );
    ChatMessage.create( { content: html, whisper: ChatMessage.getWhisperRecipients(this.name).concat(ChatMessage.getWhisperRecipients("GM")) } );

    this.checkDeath();
  }

}
