/**
 * Gestion des ActiveEffects pour Mournblade CYD 2.0
 * Ce module fournit des utilitaires pour créer, appliquer et gérer les effets actifs
 * sur les Acteurs et les Items.
 */

export class MournbladeCYD2Effects {

  /**
   * Initialise le système de gestion des effets
   */
  static init() {
    console.log("MournbladeCYD2 | Initializing ActiveEffects management");
    
    // Hook pour appliquer les modifications des effets
    Hooks.on("applyActiveEffect", (effect, change, current, delta, changes) => {
      return this._onApplyActiveEffect(effect, change, current, delta, changes);
    });
    
    // Hook pour supprimer les modifications des effets
    Hooks.on("removeActiveEffect", (effect, change, current, delta, changes) => {
      return this._onRemoveActiveEffect(effect, change, current, delta, changes);
    });
  }

  /**
   * Parse une valeur d'effet en nombre
   * Gère les strings comme "+2", "-3", "5"
   * @param {string|number} value - Valeur à parser
   * @returns {number} - Valeur numérique
   * @private
   */
  static _parseEffectValue(value) {
    if (typeof value === 'number') return value;
    if (typeof value !== 'string') return 0;
    
    const trimmed = value.trim();
    if (!trimmed) return 0;
    
    if (trimmed.startsWith('+')) {
      return parseFloat(trimmed.substring(1)) || 0;
    } else if (trimmed.startsWith('-')) {
      return -(parseFloat(trimmed.substring(1)) || 0);
    }
    
    return parseFloat(trimmed) || 0;
  }

  /**
   * Hook appelé lorsqu'un effet est appliqué
   * Permet de personnaliser le calcul des modifications
   * @private
   */
  static _onApplyActiveEffect(effect, change, current, delta, changes) {
    // Pour Mournblade, nous voulons gérer les valeurs string (ex: "+1", "-2")
    // Convertir delta en nombre si nécessaire
    const numericDelta = this._parseEffectValue(delta);
    const numericCurrent = current != null ? Number(current) : 0;
    
    if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
      return numericCurrent + numericDelta;
    }
    
    // Si on ne peut pas calculer, retourner delta tel quel
    return delta;
  }

  /**
   * Hook appelé lorsqu'un effet est supprimé
   * @private
   */
  static _onRemoveActiveEffect(effect, change, current, delta, changes) {
    // Logique inverse de l'application
    // Foundry gère déjà la suppression, ce hook est pour des calculs personnalisés
    const numericDelta = this._parseEffectValue(delta);
    const numericCurrent = current != null ? Number(current) : 0;
    
    if (!isNaN(numericDelta) && !isNaN(numericCurrent)) {
      return numericCurrent - numericDelta;
    }
    
    return current;
  }

  /* -------------------------------------------- */
  /*  Méthodes de création d'effets                 */
  /* -------------------------------------------- */

  /**
   * Crée un effet simple de bonus/malus à un attribut
   * @param {string} name - Nom de l'effet
   * @param {string} attribute - Attribut cible (adr, pui, cla, pre, tre, vigueur, etc.)
   * @param {number|string} value - Valeur du bonus/malus
   * @param {object} options - Options supplémentaires
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createSimpleEffect(name, attribute, value, options = {}) {
    // Validation des paramètres
    if (!name || typeof name !== "string") {
      console.warn("MournbladeCYD2 | Effect name must be a non-empty string");
      return null;
    }
    
    if (value == null) {
      console.warn("MournbladeCYD2 | Effect value cannot be null or undefined");
      return null;
    }

    const attributeKey = this.getAttributeKey(attribute);
    if (!attributeKey) {
      console.warn(`MournbladeCYD2 | Unknown attribute: ${attribute}`);
      return null;
    }

    // Normaliser la valeur en string
    const valueString = String(value).trim();

    return {
      name: name.trim(),
      icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
      description: (options.description || "").trim(),
      changes: [
        {
          key: attributeKey,
          mode: CONST.ActiveEffect.MODES.ADD,
          value: valueString,
          priority: options.priority ?? 0
        }
      ],
      disabled: Boolean(options.disabled),
      duration: options.duration || {},
      origin: options.origin || null,
      tint: options.tint || "",
      transfer: options.transfer !== false,
      statuses: options.statuses || [],
      flags: options.flags || {}
    };
  }

  /**
   * Crée un effet de bonus permanent
   * @param {string} name - Nom de l'effet
   * @param {string} attribute - Attribut cible
   * @param {number|string} value - Valeur du bonus
   * @returns {Object} - Données de l'effet
   */
  static createPermanentEffect(name, attribute, value) {
    return this.createSimpleEffect(name, attribute, value, {
      duration: {},
      type: "base"
    });
  }

  /**
   * Crée un effet temporaire (rounds, turns, etc.)
   * @param {string} name - Nom de l'effet
   * @param {string} attribute - Attribut cible
   * @param {number|string} value - Valeur du bonus/malus
   * @param {string} durationType - Type de durée (rounds, turns, seconds, combat)
   * @param {number} durationValue - Valeur de la durée
   * @returns {Object} - Données de l'effet
   */
  static createTemporaryEffect(name, attribute, value, durationType, durationValue) {
    return this.createSimpleEffect(name, attribute, value, {
      duration: { type: durationType, value: durationValue },
      type: "temp"
    });
  }

  /**
   * Crée un effet avec plusieurs modifications
   * @param {string} name - Nom de l'effet
   * @param {Array} changes - Array de modifications {key, mode, value}
   * @param {object} options - Options supplémentaires
   * @returns {Object} - Données de l'effet
   */
  static createMultiEffect(name, changes, options = {}) {
    return {
      name: name,
      icon: options.icon || "systems/fvtt-mournblade-cyd-2-0/assets/icons/capacite.webp",
      description: options.description || "",
      changes: changes.map(c => ({
        key: c.key,
        mode: c.mode || CONST.ActiveEffect.MODES.ADD,
        value: c.value.toString(),
        priority: c.priority || 0
      })),
      disabled: options.disabled || false,
      duration: options.duration || {},
      origin: options.origin || null,
      tint: options.tint || "",
      transfer: options.transfer !== false
    };
  }

  /* -------------------------------------------- */
  /*  Méthodes d'application d'effets              */
  /* -------------------------------------------- */

  /**
   * Applique un effet à un acteur
   * @param {Actor} actor - L'acteur cible
   * @param {Object|ActiveEffect} effectData - Données de l'effet ou instance ActiveEffect
   * @returns {Promise<ActiveEffect|null>} - L'effet créé ou null
   */
  static async applyEffectToActor(actor, effectData) {
    if (!actor || !actor.canUserModify(game.user, "update")) return null;
    
    let effect;
    if (effectData instanceof foundry.documents.ActiveEffect) {
      effect = effectData;
    } else if (effectData?.toObject) {
      effect = effectData;
    } else {
      effect = new CONFIG.ActiveEffect.documentClass(effectData);
    }
    
    try {
      const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", [effect.toObject()]);
      return createdEffects[0];
    } catch (error) {
      console.error("MournbladeCYD2 | Failed to apply effect:", error);
      ui.notifications?.error(
        game.i18n?.localize("EFFECT.applyError") || 
        `Erreur: Impossible d'appliquer l'effet (${error.message})`
      );
      return null;
    }
  }

  /**
   * Applique les effets d'un item à un acteur
   * @param {Item} item - L'item source
   * @param {Actor} actor - L'acteur cible
   * @returns {Promise<Array<ActiveEffect>>} - Liste des effets appliqués
   */
  static async applyItemEffectsToActor(item, actor) {
    if (!item?.effects?.length || !actor) return [];
    if (!actor.canUserModify(game.user, "update")) return [];
    
    const effectsToApply = [];
    for (const effectData of item.effects) {
      // Par défaut, appliquer automatiquement SAUF si explicitement désactivé
      const autoApply = effectData.getFlag("mournblade-cyd2", "autoApply");
      if (autoApply !== false) {
        effectsToApply.push({
          ...effectData.toObject(),
          origin: item.uuid,
          name: `${item.name}: ${effectData.name}`
        });
      }
    }
    
    if (effectsToApply.length === 0) return [];
    
    try {
      const createdEffects = await actor.createEmbeddedDocuments("ActiveEffect", effectsToApply);
      return createdEffects;
    } catch (error) {
      console.error("MournbladeCYD2 | Failed to apply item effects:", error);
      ui.notifications?.error(
        game.i18n?.localize("EFFECT.applyItemError") || 
        `Erreur: Impossible d'appliquer les effets de l'item`
      );
      return [];
    }
  }

  /* -------------------------------------------- */
  /*  Méthodes de gestion d'effets                  */
  /* -------------------------------------------- */

  /**
   * Désactive un effet
   * @param {Actor|Item} owner - Le propriétaire de l'effet
   * @param {string} effectId - ID de l'effet
   * @returns {Promise<ActiveEffect|null>} - L'effet désactivé
   */
  static async disableEffect(owner, effectId) {
    if (!owner?.canUserModify(game.user, "update")) return null;
    
    const effect = owner.effects.get(effectId);
    if (!effect) return null;
    
    await effect.update({ disabled: true });
    return effect;
  }

  /**
   * Active un effet
   * @param {Actor|Item} owner - Le propriétaire de l'effet
   * @param {string} effectId - ID de l'effet
   * @returns {Promise<ActiveEffect|null>} - L'effet activé
   */
  static async enableEffect(owner, effectId) {
    if (!owner?.canUserModify(game.user, "update")) return null;
    
    const effect = owner.effects.get(effectId);
    if (!effect) return null;
    
    await effect.update({ disabled: false });
    return effect;
  }

  /**
   * Toggle l'état d'un effet (actif/désactivé)
   * @param {Actor|Item} owner - Le propriétaire de l'effet
   * @param {string} effectId - ID de l'effet
   * @returns {Promise<ActiveEffect|null>} - L'effet togglé
   */
  static async toggleEffect(owner, effectId) {
    if (!owner?.canUserModify(game.user, "update")) return null;
    
    const effect = owner.effects.get(effectId);
    if (!effect) return null;
    
    await effect.update({ disabled: !effect.disabled });
    return effect;
  }

  /**
   * Supprime un effet
   * @param {Actor|Item} owner - Le propriétaire de l'effet
   * @param {string} effectId - ID de l'effet
   * @returns {Promise<ActiveEffect|null>} - L'effet supprimé
   */
  static async deleteEffect(owner, effectId) {
    if (!owner?.canUserModify(game.user, "delete")) return null;
    
    const effect = owner.effects.get(effectId);
    if (!effect) return null;
    
    await owner.deleteEmbeddedDocuments("ActiveEffect", [effectId]);
    return effect;
  }

  /* -------------------------------------------- */
  /*  Méthodes utilitaires                         */
  /* -------------------------------------------- */

  /**
   * Obtient la clé complète pour un attribut
   * @param {string} attribute - Attribut court (adr, pui, cla, pre, tre, vigueur, etc.)
   * @returns {string|null} - Clé complète ou null
   */
  static getAttributeKey(attribute) {
    if (!attribute) return null;
    
    const config = game.system.mournbladecyd2?.config;
    if (!config?.effectAttributeKeys) return null;
    
    // Normaliser en minuscules pour correspondre à la config
    const normalizedAttribute = attribute.toLowerCase().trim();
    return config.effectAttributeKeys[normalizedAttribute] || null;
  }

  /**
   * Obtient tous les attributs modifiables
   * @returns {Object} - Map des attributs courts vers les clés complètes
   */
  static getAllAttributeKeys() {
    const config = game.system.mournbladecyd2?.config;
    return config?.effectAttributeKeys || {};
  }

  /**
   * Obtient les effets actifs d'un acteur
   * @param {Actor} actor - L'acteur
   * @returns {Array<ActiveEffect>} - Liste des effets actifs (non désactivés)
   */
  static getActiveEffects(actor) {
    if (!actor?.effects) return [];
    return actor.effects.filter(e => !e.disabled);
  }

  /**
   * Obtient les effets désactivés d'un acteur
   * @param {Actor} actor - L'acteur
   * @returns {Array<ActiveEffect>} - Liste des effets désactivés
   */
  static getDisabledEffects(actor) {
    if (!actor?.effects) return [];
    return actor.effects.filter(e => e.disabled);
  }

  /**
   * Obtient les effets par origine
   * @param {Actor} actor - L'acteur
   * @param {string} originUuid - UUID de l'origine
   * @returns {Array<ActiveEffect>} - Liste des effets de cette origine
   */
  static getEffectsByOrigin(actor, originUuid) {
    if (!actor?.effects) return [];
    return actor.effects.filter(e => e.origin === originUuid);
  }

  /**
   * Obtient les effets temporaires en cours
   * @param {Actor} actor - L'acteur
   * @returns {Array<ActiveEffect>} - Liste des effets temporaires actifs
   */
  static getActiveTemporaryEffects(actor) {
    if (!actor?.effects) return [];
    return actor.effects.filter(e => !e.disabled && e.duration?.type);
  }

  /**
   * Calcule la valeur totale des modifications pour une clé donnée
   * @param {Actor} actor - L'acteur
   * @param {string} key - Clé à vérifier
   * @returns {number} - Somme des modifications
   */
  static getTotalModificationForKey(actor, key) {
    if (!actor?.effects) return 0;
    
    let total = 0;
    for (const effect of actor.effects) {
      if (effect.disabled) continue;
      
      for (const change of effect.changes || []) {
        if (change.key === key && change.mode === CONST.ActiveEffect.MODES.ADD) {
          total += Number(change.value) || 0;
        }
      }
    }
    
    return total;
  }

  /**
   * Obtient toutes les modifications actives groupées par clé
   * @param {Actor} actor - L'acteur
   * @returns {Object} - Objet avec les clés et les valeurs totales
   */
  static getAllActiveModifications(actor) {
    if (!actor?.effects) return {};
    
    const modifications = {};
    
    for (const effect of actor.effects) {
      if (effect.disabled) continue;
      
      for (const change of effect.changes || []) {
        if (!modifications[change.key]) {
          modifications[change.key] = {
            value: 0,
            effects: []
          };
        }
        
        // Appliquer selon le mode
        const numericValue = Number(change.value) || 0;
        switch (change.mode) {
          case CONST.ActiveEffect.MODES.ADD:
            modifications[change.key].value += numericValue;
            break;
          case CONST.ActiveEffect.MODES.OVERRIDE:
            modifications[change.key].value = numericValue;
            modifications[change.key].overridden = true;
            break;
          case CONST.ActiveEffect.MODES.MULTIPLY:
            // Ne peut pas être additionné, stocké séparément
            if (!modifications[change.key].multipliers) {
              modifications[change.key].multipliers = [];
            }
            modifications[change.key].multipliers.push(numericValue);
            break;
        }
        
        modifications[change.key].effects.push(effect.name);
      }
    }
    
    return modifications;
  }

  /* -------------------------------------------- */
  /*  Méthodes de création d'effets prédéfinis       */
  /* -------------------------------------------- */

  /**
   * Crée un effet de bonus d'attribut
   * @param {string} attribute - Attribut (adr, pui, cla, pre, tre)
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createAttributeBonusEffect(attribute, value, source = "Effet") {
    const attrNames = {
      adr: "Adresse",
      pui: "Puissance",
      cla: "Clairvoyance",
      pre: "Présence",
      tre: "Trempe"
    };
    
    return this.createSimpleEffect(
      `${source}: Bonus de ${attrNames[attribute] || attribute}`,
      attribute,
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/attributs.webp",
        type: "base"
      }
    );
  }

  /**
   * Crée un effet de malus d'attribut
   * @param {string} attribute - Attribut
   * @param {number} value - Valeur du malus (positif)
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createAttributeMalusEffect(attribute, value, source = "Effet") {
    const attrNames = {
      adr: "Adresse",
      pui: "Puissance",
      cla: "Clairvoyance",
      pre: "Présence",
      tre: "Trempe"
    };
    
    return this.createSimpleEffect(
      `${source}: Malus de ${attrNames[attribute] || attribute}`,
      attribute,
      `-${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/malus.webp",
        type: "base"
      }
    );
  }

  /**
   * Crée un effet de bonus à la Vigueur
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createVigueurBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus de Vigueur`,
      "vigueur",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/vigueur.webp",
        type: "base"
      }
    );
  }

  /**
   * Crée un effet de bonus au Seuil de Pouvoir
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createSeuilPouvoirBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus au Seuil de Pouvoir`,
      "seuilPouvoir",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/ame.webp",
        type: "base"
      }
    );
  }

  /**
   * Crée un effet de bonus à la Bonne Aventure
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createBonneAventureBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus de Bonne Aventure`,
      "bonneAventure",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/bonneaventure.webp",
        type: "base"
      }
    );
  }

  /**
   * Crée un effet de bonus à l'Initiative
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createInitiativeBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus d'Initiative`,
      "initiative",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/initiative.webp",
        type: "temp",
        duration: { type: "rounds", value: 1 }
      }
    );
  }

  /**
   * Crée un effet de bonus à la Défense
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createDefenseBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus de Défense`,
      "defense",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/defense.webp",
        type: "temp",
        duration: { type: "rounds", value: 1 }
      }
    );
  }

  /**
   * Crée un effet de bonus à la Protection
   * @param {number} value - Valeur du bonus
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createProtectionBonusEffect(value, source = "Effet") {
    return this.createSimpleEffect(
      `${source}: Bonus de Protection`,
      "protection",
      `+${value}`,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/protection.webp",
        type: "base"
      }
    );
  }

  /* -------------------------------------------- */
  /*  Méthodes de gestion des statuts               */
  /* -------------------------------------------- */

  /**
   * Crée un effet qui applique un statut
   * @param {string} status - Nom du statut
   * @param {string} source - Source de l'effet
   * @returns {Object} - Données de l'effet
   */
  static createStatusEffect(status, source = "Effet") {
    return {
      name: `${source}: ${status}`,
      icon: `systems/fvtt-mournblade-cyd-2-0/assets/icons/status_${status.toLowerCase()}.webp`,
      description: `Applique le statut ${status}`,
      changes: [],
      statuses: [status],
      disabled: false,
      duration: {},
      origin: null,
      tint: "",
      transfer: true
    };
  }

  /**
   * Crée un effet d'adversité bleue
   * @param {number} value - Nombre d'adversités
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createAdversiteBleueEffect(value) {
    if (value == null) return null;
    
    return this.createSimpleEffect(
      `Adversité Bleue: +${value}`,
      "adversite.bleue",
      value,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_bleue.webp",
        duration: { type: "rounds", value: 1 },
        statuses: ["adversite-bleue"]
      }
    );
  }

  /**
   * Crée un effet d'adversité rouge
   * @param {number} value - Nombre d'adversités
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createAdversiteRougeEffect(value) {
    if (value == null) return null;
    
    return this.createSimpleEffect(
      `Adversité Rouge: +${value}`,
      "adversite.rouge",
      value,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_rouge.webp",
        duration: { type: "rounds", value: 1 },
        statuses: ["adversite-rouge"]
      }
    );
  }

  /**
   * Crée un effet d'adversité noire
   * @param {number} value - Nombre d'adversités
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createAdversiteNoireEffect(value) {
    if (value == null) return null;
    
    return this.createSimpleEffect(
      `Adversité Noire: +${value}`,
      "adversite.noire",
      value,
      {
        icon: "systems/fvtt-mournblade-cyd-2-0/assets/icons/gemme_noire.webp",
        duration: { type: "rounds", value: 1 },
        statuses: ["adversite-noire"]
      }
    );
  }

  /* -------------------------------------------- */
  /*  Méthodes pour les Runes                        */
  /* -------------------------------------------- */

  /**
   * Crée un effet de Rune prononcée
   * @param {Object} rune - Données de la rune
   * @param {number} pointsAme - Points de pouvoir dépensés
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createRunePrononceeEffect(rune, pointsAme) {
    if (!rune || !rune.name || pointsAme == null) return null;
    
    // Utiliser une icône par défaut si l'image de la rune est l'image par défaut
    const icon = rune.img?.includes('/blank.png') || !rune.img 
      ? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp" 
      : rune.img;
    
    return {
      name: `Rune: ${rune.name} (Prononcée)`,
      icon: icon,
      description: rune.system?.description || "",
      changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
      disabled: false,
      duration: { type: "rounds", value: Math.ceil(pointsAme / 3) },
      origin: rune.uuid || null,
      tint: "#00ff00",
      transfer: true,
      flags: {
        "mournblade-cyd2": {
          runeId: rune._id,
          runeType: "prononcee",
          pointsAme: pointsAme
        }
      }
    };
  }

  /**
   * Crée un effet de Rune tracée
   * @param {Object} rune - Données de la rune
   * @param {number} pointsAme - Points de pouvoir dépensés
   * @returns {Object|null} - Données de l'effet ou null
   */
  static createRuneTraceeEffect(rune, pointsAme) {
    if (!rune || !rune.name || pointsAme == null) return null;
    
    // Utiliser une icône par défaut si l'image de la rune est l'image par défaut
    const icon = rune.img?.includes('/blank.png') || !rune.img 
      ? "systems/fvtt-mournblade-cyd-2-0/assets/icons/rune.webp" 
      : rune.img;
    
    return {
      name: `Rune: ${rune.name} (Tracée)`,
      icon: icon,
      description: rune.system?.description || "",
      changes: [], // Les modifications spécifiques peuvent être ajoutées par les appels
      disabled: false,
      duration: { type: "rounds", value: Math.ceil(pointsAme / 3) * 2 },
      origin: rune.uuid || null,
      tint: "#0000ff",
      transfer: true,
      flags: {
        "mournblade-cyd2": {
          runeId: rune._id,
          runeType: "tracee",
          pointsAme: pointsAme
        }
      }
    };
  }

}

// Initialisation automatique
Hooks.once("init", () => {
  MournbladeCYD2Effects.init();
});
